├── Manifest.toml ├── Project.toml ├── README.md ├── TODO.md ├── doc └── syntax.md ├── src ├── SimpleTypeChecker.jl ├── adaptor │ ├── API.jl │ ├── DerivedExpr.jl │ ├── Inference.jl │ ├── InferenceDefinition.jl │ ├── InferenceError.jl │ ├── InferenceErrorUtility.jl │ ├── JSAdaptor.jl │ ├── JuExprValidator.jl │ ├── Scope.jl │ ├── ScopeChecking.jl │ ├── SyntaxAdaptor.jl │ ├── SyntaxDefinition.jl │ ├── TreeQuery.jl │ ├── UtilityUnType.jl │ ├── datastruct.jl │ ├── precompile.jl │ ├── toplevel.jl │ ├── type.jl │ └── utility.jl ├── server │ └── SimpleHTTPServer.jl └── www │ ├── class.js │ ├── data.js │ ├── dune │ ├── fonts │ ├── FiraCode-Bold.woff2 │ ├── FiraCode-Regular.woff2 │ ├── HelveticaNeue-Bold.woff2 │ ├── HelveticaNeue-Regular.woff2 │ ├── SourceCodePro-Black.otf.woff2 │ ├── SourceCodePro-Bold.otf.woff2 │ └── SourceCodePro-Regular.otf.woff2 │ ├── img │ ├── hazelnut.svg │ └── loading.gif │ ├── index_template.html │ ├── jquery.min.js │ ├── mystyle.css │ ├── popper.min.js │ ├── style.css │ ├── tooltip_style.css │ └── www │ ├── dune │ ├── fonts │ ├── FiraCode-Bold.woff2 │ ├── FiraCode-Regular.woff2 │ ├── HelveticaNeue-Bold.woff2 │ ├── HelveticaNeue-Regular.woff2 │ ├── SourceCodePro-Black.otf.woff2 │ ├── SourceCodePro-Bold.otf.woff2 │ └── SourceCodePro-Regular.otf.woff2 │ ├── img │ ├── hazelnut.svg │ └── loading.gif │ ├── index.html │ └── style.css └── test ├── abstract ├── abstract.jl └── abstracttest.jl ├── case ├── case.jl ├── casetest.jl └── scope.jl ├── eval.jl ├── refactor.jl ├── runtests.jl ├── self ├── macro.jl └── selftest.jl └── watch.jl /Manifest.toml: -------------------------------------------------------------------------------- 1 | # This file is machine-generated - editing it directly is not advised 2 | 3 | julia_version = "1.10.5" 4 | manifest_format = "2.0" 5 | project_hash = "8d783163a0faf6265254228e69332d612e1b82df" 6 | 7 | [[deps.JuliaSyntax]] 8 | git-tree-sha1 = "db2bdeda30e452485863799be4515f6305431a46" 9 | uuid = "70703baa-626e-46a2-a12c-08ffd08c73b4" 10 | version = "0.3.2" 11 | 12 | [[deps.Sockets]] 13 | uuid = "6462fe0b-24de-5631-8697-dd941f90decc" 14 | -------------------------------------------------------------------------------- /Project.toml: -------------------------------------------------------------------------------- 1 | name = "SimpleTypeChecker" 2 | uuid = "99a2e2af-0434-418a-b0f0-416ee7147db6" 3 | authors = ["chenningcong <393069484@qq.com>"] 4 | version = "0.1.0" 5 | 6 | [deps] 7 | JuliaSyntax = "70703baa-626e-46a2-a12c-08ffd08c73b4" 8 | Sockets = "6462fe0b-24de-5631-8697-dd941f90decc" 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SimpleTypeChecker is an experimental Julia package designed to enforce C-style programming in Julia language. Note : it won't save you if your codes already contain a lot of messy dynamic dispatch. It's more appropriate for developing new codes from scratch. 2 | See demo below for an example usage. 3 | 4 | # Installation 5 | ```julia 6 | pkg> add https://github.com/ChenNingCong/SimpleTypeChecker 7 | ``` 8 | 9 | # Usage 10 | ```julia 11 | ctx = SimpleTypeChecker.Inference.GlobalContext() 12 | SimpleTypeChecker.API.addFile!(ctx, mod, filepath) 13 | SimpleTypeChecker.API.runCheck!(ctx;checkSyntax=true) 14 | SimpleTypeChecker.API.@nocheck fun 15 | SimpleTypeChecker.API.check(ctx, f, tt) 16 | ``` 17 | SimpleTypeChecker provides several APIs to check all the functions in a file. To use this function, firstly import/include the module you want to check, then call `ctx = SimpleTypeChecker.Inference.GlobalContext()` to construct a context for type inference. Use `SimpleTypeChecker.API.addFile!(ctx, mod, filepath)` to add all the files you want to check into the context. Here `mod` is the module you just evaled and `filepath` is the filepath (*absolute path*) where the module is defined. Finally call `runCheck!` to check the context. By default we will check unsupported syntax. If you want to ignore unsupported sytnax error, use `checkSyntax = false`. 18 | 19 | If you have a function that is highly dynamic or uses some features that SimpleTypeChecker doesn't support, and you are certain the function is type stable, then you can use `SimpleTypeChecker.API.@nocheck fun` to skip the checking of that particular function. If you don't want to import SimpleTypeChecker, then using any macro (`@inline`, `@inbounds`) is sufficient, because currently we skip checking of any macro. 20 | 21 | Currently, only functions with concrete type annotation can be checked. If you want to check individual specializations like `sum(x::AbstractArray)` and `println(io::IO, x)`, use `SimpleTypeChecker.check(ctx, f, tt)`. `SimpleTypeChecker.check` accepts parameters like `code_warntype` and `code_typed`, `SimpleTypeChecker.check(ctx, sin, (Float64,))`. If you find this too complicated, then you can create a `main` function and put all the specializations in that `main` function. `SimpleTypeChecker` will recur into the subprocedure calls automatically. 22 | 23 | # Demo 24 | 1. SimpleTypeChecker is checked against itself, the following code checks almost all the codes of SimpleTypeChecker (some codes interact with Julia's compiler so they simply can't be statically-typed) 25 | ```julia 26 | import SimpleTypeChecker 27 | # firstly we get the package directory of SimpleTypeChecker 28 | const path = abspath(joinpath(splitdir(pathof(SimpleTypeChecker))[1], "..")) 29 | ctx = SimpleTypeChecker.Inference.GlobalContext() 30 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.SyntaxAdaptor, joinpath(path, "src/adaptor/TreeQuery.jl")) 31 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceDefinition.jl")) 32 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/Inference.jl")) 33 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceErrorUtility.jl")) 34 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceError.jl")) 35 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/ScopeChecking.jl")) 36 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Server, joinpath(path, "src/server/SimpleHTTPServer.jl")) 37 | SimpleTypeChecker.API.runCheck!(ctx) 38 | ``` 39 | 2. You can check the `test/case/case.jl` of SimpleTypeChecker's test cases, which includes some common program errors. 40 | ```julia 41 | import SimpleTypeChecker 42 | # firstly we get the package directory of SimpleTypeChecker 43 | const path = abspath(joinpath(splitdir(pathof(SimpleTypeChecker))[1], "..")) 44 | const testpath = abspath(joinpath(path, "test/case/case.jl")) 45 | include(testpath) 46 | ctx = SimpleTypeChecker.Inference.GlobalContext() 47 | SimpleTypeChecker.API.addFile!(ctx, CaseTest, joinpath(path, testpath)) 48 | SimpleTypeChecker.API.runCheck!(ctx) 49 | ``` 50 | 51 | For example, if you check `test/case/case.jl`, you will get a long list of error message: 52 | ``` 53 | ──────────────────────────────────────────────────────────────── 54 | Checking MethodInstance for Main.CaseTest.f26() 55 | 56 | /home/chenningcong/.julia/packages/SimpleTypeChecker/tu1RO/test/case/case.jl:225:11 57 | UndefinedError: variable y is potentially unitialized here 58 | y 59 | 60 | ──────────────────────────────────────────────────────────────── 61 | Checking MethodInstance for Main.CaseTest.f27() 62 | 63 | /home/chenningcong/.julia/packages/SimpleTypeChecker/tu1RO/test/case/case.jl:235:15 64 | UndefinedError: variable z is potentially unitialized here 65 | z 66 | 67 | ──────────────────────────────────────────────────────────────── 68 | Checking MethodInstance for Main.CaseTest.f30() 69 | 70 | /home/chenningcong/.julia/packages/SimpleTypeChecker/tu1RO/test/case/case.jl:267:13 71 | TypeError: 2-th paramter of type application (a.k.a parameterized type) is not a constant 72 | Vector{Int, x} 73 | 74 | ──────────────────────────────────────────────────────────────── 75 | ``` 76 | 77 | # Internal 78 | The idea is simple : SimpleTypeChecker performs type inference on Julia's AST (instead of lowered IR) and also rely on Julia's type inferencer 79 | 1. SimpleTypeChecker reads the method definitions from the file you provide, and use `JuliaSyntax` to parse the file and construct a loseless AST (a.k.a AST with precise location information) 80 | 2. For each method AST `f`, SimpleTypeCheck walk through the AST and check common errors. If it encounters a function calls to another function `g`, then it calls Julia's builtin type inferencer to get the return type of `g` 81 | 82 | Note : we need to rely on Julia's builtin type inferencer, because we don't have AST for every method definition (especially those in Base library). Also, inference for recursive function is generally undeciable, adding recursive check in SimpleTypeChecker will complicate a lot of thing. 83 | 84 | # Limitation 85 | Currently there are many limitations, like we don't support destruct assignment in for-loop (`for (x, v) in x`). 86 | Some of them are temporal and will be removed in the future, but at least the following limitations are permanent. They are critial parts of SimpleTypeChecker's design and can't be simply removed by clever engineer efforts. 87 | 88 | In summary, these limitations are the prices in exchange for a better error reporting. 89 | 90 | --- 91 | >Function calls with abstract parameters (including `Union{...}, Any, Integer, where`) are unconditionally disallowed, whether there is only one matching method, whether the function is a commonly used one (like `==(Any, Any)`), etc. 92 | 93 | Reason: 94 | Sometimes abstract calls are intended, like 95 | ```julia 96 | struct A 97 | val::Union{Int, Nothing} 98 | end 99 | # a.val isa Union, an abstract type 100 | copy(a::A) = A(a.val) 101 | ``` 102 | So there is simply no way to tell whether an abstract call is intended or not. Then the only solution is to ban abstract calls everywhere. 103 | 104 | Workaround: 105 | Wrap the abstract values in a struct. Only pass the wrapper and manipulate the wrapper with interface methods (In the future, SimpleTypeChecker may directly support `MLStyle.jl`'s pattern matching and algebraic types, so we don't need to wrap and dewrap them manually). 106 | 107 | --- 108 | >Limited type calculations and constant propagation, no heap analysis, no complicated type narrowing (only a single `isa`) 109 | 110 | Reason: 111 | 112 | Such analyses require nontrivial bidirectional flow analysis. One immediate consequence of such complicated analyses is that it's hard to precisely locate where the error is. Codebases in Julia are generally loosely typed. If there is an error somewhere, the this error may gradually propagate to other codes. That's why it's painful to debug type instability, because you need to recurse into a long function chains and context to seek the real source of bug. 113 | 114 | Workaround: 115 | 116 | Do calculations in short unchecked functions 117 | 118 | --- 119 | > Unable to distinguish errors caused by `::` and implicit convertion, like `push!([1,2,3], "1")`. 120 | 121 | Reason: 122 | 123 | `push!([1,2,3], "1")` is a totally valid function call. Honestly speaking, should this be considered as compile time error at all? 124 | 125 | Workaround: 126 | Almost impossible to workaround. Currently my thought: 127 | 1. Use a whitelist. Only the functions in the whitelist can return `Union{}`, like `error`. 128 | 2. Check functions recursively and only trust Julia's compiler when we don't have AST (this solve the problem if you have a return type assertion on a method, but the method is inferred as `Union{}`) 129 | 3. Manually provide *interface* definition for commonly used container in Base library. (Much like typeclass in Haskell, if type parameters don't satisfy the constraint, then immediately raise error) 130 | 131 | --- 132 | 133 | > Scope of variable in if-block and interpretation of `local x::Int` is vague. 134 | 135 | Reason: 136 | Julia has no scoped if block, which raises some interesting problems of variable in if-block. Should the following case be treated as type instability? 137 | ```julia 138 | function f(y::Bool) 139 | if y 140 | x = 1 141 | print(x) 142 | else 143 | x = 1.0 144 | print(x) 145 | end 146 | end 147 | ``` 148 | 149 | If we perform basic-block specializations and view two x as individual unrelated copies of value 1 and 1.0, then this code is type stable. SimpleTypeChecker can actually detect it and allow it (as long as if x is not used in following branch), but I choose to ban this case. So automatically merge of different types are prohibited. This is to prevent the following case: 150 | 151 | ```julia 152 | function f(y::Bool) 153 | if y 154 | x = 1 155 | println(x) 156 | # many more codes here... 157 | elseif somecond 158 | # ... 159 | else 160 | # accidentally name a string as x 161 | x = "1" 162 | end 163 | # more code here... 164 | # abstract call !!! 165 | 166 | # error reports here, too late 167 | print(x) 168 | end 169 | ``` 170 | 171 | What if I want to construct a Union type? then use a typed slot: 172 | ```julia 173 | function f(y::Bool) 174 | local x::Union{Int, Nothing} 175 | if y 176 | x = 1 177 | else 178 | x = nothing 179 | end 180 | end 181 | ``` 182 | 183 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | 1. Unreachable code elimimation 2 | 2. Error reporting and formatting 3 | 3. Poison type handling 4 | 4. Logging 5 | 6 | Julia's scope problem 7 | Consider this program 8 | ``` 9 | function f(x) 10 | if (x > 0) 11 | for i in 1:10 12 | # the scope of t is the function 13 | # t is not the local scope of the for loop 14 | t = 2 15 | end 16 | print(t) 17 | else 18 | t = 2 19 | end 20 | end 21 | ``` 22 | We require that: 23 | 1. if a variable a of scope X is assigned in a inner scope Y, then variable a is also declared or assigned in X before that assignment in Y 24 | (every assignment in inner scope is dominated by some assignment or declaration in outer scope of the same variable) 25 | 2. There can be at most one declaration for each variable, and every declaration is located before the assignment (if there is one) 26 | Note: 27 | 1. If a variable is declared, then we know that declaration dominates all other assignemnt expression 28 | 29 | We need addtional rule for if clause, because if clause is not scoped 30 | 1, If a variable is declared in an if clause, that variable can only in that 31 | 1. If a variable is declared in the condition of a if clause, -------------------------------------------------------------------------------- /doc/syntax.md: -------------------------------------------------------------------------------- 1 | Currently, SimpleTypeChecker only supports a subset of Julia's syntax. In the future, more syntax will be supported. 2 | 3 | Following are unsupported syntax tables and supported syntax tables. If you use some unsupported syntax, SimpleTypeChecker will raise `SyntaxError`. Using `runCheck!(ctx; checkSyntax = false)` can disable syntax checking and ignore all unsupported syntax. 4 | 5 | ## Unsupported Syntax: 6 | |Description|Example | Note| 7 | |--|--|--| 8 | | splatting | `push!(x, y...)`
`x,y,... = z` | may never support| 9 | | nested function | `x->x+1`
function nested in function | may support, but don't recommend using due to implementation difficulties. Please use toplevel function as much as possible| 10 | | inline quote | `:(x + y)` | may never support, use `quote ... end` to ensure type stability| 11 | | named tuple | `(x = 1, y = 3)` | will support | 12 | | multiple for assignment | `for x in arr1, y = arr2 println(1) end` |will support| 13 | | generator |`[i in for i in 1:10]` | may support, but don't recommend using due to implementation difficulties| 14 | | subtyping shorthand and where| `Array{<:Int, 1}`|will support| 15 | | assertion (type narrowing)| `x::Int`|will support| 16 | | macrocall|`@inline`| currently silient ignore, in the future user can provide custom annotation to easy analysis| 17 | | try-catch-finally|| will support| 18 | | vect |`[1,2,3]`|may support, but recommend using typed vect`Int[1,2,3]`| 19 | | outer | |may never support| 20 | | broadcast | x. = x.+ 1|will support| 21 | 22 | --- 23 | 24 | Note : If you use some unsupported syntax but don't want to change them, don't worry. You can isolate them in a function (unless it's closure that capture some variables). For example: 25 | ``` 26 | function large_function(x) 27 | ... 28 | # ops, we can't check generator 29 | arrs = [i+1 for i in x] 30 | ... 31 | end 32 | ``` 33 | We can modify it to 34 | ``` 35 | @inline function createArray(x) 36 | return [i+1 for i in x] 37 | end 38 | 39 | function large_function(x) 40 | ... 41 | arrs = createArray(x) 42 | ... 43 | end 44 | ``` 45 | In extreme case, you can always bypass checking of a specific function. 46 | 47 | ## Supported Syntax: 48 | |Description|Example | Note| 49 | |--|--|--| 50 | | assignment to a single variable | `x = 1+1`| can use update operator (`x+=1`)| 51 | | assignment by tuple destruct | `(x, (y[4], z.l)) = (3, (4, 5))`| can't use update operator (`(x, y) +=1`)| 52 | | assignment with storage type | `x::Int = 1`| can't use update operator (`x::Int +=1`)
a variable can only be declared once| 53 | | array assignment | `x[i,j,k] += 1` | can use update operator (`x[1]+=1`)| 54 | | setproperty | `x.k = 1` | can use update operator (`x.k += 1`)| 55 | | array ref| `x[1]`|| 56 | | getproperty | `x.k`|| 57 | | literal | `1`, `"x"` | | 58 | | variable | x || 59 | | block | `begin println(1); x = 1+1` || 60 | | tuple | `(y, z)` || 61 | | function call|`f(x, y;z =1`)|Keyword arguments require explicit `semi-colon`
Use `f(x;y=1)` instead of `f(x,y=1)`| 62 | | ifelse/while/return/reak/continue/` ? :`||| 63 | | let|`let x = 1, y, z::Int; println(x) end`|a variable can only be declared once
tuple destruct is disallowed(`let (x, y) = z end`)| 64 | | for|`for (x,(y,z)) in iter end`|| 65 | | local|`local x = 1, y, z::Int`|a variable can only be declared once
tuple destruct is disallowed(`local (x, y)`)| 66 | | global|`global x,y,z`|a variable can only be declared once
only variable is allowed)| -------------------------------------------------------------------------------- /src/SimpleTypeChecker.jl: -------------------------------------------------------------------------------- 1 | module SimpleTypeChecker 2 | 3 | include("./adaptor/toplevel.jl") 4 | 5 | end # module SimpleTypeChecker 6 | -------------------------------------------------------------------------------- /src/adaptor/API.jl: -------------------------------------------------------------------------------- 1 | import ..SimpleTypeChecker 2 | import ..SimpleTypeChecker.Utility.@nocheck 3 | import ..SimpleTypeChecker.Inference.GlobalContext 4 | import ..SimpleTypeChecker.Inference.addFile! 5 | import ..SimpleTypeChecker.Inference.runCheck! 6 | import ..SimpleTypeChecker.Inference.KwFunCall 7 | 8 | function check(ctx::SimpleTypeChecker.Inference.GlobalContext, f, tt) 9 | ci = Base.code_typed(f,tt)[1].first 10 | if ci isa Core.CodeInfo 11 | mi = ci.parent 12 | push!(ctx.queue, KwFunCall(mi)) 13 | runCheck!(ctx) 14 | end 15 | return 16 | end 17 | 18 | 19 | 20 | function loadPackage(s)::GlobalContext 21 | name = Symbol(String(s)) 22 | mod = Main 23 | for i in 1:2 24 | try 25 | Core.eval(Main,:(using $name)) 26 | mod = Core.eval(Main, name) 27 | catch e 28 | println(e) 29 | end 30 | end 31 | if mod == Main 32 | error("Failed to include module") 33 | end 34 | path = dirname(abspath(pathof(mod))) 35 | files = String[] 36 | for (root, dir, files_) in collect(walkdir(path)) 37 | for f in files_ 38 | apath = joinpath(path, root, f) 39 | println("Scanning file from package $mod", apath) 40 | push!(files, apath) 41 | end 42 | end 43 | ctx = GlobalContext() 44 | for i in files 45 | full = joinpath(path, i) 46 | println(mod, " ", full) 47 | addFile!(ctx, mod, full) 48 | end 49 | return ctx 50 | end 51 | 52 | function checkFactory(ctx::GlobalContext) 53 | function helper(x...) 54 | check(ctx, x...) 55 | end 56 | return helper 57 | end 58 | 59 | macro code_checked(ctx, ex0...) 60 | InteractiveUtils.gen_call_with_extracted_types_and_kwargs(__module__, :(checkFactory($ctx)), ex0) 61 | end 62 | 63 | export addFile!, runCheck!, GlobalContext, @nocheck, check, loadPackage -------------------------------------------------------------------------------- /src/adaptor/DerivedExpr.jl: -------------------------------------------------------------------------------- 1 | struct NoDerivedExpr 2 | parent::JuExpr 3 | end 4 | 5 | struct DerivedExprKwArg 6 | parent::JuExpr 7 | var::Symbol 8 | end 9 | 10 | struct DerivedExprKwArgLHS 11 | parent::DerivedExprKwArg 12 | end 13 | 14 | struct DerivedExprParameter 15 | parent::JuExpr 16 | end 17 | 18 | struct DerivedExprImplicitKwArg 19 | parent::JuExpr 20 | var::Symbol 21 | end 22 | 23 | struct DerivedExprModuleName 24 | parent::JuExpr 25 | end 26 | 27 | struct DerivedExprForVar 28 | parent::JuExpr 29 | end 30 | 31 | struct DerivedExprField 32 | parent::JuExpr 33 | end 34 | 35 | struct DerivedExprAssignLHS 36 | parent::JuExpr 37 | end 38 | 39 | struct DerivedExprFunName 40 | parent::JuExpr 41 | end 42 | 43 | struct DerivedExprFunParam 44 | parent::JuExpr 45 | var::Symbol 46 | end 47 | 48 | struct DerivedExprFunParamList 49 | parent::JuExpr 50 | end 51 | 52 | struct DerivedExprReturnType 53 | parent::JuExpr 54 | end 55 | 56 | struct DerivedExprFunDefArg 57 | parent::JuExpr 58 | sym::Symbol 59 | end 60 | 61 | struct DerivedExprFunDefArgPos 62 | parent::JuExpr 63 | i::Int 64 | end 65 | 66 | struct DerivedExprFunSignature 67 | parent::JuExpr 68 | end 69 | 70 | struct DerivedExprArrayRef 71 | parent::JuExpr 72 | end 73 | 74 | struct DerivedExprArraySet 75 | parent::JuExpr 76 | end 77 | 78 | struct DerivedExprForAssign 79 | parent::JuExpr 80 | end 81 | 82 | struct DerivedExprElseIf 83 | parent::JuExpr 84 | end -------------------------------------------------------------------------------- /src/adaptor/InferenceDefinition.jl: -------------------------------------------------------------------------------- 1 | using ..SyntaxDefinition 2 | using ..Utility 3 | import ..Utility 4 | import JuliaSyntax 5 | 6 | struct CompileType 7 | isConst::Bool 8 | val::Any 9 | typ::Any 10 | end 11 | 12 | function makeBottomType() 13 | CompileType(true, nothing, Union{}) 14 | end 15 | 16 | @nocheck function isBottomType(t::CompileType)::Bool 17 | return t.typ == Union{} 18 | end 19 | 20 | function makeConstVal(val::Any) 21 | CompileType(true, val, Core.Typeof(val)) 22 | end 23 | 24 | @nocheck function makeType(val::Any) 25 | if Base.issingletontype(val) 26 | CompileType(true, val.instance, val) 27 | elseif val isa DataType && val.name == Type.body.name 28 | # this is incorrect, but how can we infer the type here? 29 | println(val) 30 | CompileType(true, val.parameters[1], val) 31 | else 32 | CompileType(false, nothing, val) 33 | end 34 | end 35 | 36 | function isConstVal(typ::CompileType) 37 | return typ.isConst 38 | end 39 | 40 | @nocheck function removeConst(typ::CompileType) 41 | if typ.isConst 42 | makeType(typ.typ) 43 | else 44 | return typ 45 | end 46 | end 47 | 48 | @nocheck function lift(v::CompileType) 49 | if isConstVal(v) 50 | return makeType(v.val) 51 | else 52 | error("Internal error") 53 | end 54 | end 55 | 56 | @nocheck function convert2ConstVal(val::JuASTVal)::CompileType 57 | makeConstVal(val.val) 58 | end 59 | 60 | #= 61 | @enum FlowNodeKind begin 62 | ValueFlowNodeBegin 63 | 64 | LiteralFlowNode 65 | AssignFlowNode 66 | UntypedDeclareFlowNode 67 | TypedDeclareFlowNode 68 | IfStmtFlowNode 69 | EmptyElseFlowNode 70 | BlockFlowNode 71 | EmptyBlockFlowNode 72 | VarFlowNode 73 | GlobalVarFlowNode 74 | FunCallNode 75 | CurlyCallNode 76 | GetPropertyNode 77 | SetPropertyNode 78 | ArrayRefNode 79 | ArraySetNode 80 | ReturnNode 81 | EmptyReturnNode 82 | ExpectedReturnNode 83 | ParamNode 84 | SparamNode 85 | WhileStmtFlowNode 86 | ForStmtVarNode # like a local variable declaration 87 | ForStmtFlowNode 88 | DeclarationListFlowNode 89 | BreakStmtFlowNode 90 | ContinueStmtFlowNode 91 | UninitializedFlowNode 92 | TupleLiteralFlowNode 93 | DestructFlowNode 94 | 95 | ControlFlowNodeBegin 96 | PhiFlowNode 97 | ForVarNode 98 | ForUpdateNode # variable that loses constantness in the loop 99 | ConditionalFlowNode # variable that defines conditionally 100 | PiNode # pi projection of a variable 101 | NegPiNode # pi projection of a variable on a negative condition branch 102 | end 103 | =# 104 | 105 | mutable struct FlowNode 106 | # field (ast shared by all flow node) 107 | const ast::JuAST 108 | const source::Vector{FlowNode} 109 | const typ::CompileType 110 | const kind::Any # value depends on different flow kind 111 | end 112 | 113 | struct LiteralFlowNode end 114 | 115 | struct UpdateOp 116 | isUpdate::Bool 117 | isDotcall::Bool 118 | ast::JuAST # ast of the whole assignment, update operator has no mapping ast 119 | op::Symbol 120 | function UpdateOp(ast::JuAST, op::Symbol, isDotcall::Bool) 121 | new(true, isDotcall, ast, op) 122 | end 123 | function UpdateOp(ast::JuAST, isDotcall::Bool) 124 | new(false, isDotcall) 125 | end 126 | function UpdateOp() 127 | new(false, false) 128 | end 129 | end 130 | 131 | struct PiFlowNode end 132 | # represent all kind of assignment flow node 133 | # we can add additional fields here, to easy compiler analysis 134 | struct AssignLHSVarFlowNode 135 | isInitialized::Bool 136 | updateOp::UpdateOp 137 | end 138 | struct ConditionalFlowNode end 139 | struct ForUpdateFlowNode end 140 | struct DeclarationFlowNode end 141 | struct AssignLHSArrayRefFlowNode end 142 | 143 | struct LetFlowNode end 144 | struct WhileStmtFlowNode end 145 | #= 146 | There are many kinds of flow nodes that reprensent an assignment action (can appear in ContextValue) 147 | Lowered from FlowNode 148 | PiFlowNode type narrowing of a variable, must be initialized 149 | PhiNode merge of definitions in each branch (for or if-else), must be initialized 150 | AssignLHSVarFlowNode assignment with storage type (`local x::Int` or `x = 1` ), potentially uninitialized 151 | ConditionalFlowNode merge of definitions in each branch (for or if-else), inferred as uninitialized 152 | ForUpdateFlowNode change of constantness for varables in for-loop, must be initialized 153 | DeclarationFlowNode assignment without storage type (`local x`), must be uninitialized 154 | 155 | Note : x[1] = 1 and x.x = 1 is not treated as variable assignment 156 | 157 | AssignLHSTupleDestructFlowNode definition (assignment with initializer), must be initialized, can't not used in `local` 158 | 159 | Lattice 160 | Must Unitialized <: Potentially Unitialized <: Must Initialized 161 | DeclarationFlowNode AssignLHSVarFlowNode,ConditionalFlowNode, other FlowNode 162 | 163 | =# 164 | function mustInitialized(node::FlowNode)::Bool 165 | val = node.kind 166 | if val isa PiFlowNode 167 | return true 168 | elseif val isa AssignLHSVarFlowNode 169 | return val.isInitialized 170 | elseif val isa ConditionalFlowNode 171 | return false 172 | elseif val isa PhiFlowNode 173 | return true 174 | elseif val isa ForUpdateFlowNode 175 | return true 176 | elseif val isa DeclarationFlowNode 177 | return false 178 | # TODO : add other kinds of FlowNode here!!!! 179 | elseif val isa SparamFlowNode 180 | return true 181 | elseif val isa ParamFlowNode 182 | return true 183 | else 184 | throw(InternalError("")) 185 | end 186 | end 187 | 188 | function mustUnInitialized(node::FlowNode)::Bool 189 | val = node.kind 190 | if val isa DeclarationFlowNode 191 | return true 192 | elseif val isa AssignLHSVarFlowNode 193 | return !val.isInitialized 194 | else 195 | return false 196 | end 197 | end 198 | 199 | function isDeclarationFlowNode(node::FlowNode)::Bool 200 | val = node.kind 201 | if val isa DeclarationFlowNode 202 | return true 203 | else 204 | return false 205 | end 206 | end 207 | 208 | struct AssignExprFlowNode end 209 | struct VarFlowNode end 210 | struct GlobalVarFlowNode end 211 | 212 | struct MethodCallStruct 213 | fargs::Vector{FlowNode} 214 | kwargs::Vector{Pair{Symbol, FlowNode}} 215 | function MethodCallStruct(fargs::Vector{FlowNode}, kwargs::Vector{Pair{Symbol, FlowNode}}) 216 | new(fargs, kwargs) 217 | end 218 | end 219 | 220 | function MethodCallStruct(fargs::Vector{FlowNode})::MethodCallStruct 221 | MethodCallStruct(fargs, Pair{Symbol, FlowNode}[]) 222 | end 223 | 224 | function MethodCallStruct(f::FlowNode, args::Vector{FlowNode})::MethodCallStruct 225 | MethodCallStruct(vcat(f, args)) 226 | end 227 | 228 | struct FunCallFlowNode 229 | ms::MethodCallStruct 230 | end 231 | struct BlockFlowNode end 232 | struct TupleFlowNode end 233 | struct LiteralExprFlowNode end 234 | struct MacroCallFlowNode end 235 | struct CurlyCallFlowNode end 236 | struct ReturnFlowNode end 237 | struct BreakStmtFlowNode end 238 | struct ContinueStmtFlowNode end 239 | struct ArrayRefFlowNode end 240 | struct GetPropertyFlowNode end 241 | struct IfStmtFlowNode end 242 | struct PhiFlowNode end 243 | struct AssignLHSTupleDestructFlowNode end 244 | struct SparamFlowNode end 245 | struct ParamFlowNode end 246 | struct ExpectedReturnFlowNode end 247 | struct SetPropertyFlowNode end 248 | struct KeyParamFlowNode end 249 | # special node for Expr, Expr is a mutable that is local to the function body 250 | # we treat the value as a non-constant, because it's mutable 251 | function makeLiteralFlowNode(ast::JuAST, val::JuASTVal) 252 | FlowNode(ast, FlowNode[], convert2ConstVal(val), LiteralFlowNode()) 253 | end 254 | 255 | function makeLiteralExprFlowNode(ast::JuAST) 256 | FlowNode(ast, FlowNode[], makeType(Expr), LiteralExprFlowNode()) 257 | end 258 | function makeMacroCallFlowNode(ast::JuAST) 259 | FlowNode(ast, FlowNode[], makeType(Nothing), MacroCallFlowNode()) 260 | end 261 | 262 | function makeAssignLHSVarFlowNode(ast::JuAST, rhs::FlowNode, updateOp::UpdateOp) 263 | FlowNode(ast, FlowNode[rhs], rhs.typ, AssignLHSVarFlowNode(true, updateOp)) 264 | end 265 | 266 | function makeAssignLHSLocalVarFlowNode(ast::JuAST) 267 | FlowNode(ast, FlowNode[], makeBottomType(), DeclarationFlowNode()) 268 | end 269 | 270 | function makeAssignLHSVarAssertFlowNode(ast::JuAST, typ::FlowNode, styp::CompileType, isInitialized::Bool) 271 | FlowNode(ast, FlowNode[typ], styp, AssignLHSVarFlowNode(isInitialized, UpdateOp())) 272 | end 273 | 274 | function makeLetFlowNode(ast::JuAST, typ::FlowNode) 275 | FlowNode(ast, FlowNode[typ], typ.typ, LetFlowNode()) 276 | end 277 | 278 | function makeForUpdateFlowNode(ast::JuAST, node::FlowNode) 279 | if mustInitialized(node) && isConstVal(node.typ) 280 | return FlowNode(ast, FlowNode[node], removeConst(node.typ), ForUpdateFlowNode()) 281 | else 282 | return node 283 | end 284 | end 285 | 286 | function makeIterateFlowNode(ast::JuAST, tt::CompileType) 287 | FlowNode(ast, FlowNode[], tt, nothing) 288 | end 289 | 290 | #= 291 | function makeForVarFlowNode(ast::JuAST, rhs::FlowNode, tt::CompileType) 292 | FlowNode(ast, FlowNode[rhs], tt, AssignLHSVarFlowNode(true, UpdateOp(), rhs)) 293 | end 294 | =# 295 | function makeAssignExprFlowNode(ast::JuAST, rhs::FlowNode) 296 | FlowNode(ast, FlowNode[rhs], rhs.typ, AssignExprFlowNode()) 297 | end 298 | 299 | function makeVarFlowNode(ast::JuAST, ref::FlowNode) 300 | FlowNode(ast, FlowNode[ref], ref.typ, VarFlowNode()) 301 | end 302 | 303 | function makeGlobalVarFlowNode(ast::JuAST, typ::CompileType) 304 | FlowNode(ast, FlowNode[], typ, GlobalVarFlowNode()) 305 | end 306 | 307 | function makeFunCallFlowNode(ast::JuAST, ms::MethodCallStruct, typ::CompileType) 308 | FlowNode(ast, FlowNode[], typ, FunCallFlowNode(ms)) 309 | end 310 | 311 | function makeEmptyBlockFlowNode(ast::JuAST) 312 | FlowNode(ast, FlowNode[], makeConstVal(nothing), BlockFlowNode()) 313 | end 314 | 315 | function makeBlockFlowNode(ast::JuAST, last::FlowNode) 316 | FlowNode(ast, FlowNode[last], last.typ, BlockFlowNode()) 317 | end 318 | 319 | function makeTupleFlowNode(ast::JuAST, args::Vector{FlowNode}, tt::CompileType) 320 | FlowNode(ast, args, tt, TupleFlowNode()) 321 | end 322 | 323 | function makeCurlyCallFlowNode(ast::JuAST, args::Vector{FlowNode}, typ::CompileType) 324 | FlowNode(ast, args, typ, CurlyCallFlowNode()) 325 | end 326 | 327 | function makeEmptyReturnFlowNode(ast::JuAST) 328 | FlowNode(ast, FlowNode[], makeBottomType(), ReturnFlowNode()) 329 | end 330 | 331 | function makeReturnFlowNode(ast::JuAST, e::FlowNode) 332 | FlowNode(ast, FlowNode[e], makeBottomType(), ReturnFlowNode()) 333 | end 334 | 335 | function makeBreakStmtNode(ast::JuAST) 336 | FlowNode(ast, FlowNode[], makeBottomType(), BreakStmtFlowNode()) 337 | end 338 | 339 | function makeContinueStmtNode(ast::JuAST) 340 | FlowNode(ast, FlowNode[], makeBottomType(), ContinueStmtFlowNode()) 341 | end 342 | 343 | function makeArrayRefFlowNode(ast::JuAST, ms::MethodCallStruct, typ::CompileType) 344 | FlowNode(ast, ms.fargs, typ, ArrayRefFlowNode()) 345 | end 346 | 347 | function makeGetPropertyFlowNode(ast::JuAST, node::FlowNode, typ::CompileType) 348 | FlowNode(ast, FlowNode[node], typ, GetPropertyFlowNode()) 349 | end 350 | 351 | function makeEmptyElseFlowNode(ast::JuAST) 352 | FlowNode(ast, FlowNode[], makeConstVal(nothing), IfStmtFlowNode()) 353 | end 354 | 355 | function makeIfStmtFlowNode(ast::JuAST, typ::CompileType) 356 | FlowNode(ast, FlowNode[], typ, IfStmtFlowNode()) 357 | end 358 | 359 | function makePiFlowNode(ast::JuAST, condnode::FlowNode, typ::CompileType) 360 | FlowNode(ast, FlowNode[condnode], typ, PiFlowNode()) 361 | end 362 | 363 | function makeNegPiFlowNode(ast::JuAST, condnode::FlowNode, typ::CompileType) 364 | FlowNode(ast, FlowNode[condnode], typ, PiFlowNode()) 365 | end 366 | 367 | function makeConditionalFlowNode(ast::JuAST) 368 | FlowNode(ast, FlowNode[], makeBottomType(), ConditionalFlowNode()) 369 | end 370 | 371 | function makePhiFlowNode(ast::JuAST, nodes::Vector{FlowNode}, v::CompileType) 372 | FlowNode(ast, nodes, v, PhiFlowNode()) 373 | end 374 | 375 | function makeAssignLHSTupleDestructFlowNode(ast::JuAST, node::FlowNode, v::CompileType) 376 | FlowNode(ast, FlowNode[node], v, AssignLHSTupleDestructFlowNode()) 377 | end 378 | 379 | function makeAssignLHSArrayRefFlowNode(ast::JuAST, ms::MethodCallStruct, v::CompileType) 380 | FlowNode(ast, ms.fargs, v, AssignLHSArrayRefFlowNode()) 381 | end 382 | 383 | function makeSparamFlowNode(ast::JuAST, v::CompileType) 384 | FlowNode(ast, FlowNode[], v, SparamFlowNode()) 385 | end 386 | 387 | function makeParamFlowNode(ast::JuAST, v::CompileType) 388 | FlowNode(ast, FlowNode[], v, ParamFlowNode()) 389 | end 390 | 391 | function makeKeyParamFlowNode(ast::JuAST, v::CompileType) 392 | FlowNode(ast, FlowNode[], v, KeyParamFlowNode()) 393 | end 394 | 395 | function makeExpectedReturnFlowNode(ast::JuAST, v::CompileType) 396 | FlowNode(ast, FlowNode[], v, ExpectedReturnFlowNode()) 397 | end 398 | 399 | 400 | function makeSetPropertyFlowNode(ast::JuAST, xnode::FlowNode, rhsnode::FlowNode, typ::CompileType) 401 | FlowNode(ast, FlowNode[xnode, rhsnode], typ, SetPropertyFlowNode()) 402 | end 403 | 404 | function makeWhileStmtFlowNode(ast::JuAST) 405 | FlowNode(ast, FlowNode[], makeType(Nothing), WhileStmtFlowNode()) 406 | end 407 | 408 | #= 409 | #=========begin here =======# 410 | function makeDeclarationListNode(ex::JuExpr) 411 | FlowNode(ex, DeclarationListFlowNode, FlowNode[], makeConstVal(nothing)) 412 | end 413 | 414 | function makePhiFlowNode(ex::JuExpr, v::Vector{FlowNode}, typ::CompileType) 415 | FlowNode(ex, PhiFlowNode, v, typ) 416 | end 417 | 418 | =# 419 | 420 | struct ErrorLogger <: IO 421 | io::IOBuffer 422 | end 423 | 424 | @nocheck function Base.write(log::ErrorLogger, x::UInt8) 425 | write(log.io, x) 426 | end 427 | 428 | struct Argument 429 | name::Symbol 430 | typ::Maybe{JuAST} 431 | initializer::Maybe{JuAST} 432 | end 433 | 434 | struct FunDef 435 | ast::JuAST 436 | # type params and bound 437 | sparams::Vector{Pair{Symbol, Maybe{JuAST}}} 438 | rt::Maybe{JuAST} 439 | 440 | # we disallow destruct syntax in all kinds of parameters 441 | # we disallow short hand function syntax 442 | # we only support qualified name 443 | 444 | # (::f)(...) functor is not supported yet 445 | fname::Vector{Symbol} 446 | args::Vector{Argument} 447 | optargs::Vector{Argument} 448 | kwargs::Vector{Argument} 449 | body::JuAST 450 | end 451 | 452 | function _helper_sort_lt(x1::Pair{Symbol, CompileType}, x2::Pair{Symbol, CompileType})::Bool 453 | return x1.first < x2.first 454 | end 455 | 456 | const KeywordSortList = Vector{Pair{Symbol, CompileType}} 457 | 458 | struct KwFunCall 459 | mi::Core.MethodInstance 460 | kwargs::KeywordSortList 461 | function KwFunCall(mi::Core.MethodInstance, li::KeywordSortList) 462 | return new(mi, sort(li; lt = _helper_sort_lt)) 463 | end 464 | end 465 | 466 | function KwFunCall(mi::Core.MethodInstance) 467 | KwFunCall(mi, KeywordSortList()) 468 | end 469 | 470 | struct MacroExpander 471 | expander::Any 472 | end 473 | 474 | mutable struct GlobalContext 475 | errio::ErrorLogger 476 | # each source file has an individual JuAST 477 | fileMapping::Dict{JuliaSyntax.SourceFile, JuAST} 478 | # each method has an individual JuAST 479 | methodDefs::Dict{Core.Method, FunDef} 480 | 481 | queue::Vector{KwFunCall} 482 | hasChecked::Dict{Core.MethodInstance, Vector{Pair{KeywordSortList, Any}}} 483 | cache::Dict{Any, Vector{CompileType}} 484 | const expanders::Dict{CompileType, MacroExpander} 485 | end 486 | 487 | function GlobalContext() 488 | GlobalContext(ErrorLogger(IOBuffer()), 489 | Dict{JuliaSyntax.SourceFile, JuAST}(), 490 | Dict{Core.Method, FunDef}(), 491 | KwFunCall[], 492 | Dict{Core.MethodInstance, Vector{Pair{Dict{Symbol, CompileType}, Any}}}(), 493 | Dict{Any, Vector{Any}}(), 494 | Dict{CompileType, MacroExpander}()) 495 | end 496 | 497 | 498 | #= 499 | Our check is still incorrectly implemented 500 | 501 | We need to walk AST for three times 502 | First time : collect explicitly declarated variable 503 | Second time : collect implicitly declarated variable (variable defined with assign) 504 | Third time : validate variable declaration 505 | =# 506 | struct ScopeInfo 507 | parent::JuAST 508 | # variable that is declared explicitly with local or for/let/function params, collected in first round 509 | locals::Dict{Symbol, JuAST} 510 | # variable that we have meet in second turn 511 | curlocals::Set{Symbol} 512 | # variable that is declared explicitly with global, collected in first round 513 | globals::Dict{Symbol, JuAST} 514 | # variable that we have meet in second turn 515 | curglobals::Set{Symbol} 516 | # variable that is local to this scope, but defined through an assignment 517 | implicitlocals::Dict{Symbol, JuAST} 518 | curimplicitlocals::Set{Symbol} 519 | 520 | modified::Dict{Symbol, JuAST} # variable that is not in this scope and modified in this scope 521 | # relationship 522 | # locals + implicitlocals = alllocals 523 | # curlocals <: alllocals 524 | # intersect(locals, implicitlocals) = empty 525 | # intersect(modifies, alllocals) = empty 526 | end 527 | 528 | function prettySymbols(s) 529 | str = join(s, ", ") 530 | if length(str) == 0 531 | return "(Empty)" 532 | end 533 | return str 534 | end 535 | 536 | @nocheck function Base.show(io::IO, info::ScopeInfo) 537 | println(io, "At $(formatLocation(info.parent.loc))") 538 | println(io, "Locals : ", prettySymbols(keys(info.locals))) 539 | println(io, "Globals : ", prettySymbols(keys(info.globals))) 540 | println(io, "Implicit locals : ", prettySymbols(keys(info.implicitlocals))) 541 | println(io, "Modified : ", prettySymbols(keys(info.modified))) 542 | return 543 | end 544 | 545 | function ScopeInfo(parent::JuAST) 546 | ScopeInfo(parent, 547 | Dict{Symbol, JuAST}(), 548 | Set{Symbol}(), 549 | Dict{Symbol, JuAST}(), 550 | Set{Symbol}(), 551 | Dict{Symbol, JuAST}(), 552 | Set{Symbol}(), 553 | Dict{Symbol, JuAST}()) 554 | end 555 | 556 | const ScopeInfos = Dict{JuAST, ScopeInfo} 557 | 558 | mutable struct ScopeInfoContext 559 | const ctx::GlobalContext 560 | const infos::ScopeInfos 561 | chains::Vector{ScopeInfo} 562 | walkTurn::Int 563 | end 564 | 565 | function addInfo!(ctx::ScopeInfoContext, ast::JuAST, info::ScopeInfo)::Nothing 566 | if haskey(ctx.infos, ast) 567 | error("Repetive attach scope") 568 | end 569 | ctx.infos[ast] = info 570 | return 571 | end 572 | 573 | function ScopeInfoContext(ctx::GlobalContext) 574 | ScopeInfoContext(ctx, 575 | ScopeInfos(), 576 | ScopeInfo[], 577 | 1) 578 | end 579 | 580 | 581 | struct ContextValue 582 | typ::FlowNode # primary type of this context variable 583 | curtyp::FlowNode # inferred type on this path 584 | end 585 | 586 | struct Context 587 | mapping::ImmutableDict{Symbol, ContextValue} 588 | end 589 | 590 | function Context() 591 | return Context(ImmutableDict{Symbol, ContextValue}()) 592 | end 593 | 594 | function hasVar(ctx::Context, var::Symbol)::Bool 595 | return Utility.exist(ctx.mapping, var) 596 | end 597 | 598 | function lookup(ctx::Context, var::Symbol)::ContextValue 599 | return ctx.mapping.data[var] 600 | end 601 | 602 | function update(ctx::Context, var::Symbol, val::ContextValue)::Context 603 | return Context(Utility.update(ctx.mapping, var, val)) 604 | end 605 | 606 | function getAllVars(ctx::Context) 607 | return keys(ctx.mapping.data) 608 | end 609 | 610 | mutable struct Engine 611 | const globalCtx::GlobalContext 612 | const mi::Core.MethodInstance 613 | const mod::Core.Module 614 | const errio::ErrorLogger 615 | # a JuAST can have multiple mapping 616 | const flowMapping::Dict{JuAST, Vector{FlowNode}} 617 | const scopeInfos::ScopeInfos 618 | # perform necessary end and begin 619 | const arrayContext::Vector{FlowNode} 620 | const loopContext::Vector{Vector{Context}} 621 | retVal::Maybe{FlowNode} 622 | end 623 | 624 | @nocheck function inferExpand(eng::Engine, ctx::Context, e::MacroExpander, ast::JuAST)::InferResult 625 | result = e.expander(eng, ctx, ast) 626 | if result isa InferResult 627 | return result 628 | else 629 | error("Macto expand must return a InferResult") 630 | end 631 | end 632 | 633 | const GlobalExpanders = Dict{CompileType, MacroExpander}() 634 | function addGlobalExpander!(mod::Module, symbol::Symbol, f) 635 | GlobalExpanders[makeConstVal(getproperty(mod, symbol))] = MacroExpander(f) 636 | return 637 | end 638 | 639 | function getGlobalExpander(ctx::GlobalContext) 640 | for (k, v) in GlobalExpanders 641 | ctx.expanders[k] = v 642 | end 643 | end 644 | 645 | function enterLoop(eng::Engine) 646 | push!(eng.loopContext, FlowNode[]) 647 | end 648 | 649 | function leaveLoop(eng::Engine) 650 | pop!(eng.loopContext) 651 | end 652 | 653 | function getGlobalCtx(eng::Engine) 654 | return eng.globalCtx 655 | end 656 | 657 | function getMod(mi::Core.MethodInstance)::Core.Module 658 | v = mi.def 659 | if v isa Module 660 | return v 661 | else 662 | return v.module 663 | end 664 | end 665 | 666 | function Engine(ctx::GlobalContext, mi::Core.MethodInstance, scopeInfos::ScopeInfos) 667 | # stdout is abstract 668 | return Engine(ctx, mi, getMod(mi), ctx.errio, Dict{JuAST, Vector{FlowNode}}(), scopeInfos, FlowNode[], Vector{Vector{Context}}(), None(FlowNode)) 669 | end 670 | 671 | function addFlowMapping!(eng::Engine, ast::JuAST, node::FlowNode)::Nothing 672 | map = eng.flowMapping 673 | if !haskey(map, ast) 674 | map[ast] = FlowNode[] 675 | end 676 | push!(map[ast], node) 677 | return 678 | end 679 | 680 | struct InferResult 681 | ctx::Context 682 | node::FlowNode 683 | end 684 | 685 | struct InferReport 686 | ast::JuAST 687 | eng::Engine 688 | rel::InferResult 689 | end 690 | 691 | function displayResult(io::IO, rel::InferResult) 692 | displayContext(io, rel.ctx) 693 | end 694 | 695 | @nocheck function displayContext(io::IO, ctx::Context) 696 | all = sort(collect(ctx.mapping.data), by = x->x[1]) 697 | cons_v = [i for i in all if isConstVal(i[2].curtyp.typ)] 698 | # TODO : how should we deal with variout assignment node? 699 | other_v = [i for i in all if !isConstVal(i[2].curtyp.typ)] 700 | #other_v_cond = [i for i in all if !isConstVal(i[2].curtyp.typ) && i[2].curtyp.nodeKind == ConditionalFlowNode] 701 | if !isempty(cons_v) 702 | println(io, "Constant :") 703 | for (k,v) in cons_v 704 | println(io, " $k : $(repr(v.curtyp.typ.val))") 705 | end 706 | end 707 | if !isempty(other_v) 708 | println(io, "Variable : (Slot type and Current type)") 709 | for (k,v) in other_v 710 | a = v.typ.typ.typ 711 | b = v.curtyp.typ.typ 712 | if (a == b) 713 | println(io, " $k::$a") 714 | else 715 | println(io, " $k::$a | $b") 716 | end 717 | end 718 | end 719 | #= 720 | if !isempty(other_v_cond) 721 | println(io, "Conditionally Defined Variable : (Slot type and Current type)") 722 | for (k,v) in other_v_cond 723 | a = v.typ.typ.typ 724 | b = v.curtyp.typ.typ 725 | if (a == b) 726 | println(io, " $k::$a") 727 | else 728 | println(io, " $k::$a | $b") 729 | end 730 | end 731 | end 732 | =# 733 | if isempty(all) 734 | println(io, "(Empty Context)") 735 | end 736 | displayReturn 737 | end 738 | 739 | function displayReturn(io::ErrorLogger, eng::Engine) 740 | val = eng.retVal 741 | if !isNone(val) 742 | println(io, "Return Type : $(toString(castJust(val).typ))") 743 | end 744 | end 745 | 746 | @nocheck function displayReport(io::ErrorLogger, r::InferReport) 747 | println(io, '\u2500'^64) 748 | ex = r.f 749 | val = ex.val 750 | if val isa FunDef 751 | println(io, "Inference Result for Method $(r.eng.mi)") 752 | displayReturn(io, r.eng) 753 | displayResult(io, r.rel) 754 | println(io, '\u2500'^64) 755 | else 756 | error("Internal Error") 757 | end 758 | return 759 | end -------------------------------------------------------------------------------- /src/adaptor/InferenceError.jl: -------------------------------------------------------------------------------- 1 | import ..SyntaxDefinition.formatLocation 2 | 3 | #= 4 | To make test easier, we separate error reporting and error generation 5 | A type inference error is firstly packed into a InferenceError structure, then dispatched by an io function 6 | to generate human-readable messages. 7 | abstract type InferenceError -> a subtype represents all kinds of inference error 8 | struct InferenceErrorXXX -> a type represent a specific kind of inference error 9 | reportErrorXXX(...)::InferenceErrorXXX -> generate a inference error 10 | displayErrorXXX(io::IO, err::InferenceErrorXXX) -> print the error to the terminal or IOBuffer 11 | =# 12 | 13 | @nocheck function printWarningHead(eng::Engine, ast::JuAST, errkind::String) 14 | loc = ast.loc 15 | println(stdout, formatLocation(loc)) 16 | print(stdout, " ") 17 | println(stdout, errkind) 18 | try 19 | code = loc.file.code[loc.span[1]:loc.span[2]] 20 | needTab = !occursin('\n', code) 21 | if needTab 22 | println(stdout, ' '^4, code) 23 | else 24 | println(stdout, '\n', code) 25 | end 26 | catch e 27 | println(stdout, ' '^4, "(Failed to extract code)") 28 | end 29 | end 30 | 31 | function reportWarning(eng::Engine, ast::JuAST, msg::String)::Nothing 32 | printWarningHead(eng, ast, msg) 33 | end 34 | 35 | struct InferenceErrorAssignBottom <: InferenceError 36 | eng::Engine 37 | ast::JuAST 38 | end 39 | 40 | function reportErrorAssignBottom(eng::Engine, ast::JuAST)::Union{} 41 | throwInferenceError(InferenceErrorAssignBottom(eng, ast)) 42 | end 43 | 44 | function displayErrorAssignBottom(err::InferenceErrorAssignBottom)::Nothing 45 | eng = err.eng 46 | ast = err.ast 47 | printErrorHead(eng, ast, "AssignError : rhs is a bottom value") 48 | end 49 | 50 | struct InferenceErrorAssignIncompatible <: InferenceError 51 | eng::Engine 52 | ast::JuAST 53 | old::FlowNode 54 | new::FlowNode 55 | end 56 | 57 | function reportErrorAssignIncompatible(eng::Engine, old::FlowNode, new::FlowNode)::Union{} 58 | throwInferenceError(InferenceErrorAssignIncompatible(eng, new.ast, old, new)) 59 | end 60 | 61 | function displayErrorAssignIncompatible(err::InferenceErrorAssignIncompatible)::Nothing 62 | eng = err.eng 63 | new = err.new 64 | old = err.old 65 | new = err.new 66 | printErrorHead(eng, new.ast, "AssignError: reassigned type $(toString(new.typ)) is incompatible") 67 | println(eng.errio, "Previous type is $(toString(old.typ)), ", formatLocation(old.ast.loc)) 68 | end 69 | 70 | struct InferenceErrorUnitializedVar <: InferenceError 71 | eng::Engine 72 | ast::JuAST 73 | id::Symbol 74 | end 75 | 76 | function reportErrorUnitializedVar(eng::Engine, ast::JuAST, id::Symbol)::Union{} 77 | throwInferenceError(InferenceErrorUnitializedVar(eng, ast, id)) 78 | end 79 | 80 | function displayErrorUnitializedVar(err::InferenceErrorUnitializedVar)::Nothing 81 | eng = err.eng 82 | ast = err.ast 83 | id = err.id 84 | printErrorHead(eng, ast, "UndefinedError: variable $id is potentially unitialized here") 85 | end 86 | 87 | struct InferenceErrorUndefinedVar <: InferenceError 88 | eng::Engine 89 | ast::JuAST 90 | m::Module 91 | p::Symbol 92 | end 93 | 94 | function reportErrorUndefinedVar(eng::Engine, ast::JuAST, m::Module, p::Symbol)::Union{} 95 | throwInferenceError(InferenceErrorUndefinedVar(eng, ast, m, p)) 96 | end 97 | 98 | function displayErrorUndefinedVar(err::InferenceErrorUndefinedVar)::Nothing 99 | eng = err.eng 100 | ast = err.ast 101 | m = err.m 102 | p = err.p 103 | printErrorHead(eng, ast, "UndefVarError: In module $m, variable $p is not defined") 104 | end 105 | 106 | struct InferenceErrorUpdateAssignReturnBottom <: InferenceError 107 | eng::Engine 108 | ast::JuAST 109 | end 110 | 111 | function reportErrorUpdateAssignReturnBottom(eng::Engine, ast::JuAST)::Union{} 112 | throwInferenceError(InferenceErrorUpdateAssignReturnBottom(eng, ast)) 113 | end 114 | 115 | function displayErrorUpdateAssignReturnBottom(err::InferenceErrorUpdateAssignReturnBottom)::Nothing 116 | eng = err.eng 117 | ast = err.ast 118 | printErrorHead(eng, ast, "AssignError: update assign evaluates to a bottom value") 119 | end 120 | 121 | struct InferenceErrorFunCall <: InferenceError 122 | eng::Engine 123 | ast::JuAST 124 | ms::MethodCallStruct 125 | num::Int 126 | isDotcall::Bool 127 | end 128 | 129 | @nocheck function reportErrorFunCall(eng::Engine, ast::JuAST, ms::MethodCallStruct, num::Int, isDotcall::Bool = false)::Union{} 130 | throwInferenceError(InferenceErrorFunCall(eng, ast, ms, num, isDotcall)) 131 | end 132 | 133 | function displayErrorFunCall(err::InferenceErrorFunCall)::Nothing 134 | eng = err.eng 135 | ast = err.ast 136 | ms = err.ms 137 | num = err.num 138 | isDotcall = err.isDotcall 139 | str = getSignature(ms) 140 | if isDotcall 141 | m = "broadcast call" 142 | else 143 | m = "method" 144 | end 145 | if num >= 2 146 | printErrorHead(eng, ast, "MethodError: more than one matching $m for $(str)") 147 | elseif num == 0 148 | printErrorHead(eng, ast, "MethodError: no matching $m for $(str)") 149 | end 150 | end 151 | 152 | struct InferenceErrorKeywordUseBottom <: InferenceError 153 | eng::Engine 154 | ast::JuAST 155 | sym::Symbol 156 | end 157 | 158 | function reportErrorKeywordUseBottom(eng::Engine, ast::JuAST, sym::Symbol)::Union{} 159 | throwInferenceError(InferenceErrorKeywordUseBottom(eng, ast, sym)) 160 | end 161 | 162 | function displayErrorKeywordUseBottom(err::InferenceErrorKeywordUseBottom)::Nothing 163 | eng = err.eng 164 | ast = err.ast 165 | sym = err.sym 166 | printErrorHead(eng, ast, "BottomError : keyword argument $sym is assigned with bottom") 167 | end 168 | 169 | struct InferenceErrorPrematureUnreachable <: InferenceError 170 | eng::Engine 171 | ast::JuAST 172 | end 173 | 174 | function reportErrorPrematureUnreachable(eng::Engine, ast::JuAST)::Union{} 175 | throwInferenceError(InferenceErrorPrematureUnreachable(eng, ast)) 176 | end 177 | 178 | function displayErrorPrematureUnreachable(err::InferenceErrorPrematureUnreachable)::Nothing 179 | eng = err.eng 180 | ast = err.ast 181 | printErrorHead(eng, ast, "UnreachableError: unreachable code exists after this expression") 182 | end 183 | 184 | struct InferenceErrorTupleBottom <: InferenceError 185 | eng::Engine 186 | ast::JuAST 187 | id::Int 188 | end 189 | 190 | function reportErrorTupleBottom(eng::Engine, ast::JuAST, id::Int)::Union{} 191 | throwInferenceError(InferenceErrorTupleBottom(eng, ast, id)) 192 | end 193 | 194 | function displayErrorTupleBottom(err::InferenceErrorTupleBottom)::Nothing 195 | eng = err.eng 196 | ast = err.ast 197 | id = err.id 198 | printErrorHead(eng, ast, "BottomError: $id-th parameter of the tuple is a bottom value") 199 | end 200 | 201 | struct InferenceErrorErrorCurlyCallUseBottom <: InferenceError 202 | eng::Engine 203 | ast::JuAST 204 | i::Int 205 | end 206 | 207 | function reportErrorCurlyCallUseBottom(eng::Engine, ast::JuAST, i::Int)::Union{} 208 | throwInferenceError(InferenceErrorErrorCurlyCallUseBottom(eng, ast, i)) 209 | end 210 | 211 | function displayErrorCurlyCallUseBottom(err::InferenceErrorErrorCurlyCallUseBottom)::Nothing 212 | eng = err.eng 213 | ast = err.ast 214 | i = err.i 215 | if i == 1 216 | printErrorHead(eng, ast, "BottomError: try to call `type`{...} where `type` is a bottom value") 217 | else 218 | printErrorHead(eng, ast, "BottomError: $(i-1)-th parameter of `type`{...} call is an bottom value") 219 | end 220 | end 221 | 222 | struct InferenceErrorApplyTypeNonconst <: InferenceError 223 | eng::Engine 224 | ast::JuAST 225 | i::Vector{Int} 226 | end 227 | 228 | function reportErrorApplyTypeNonconst(eng::Engine, ast::JuAST, i::Vector{Int})::Union{} 229 | throwInferenceError(InferenceErrorApplyTypeNonconst(eng, ast, i)) 230 | end 231 | 232 | function displayErrorApplyTypeNonconst(err::InferenceErrorApplyTypeNonconst)::Nothing 233 | eng = err.eng 234 | ast = err.ast 235 | i = err.i 236 | x = toIndex(i) 237 | printErrorHead(eng, ast, "TypeError: $x-th paramter of type application (a.k.a parameterized type) is not a constant") 238 | end 239 | 240 | struct InferenceErrorGetFieldOfBottom <: InferenceError 241 | eng::Engine 242 | ast::JuAST 243 | end 244 | 245 | function reportErrorGetFieldOfBottom(eng::Engine, ast::JuAST)::Union{} 246 | throwInferenceError(InferenceErrorGetFieldOfBottom(eng, ast)) 247 | end 248 | 249 | function displayErrorGetFieldOfBottom(err::InferenceErrorGetFieldOfBottom)::Nothing 250 | eng = err.eng 251 | ast = err.ast 252 | printErrorHead(eng, ast, "BottomError: calling getproperty on a bottom value") 253 | end 254 | 255 | struct InferenceErrorSetFieldOfBottom <: InferenceError 256 | eng::Engine 257 | ast::JuAST 258 | end 259 | 260 | function reportErrorSetFieldOfBottom(eng::Engine, ast::JuAST)::Union{} 261 | throwInferenceError(InferenceErrorSetFieldOfBottom(eng, ast)) 262 | end 263 | 264 | function displayErrorSetFieldOfBottom(err::InferenceErrorSetFieldOfBottom)::Nothing 265 | eng = err.eng 266 | ast = err.ast 267 | printErrorHead(eng, ast, "BottomError: calling setproperty! on a bottom value") 268 | end 269 | 270 | struct InferenceErrorSetFieldWithBottom <: InferenceError 271 | eng::Engine 272 | ast::JuAST 273 | end 274 | 275 | function reportErrorSetFieldWithBottom(eng::Engine, ast::JuAST)::Union{} 276 | throwInferenceError(InferenceErrorSetFieldWithBottom(eng, ast)) 277 | end 278 | 279 | function displayErrorSetFieldWithBottom(err::InferenceErrorSetFieldWithBottom)::Nothing 280 | eng = err.eng 281 | ast = err.ast 282 | printErrorHead(eng, ast, "BottomError: try to assign a bottom value to a field") 283 | end 284 | 285 | struct InferenceErrorArrayRefBottom <: InferenceError 286 | eng::Engine 287 | ast::JuAST 288 | i::Int 289 | isSet::Bool 290 | end 291 | 292 | function reportErrorArrayRefBottom(eng::Engine, ast::JuAST, i::Int, isSet::Bool)::Union{} 293 | throwInferenceError(InferenceErrorArrayRefBottom(eng, ast, i, isSet)) 294 | end 295 | 296 | function displayErrorArrayRefBottom(err::InferenceErrorArrayRefBottom)::Nothing 297 | eng = err.eng 298 | ast = err.ast 299 | i = err.i 300 | isSet = err.isSet 301 | if isSet 302 | printErrorHead(eng, ast, "BottomError: rhs of a setindex! is a bottom value") 303 | else 304 | if i == 1 305 | printErrorHead(eng, ast, "BottomError: try to index a bottom value") 306 | else 307 | printErrorHead(eng, ast, "BottomError: $(i-1)-th parameter of getindex is an bottom value") 308 | end 309 | end 310 | end 311 | 312 | struct InferenceErrorStringUseBottom <: InferenceError 313 | eng::Engine 314 | ast::JuAST 315 | end 316 | 317 | function reportErrorStringUseBottom(eng::Engine, ast::JuAST)::Union{} 318 | throwInferenceError(InferenceErrorStringUseBottom(eng, ast)) 319 | end 320 | 321 | function displayErrorStringUseBottom(err::InferenceErrorStringUseBottom)::Nothing 322 | eng = err.eng 323 | ast = err.ast 324 | printErrorHead(eng, ast, "BottomError: try to intepolate a bottom into a string") 325 | end 326 | 327 | 328 | struct InferenceErrorStringConstructor <: InferenceError 329 | eng::Engine 330 | ast::JuAST 331 | ms::MethodCallStruct 332 | num::Int 333 | end 334 | 335 | function reportErrorStringConstructor(eng::Engine, ast::JuAST, ms::MethodCallStruct, num::Int)::Union{} 336 | throwInferenceError(InferenceErrorStringConstructor(eng, ast, ms, num)) 337 | end 338 | 339 | function displayErrorStringConstructor(err::InferenceErrorStringConstructor)::Nothing 340 | displayErrorFunCall(InferenceErrorFunCall(err.eng, err.ast, err.ms, err.num, false)) 341 | end 342 | 343 | struct InferenceErrorStringReturnBottom <: InferenceError 344 | eng::Engine 345 | ast::JuAST 346 | ms::MethodCallStruct 347 | end 348 | 349 | function reportErrorStringReturnBottom(eng::Engine, ast::JuAST, ms::MethodCallStruct)::Union{} 350 | throwInferenceError(InferenceErrorStringReturnBottom(eng, ast, ms)) 351 | end 352 | 353 | function displayErrorStringReturnBottom(err::InferenceErrorStringReturnBottom)::Nothing 354 | eng = err.eng 355 | ast = err.ast 356 | printErrorHead(eng, ast, "StringError: interpolation of string returns bottom") 357 | end 358 | 359 | struct InferenceErrorArrayRef <: InferenceError 360 | eng::Engine 361 | ast::JuAST 362 | ms::MethodCallStruct 363 | num::Int 364 | end 365 | 366 | function reportErrorArrayRef(eng::Engine, ast::JuAST, ms::MethodCallStruct, num::Int)::Union{} 367 | throwInferenceError(InferenceErrorArrayRef(eng, ast, ms, num)) 368 | end 369 | 370 | function displayErrorArrayRef(err::InferenceErrorArrayRef)::Nothing 371 | displayErrorFunCall(InferenceErrorFunCall(err.eng, err.ast, err.ms, err.num, false)) 372 | end 373 | 374 | struct InferenceErrorArraySet <: InferenceError 375 | eng::Engine 376 | ast::JuAST 377 | ms::MethodCallStruct 378 | num::Int 379 | end 380 | 381 | function reportErrorArraySet(eng::Engine, ast::JuAST, ms::MethodCallStruct, num::Int)::Union{} 382 | throwInferenceError(InferenceErrorArraySet(eng, ast, ms, num)) 383 | end 384 | 385 | function displayErrorArraySet(err::InferenceErrorArraySet)::Nothing 386 | displayErrorFunCall(InferenceErrorFunCall(err.eng, err.ast, err.ms, err.num, false)) 387 | end 388 | 389 | struct InferenceErrorIndexReturnBottom <: InferenceError 390 | eng::Engine 391 | ast::JuAST 392 | ms::MethodCallStruct 393 | end 394 | 395 | function reportErrorIndexReturnBottom(eng::Engine, ast::JuAST, ms::MethodCallStruct)::Union{} 396 | throwInferenceError(InferenceErrorIndexReturnBottom(eng, ast, ms)) 397 | end 398 | 399 | function displayErrorIndexReturnBottom(err::InferenceErrorIndexReturnBottom)::Nothing 400 | eng = err.eng 401 | ast = err.ast 402 | ms = err.ms 403 | str = getSignature(ms) 404 | printErrorHead(eng, ast, "BottomError: indexing operation $str returns bottom value") 405 | end 406 | 407 | struct InferenceErrorUseConditionallyDefinedVar <: InferenceError 408 | eng::Engine 409 | ast::JuAST 410 | id::Symbol 411 | end 412 | 413 | function reportErrorUseConditionallyDefinedVar(eng::Engine, ast::JuAST, id::Symbol)::Union{} 414 | throwInferenceError(InferenceErrorUseConditionallyDefinedVar(eng, ast, id)) 415 | end 416 | 417 | function displayErrorUseConditionallyDefinedVar(err::InferenceErrorUseConditionallyDefinedVar)::Nothing 418 | eng = err.eng 419 | ast = err.ast 420 | id = err.id 421 | printErrorHead(eng, ast, "UndefinedError: variable $id is conditionally defined here") 422 | end 423 | 424 | struct InferenceErrorFunCallUseBottom <: InferenceError 425 | eng::Engine 426 | ast::JuAST 427 | i::Int 428 | end 429 | 430 | function reportErrorFunCallUseBottom(eng::Engine, ast::JuAST, i::Int)::Union{} 431 | throwInferenceError(InferenceErrorFunCallUseBottom(eng, ast, i)) 432 | end 433 | 434 | function displayErrorFunCallUseBottom(err::InferenceErrorFunCallUseBottom)::Nothing 435 | eng = err.eng 436 | ast = err.ast 437 | i = err.i 438 | if i == 1 439 | printErrorHead(eng, ast, "BottomError: try to calling a bottom value") 440 | else 441 | printErrorHead(eng, ast, "BottomError: $(i-1)-th parameter of function call is an bottom value") 442 | end 443 | end 444 | 445 | struct InferenceErrorFunCallArgs <: InferenceError 446 | eng::Engine 447 | ast::JuAST 448 | ms::MethodCallStruct 449 | i::Vector{Int} 450 | kwi::Vector{Int} 451 | fargs::Vector{FlowNode} 452 | kwargs::Vector{Pair{Symbol, FlowNode}} 453 | isDotcall::Bool 454 | end 455 | 456 | function reportErrorFunCallArgs(eng::Engine, ast::JuAST, ms::MethodCallStruct, i::Vector{Int}, 457 | kwi::Vector{Int}, 458 | fargs::Vector{FlowNode}, 459 | kwargs::Vector{Pair{Symbol, FlowNode}}, 460 | isDotcall::Bool)::Union{} 461 | throwInferenceError(InferenceErrorFunCallArgs(eng, ast, ms, i, kwi, fargs, kwargs, isDotcall)) 462 | end 463 | 464 | @nocheck function explainAbstractType(typ::CompileType)::String 465 | base = toString(typ) 466 | val = typ.typ 467 | if val isa UnionAll 468 | while val isa UnionAll 469 | val = val.body 470 | end 471 | base *= ", \nfully applied type should be $(val)" 472 | end 473 | return base 474 | end 475 | 476 | @nocheck function isBuiltinEqual(tt::CompileType)::Bool 477 | if tt.val === (Core.:(===)) 478 | return true 479 | elseif tt.val ===(Base.:(!==)) 480 | return true 481 | else 482 | return false 483 | end 484 | end 485 | 486 | function displayErrorFunCallArgs(err::InferenceErrorFunCallArgs)::Nothing 487 | eng = err.eng 488 | ast = err.ast 489 | ms = err.ms 490 | i = err.i 491 | kwi = err.kwi 492 | fargs = err.fargs 493 | kwargs = err.kwargs 494 | isDotcall = err.isDotcall 495 | str = getSignature(ms) 496 | if isDotcall 497 | printErrorHead(eng, ast, "ArgumentError: broadcast call argument is of abstract type") 498 | else 499 | printErrorHead(eng, ast, "ArgumentError: Function argument is of abstract type") 500 | end 501 | for ii in i 502 | if ii != 1 503 | println(eng.errio, " $(ii-1)-th argument is of abstract type $(explainAbstractType(fargs[ii].typ))") 504 | else 505 | println(eng.errio, " the called function is of abstract type $(explainAbstractType(fargs[ii].typ))") 506 | end 507 | end 508 | for ii in kwi 509 | name, node = kwargs[ii] 510 | println(eng.errio, " keyword argument $(name) is of abstract type $(explainAbstractType(node.typ))") 511 | end 512 | if isBuiltinEqual(ms.fargs[1].typ) 513 | println(eng.errio, " Don't use ===/!== to compare values of different types, use ( isa Nothing) to split type of a variable explicitly") 514 | end 515 | end 516 | 517 | struct InferenceErrorNoConstructor <: InferenceError 518 | eng::Engine 519 | ast::JuAST 520 | ms::MethodCallStruct 521 | end 522 | 523 | function reportErrorNoConstructor(eng::Engine, ast::JuAST, ms::MethodCallStruct)::Union{} 524 | throwInferenceError(InferenceErrorNoConstructor(eng, ast, ms)) 525 | end 526 | 527 | function displayErrorNoConstructor(err::InferenceErrorNoConstructor)::Nothing 528 | eng = err.eng 529 | ast = err.ast 530 | ms = err.ms 531 | str = getSignature(ms) 532 | printErrorHead(eng, ast, "ConstructorError : constructor $str returns Union{}") 533 | end 534 | 535 | struct InferenceErrorBroadcastBottom <: InferenceError 536 | eng::Engine 537 | ast::JuAST 538 | ms::MethodCallStruct 539 | end 540 | 541 | function reportErrorBroadcastBottom(eng::Engine, ast::JuAST, ms::MethodCallStruct)::Union{} 542 | throwInferenceError(InferenceErrorBroadcastBottom(eng, ast, ms)) 543 | end 544 | 545 | function displayErrorBroadcastBottom(err::InferenceErrorBroadcastBottom)::Nothing 546 | eng = err.eng 547 | ast = err.ast 548 | ms = err.ms 549 | str = getSignature(ms) 550 | printErrorHead(eng, ast, "BroadcastError : broadcast $str returns Union{}") 551 | end 552 | 553 | struct InferenceErrorApplyTypeFailure <: InferenceError 554 | eng::Engine 555 | ast::JuAST 556 | args::Vector{FlowNode} 557 | end 558 | 559 | function reportErrorApplyTypeFailure(eng::Engine, ast::JuAST, args::Vector{FlowNode})::Union{} 560 | throwInferenceError(InferenceErrorApplyTypeFailure(eng, ast, args)) 561 | end 562 | 563 | function displayErrorApplyTypeFailure(err::InferenceErrorApplyTypeFailure)::Nothing 564 | eng = err.eng 565 | ast = err.ast 566 | args = err.args 567 | str = getTypeSignature(args) 568 | printErrorHead(eng, ast, "TypeError: fail to construct type $str") 569 | end 570 | 571 | struct InferenceErrorFieldType <: InferenceError 572 | eng::Engine 573 | ast::JuAST 574 | arg::FlowNode 575 | isSet::Bool 576 | end 577 | 578 | function reportErrorFieldType(eng::Engine, ast::JuAST, arg::FlowNode, isSet::Bool)::Union{} 579 | throwInferenceError(InferenceErrorFieldType(eng, ast, arg, isSet)) 580 | end 581 | 582 | function displayErrorFieldType(err::InferenceErrorFieldType)::Nothing 583 | eng = err.eng 584 | ast = err.ast 585 | arg = err.arg 586 | isSet = err.isSet 587 | accesstype = ifelse(!isSet, "getproperty", "setproperty!") 588 | printErrorHead(eng, ast, "FieldError: calling $accesstype on non-concrete type $(toString(arg.typ))") 589 | end 590 | 591 | struct InferenceErrorNoField <: InferenceError 592 | eng::Engine 593 | ast::JuAST 594 | arg::FlowNode 595 | p::Symbol 596 | end 597 | 598 | function reportErrorNoField(eng::Engine, ast::JuAST, arg::FlowNode, p::Symbol)::Union{} 599 | throwInferenceError(InferenceErrorNoField(eng, ast, arg, p)) 600 | end 601 | 602 | function displayErrorNoField(err::InferenceErrorNoField)::Nothing 603 | eng = err.eng 604 | ast = err.ast 605 | arg = err.arg 606 | p = err.p 607 | printErrorHead(eng, ast, "FieldError: type $(toString(arg.typ)) has no field $p") 608 | end 609 | 610 | struct InferenceErrorSetFieldTypeIncompatible <: InferenceError 611 | eng::Engine 612 | ast::JuAST 613 | x::FlowNode 614 | v::FlowNode 615 | ft::CompileType 616 | p::Symbol 617 | end 618 | 619 | function reportErrorSetFieldTypeIncompatible(eng::Engine, ast::JuAST, x::FlowNode, v::FlowNode, ft::CompileType, p::Symbol)::Union{} 620 | throwInferenceError(InferenceErrorSetFieldTypeIncompatible(eng, ast, x, v, ft, p)) 621 | end 622 | 623 | function displayErrorSetFieldTypeIncompatible(err::InferenceErrorSetFieldTypeIncompatible)::Nothing 624 | eng = err.eng 625 | ast = err.ast 626 | x = err.x 627 | v = err.v 628 | ft = err.ft 629 | p = err.p 630 | printErrorHead(eng, ast, "FieldError: assigning value of type $(toString(v.typ)) to $(toString(x.typ)) 's field $p, which has type $(toString(ft))") 631 | end 632 | 633 | struct InferenceErrorAssignInitIncompatible <: InferenceError 634 | eng::Engine 635 | ast::JuAST 636 | ass::CompileType 637 | init::CompileType 638 | end 639 | 640 | function reportErrorAssignInitIncompatible(eng::Engine, ast::JuAST, ass::CompileType, init::CompileType)::Union{} 641 | throwInferenceError(InferenceErrorAssignInitIncompatible(eng, ast, ass, init)) 642 | end 643 | 644 | function displayErrorAssignInitIncompatible(err::InferenceErrorAssignInitIncompatible)::Nothing 645 | eng = err.eng 646 | ast = err.ast 647 | ass = err.ass 648 | init = err.init 649 | printErrorHead(eng, ast, "AssignError: initializer's type $(toString(init)) is incompatible with asserted type of $(toString(ass))") 650 | println(eng.errio, "Note implicit conversion is disallowed for assignment.") 651 | end 652 | 653 | struct InferenceErrorReturnEnlargeType <: InferenceError 654 | eng::Engine 655 | r1::FlowNode 656 | r2::FlowNode 657 | end 658 | 659 | function reportErrorReturnEnlargeType(eng::Engine, r1::FlowNode, r2::FlowNode)::Union{} 660 | throwInferenceError(InferenceErrorReturnEnlargeType(eng, r1, r2)) 661 | end 662 | 663 | function displayErrorReturnEnlargeType(err::InferenceErrorReturnEnlargeType)::Nothing 664 | eng = err.eng 665 | r1 = err.r1 666 | r2 = err.r2 667 | printErrorHead(eng, r2.ast, "ReturnError: return type is enlarged") 668 | println(eng.errio, "Previous type is $(toString(r1.typ)), ", formatLocation(r1.ast.loc)) 669 | println(eng.errio, "Current type is $(toString(r2.typ)), ", formatLocation(r2.ast.loc)) 670 | end 671 | 672 | struct InferenceErrorIsaLHSBadType <: InferenceError 673 | eng::Engine 674 | ast::JuAST 675 | val::CompileType 676 | end 677 | 678 | function reportErrorIsaLHSBadType(eng::Engine, ast::JuAST, val::CompileType)::Union{} 679 | throwInferenceError(InferenceErrorIsaLHSBadType(eng, ast, val)) 680 | end 681 | 682 | function displayErrorIsaLHSBadType(err::InferenceErrorIsaLHSBadType)::Nothing 683 | eng = err.eng 684 | ast = err.ast 685 | val = err.val 686 | printErrorHead(eng, ast, "IsaError: rhs $(toString(val)) is not a valid splitted target") 687 | end 688 | 689 | struct InferenceErrorCondNotBool <: InferenceError 690 | eng::Engine 691 | ast::JuAST 692 | cond::FlowNode 693 | end 694 | 695 | function reportErrorCondNotBool(eng::Engine, cond::FlowNode)::Union{} 696 | throwInferenceError(InferenceErrorCondNotBool(eng, cond.ast, cond)) 697 | end 698 | 699 | function displayErrorCondNotBool(err::InferenceErrorCondNotBool)::Nothing 700 | eng = err.eng 701 | ast = err.ast 702 | cond = err.cond 703 | printErrorHead(eng, cond.ast, "TypeError: non-boolean type $(toString(cond.typ)) used as condition") 704 | end 705 | 706 | struct InferenceErrorMismatchedSplit <: InferenceError 707 | eng::Engine 708 | ast::JuAST 709 | xnode::FlowNode 710 | rhsnode::FlowNode 711 | end 712 | 713 | function reportErrorMismatchedSplit(eng::Engine, ast::JuAST, xnode::FlowNode, rhsnode::FlowNode)::Union{} 714 | throwInferenceError(InferenceErrorMismatchedSplit(eng, ast, xnode, rhsnode)) 715 | end 716 | 717 | function displayErrorMismatchedSplit(err::InferenceErrorMismatchedSplit)::Nothing 718 | eng = err.eng 719 | ast = err.ast 720 | xnode = err.xnode 721 | rhsnode = err.rhsnode 722 | printErrorHead(eng, ast, "IsaError: can't split type $(toString(xnode.typ)) to $(toString(rhsnode.typ))") 723 | end 724 | 725 | struct InferenceErrorIsaBadForm <: InferenceError 726 | eng::Engine 727 | ast::JuAST 728 | msg::String 729 | end 730 | 731 | function reportErrorIsaBadForm(eng::Engine, ast::JuAST, msg::String)::Union{} 732 | throwInferenceError(InferenceErrorIsaBadForm(eng, ast, msg)) 733 | end 734 | 735 | function displayErrorIsaBadFor(err::InferenceErrorIsaBadForm)::Nothing 736 | eng = err.eng 737 | ast = err.ast 738 | msg = err.msg 739 | printErrorHead(eng, ast, msg) 740 | end 741 | 742 | struct InferenceErrorIfEnlargeType <: InferenceError 743 | eng::Engine 744 | r1::FlowNode 745 | r2::FlowNode 746 | end 747 | 748 | function reportErrorIfEnlargeType(eng::Engine, r1::FlowNode, r2::FlowNode)::Union{} 749 | throwInferenceError(InferenceErrorIfEnlargeType(eng, r1, r2)) 750 | end 751 | 752 | function displayErrorIfEnlargeType(err::InferenceErrorIfEnlargeType)::Nothing 753 | eng = err.eng 754 | r1 = err.r1 755 | r2 = err.r2 756 | printErrorHead(eng, r2.ast, "IfError: if type is enlarged") 757 | println(eng.errio, "Previous type is $(toString(r1.typ)), ", formatLocation(r1.ast.loc)) 758 | println(eng.errio, "Current type is $(toString(r2.typ)), ", formatLocation(r2.ast.loc)) 759 | end 760 | 761 | struct InferenceErrorFailedToDestruct <: InferenceError 762 | eng::Engine 763 | ast::JuAST 764 | msg::String 765 | end 766 | 767 | function reportErrorFailedToDestruct(eng::Engine, ast::JuAST, msg::String)::Union{} 768 | throwInferenceError(InferenceErrorFailedToDestruct(eng, ast, msg)) 769 | end 770 | 771 | function displayErrorFailedToDestruct(err::InferenceErrorFailedToDestruct)::Nothing 772 | eng = err.eng 773 | ast = err.ast 774 | msg = err.msg 775 | printErrorHead(eng, ast, "AssignError: Failed to destruct rhs to lhs : $msg") 776 | end 777 | 778 | struct InferenceErrorIsaRHSNonconst <: InferenceError 779 | eng::Engine 780 | ast::JuAST 781 | end 782 | 783 | function reportErrorIsaRHSNonconst(eng::Engine, ast::JuAST)::Union{} 784 | throwInferenceError(InferenceErrorIsaRHSNonconst(eng, ast)) 785 | end 786 | 787 | function displayErrorIsaRHSNonconst(err::InferenceErrorIsaRHSNonconst) 788 | eng = err.eng 789 | ast = err.ast 790 | printErrorHead(eng, ast, "TypeError: rhs of isa is not a constant") 791 | end 792 | 793 | struct InferenceErrorTypeAssertNonconst <: InferenceError 794 | eng::Engine 795 | ast::JuAST 796 | end 797 | 798 | function reportErrorTypeAssertNonconst(eng::Engine, ast::JuAST)::Union{} 799 | throwInferenceError(InferenceErrorTypeAssertNonconst(eng, ast)) 800 | end 801 | 802 | function displayErrorTypeAssertNonconst(err::InferenceErrorTypeAssertNonconst)::Nothing 803 | eng = err.eng 804 | ast = err.ast 805 | printErrorHead(eng, ast, "TypeError: the typed used for type assertion is not a constant") 806 | end 807 | 808 | 809 | struct InferenceErrorkeywordNotDefined <: InferenceError 810 | eng::Engine 811 | ast::JuAST 812 | sym::Symbol 813 | end 814 | 815 | function reportErrorkeywordNotDefined(eng::Engine, ast::JuAST, sym::Symbol)::Union{} 816 | throwInferenceError(InferenceErrorkeywordNotDefined(eng, ast, sym)) 817 | end 818 | 819 | function displayErrorkeywordNotDefined(err::InferenceErrorkeywordNotDefined)::Nothing 820 | eng = err.eng 821 | ast = err.ast 822 | sym = err.sym 823 | printErrorHead(eng, ast, "UndefinedError : keyword $sym is required but not inputed") 824 | end 825 | 826 | struct InferenceErrorElementNonSameType <: InferenceError 827 | eng::Engine 828 | ast::JuAST 829 | t1::FlowNode 830 | t2::FlowNode 831 | typed::Bool 832 | end 833 | 834 | function reportErrorElementNonSameType(eng::Engine, ast::JuAST, t1::FlowNode, t2::FlowNode, typed::Bool)::Union{} 835 | throwInferenceError(InferenceErrorElementNonSameType(eng, ast, t1, t2, typed)) 836 | end 837 | 838 | function displayErrorElementNonSameType(err::InferenceErrorElementNonSameType)::Nothing 839 | eng = err.eng 840 | ast = err.ast 841 | t1 = err.t1 842 | t2 = err.t2 843 | typed = err.typed 844 | if typed 845 | printErrorHead(eng, ast, "Incompatiable type with typed array") 846 | println(eng.errio, "Previous type is $(toString(lift(t1.typ))), ", formatLocation(t1.ast.loc)) 847 | println(eng.errio, "Current type is $(toString(t2.typ)), ", formatLocation(t2.ast.loc)) 848 | else 849 | printErrorHead(eng, ast, "Array element must have the same type") 850 | println(eng.errio, "Previous type is $(toString(t1.typ)), ", formatLocation(t1.ast.loc)) 851 | println(eng.errio, "Current type is $(toString(t2.typ)), ", formatLocation(t2.ast.loc)) 852 | end 853 | end 854 | 855 | 856 | struct InferenceErrorEmptyAnyArray <: InferenceError 857 | eng::Engine 858 | ast::JuAST 859 | end 860 | 861 | function reportErrorEmptyAnyArray(eng::Engine, ast::JuAST)::Union{} 862 | throwInferenceError(InferenceErrorEmptyAnyArray(eng, ast)) 863 | end 864 | 865 | function displayErrorEmptyAnyArray(err::InferenceErrorEmptyAnyArray)::Nothing 866 | eng = err.eng 867 | ast = err.ast 868 | printErrorHead(eng, ast, "Use `Any[]` instead of `[]` to construct Any array") 869 | end 870 | -------------------------------------------------------------------------------- /src/adaptor/InferenceErrorUtility.jl: -------------------------------------------------------------------------------- 1 | # For debug only 2 | abstract type InferenceError end 3 | function throwInferenceError(err::InferenceError)::Union{} 4 | Base.throw(err) 5 | end 6 | 7 | @nocheck function toString(typ::CompileType)::String 8 | return string(typ.typ) 9 | end 10 | 11 | @nocheck function printErrorHead(eng::Engine, ast::JuAST, errkind::String) 12 | loc = ast.loc 13 | println(eng.errio, formatLocation(loc)) 14 | print(eng.errio, " ") 15 | println(eng.errio, errkind) 16 | try 17 | code = loc.file.code[loc.span[1]:loc.span[2]] 18 | needTab = !occursin('\n', code) 19 | if needTab 20 | println(eng.errio, ' '^4, code) 21 | else 22 | println(eng.errio, '\n', code) 23 | end 24 | catch e 25 | println(eng.errio, ' '^4, "(Failed to extract code)") 26 | end 27 | end 28 | 29 | @nocheck function printSignature(io::IOBuffer, f::CompileType, typ::Vector{CompileType}, kwtyp::Vector{Pair{Symbol, CompileType}}) 30 | if isConstVal(f) 31 | print(io, string(f.val), '(') 32 | else 33 | print(io, string(f.typ), '(') 34 | end 35 | for i in 1:length(typ) 36 | print(io, "::", string(typ[i].typ)) 37 | if i != length(typ) 38 | print(io, ", ") 39 | else 40 | if length(kwtyp) != 0 41 | print(io, "; ") 42 | end 43 | end 44 | end 45 | for i in kwtyp 46 | print(io, "$(i.first)::", string(i.second.typ)) 47 | end 48 | print(io, ')') 49 | end 50 | 51 | 52 | function getSignature(ms::MethodCallStruct)::String 53 | io = IOBuffer() 54 | args = ms.fargs 55 | argstt = Vector{CompileType}(undef, length(args) - 1) 56 | for i in 1:length(args) - 1 57 | argstt[i] = args[i + 1].typ 58 | end 59 | kwargstt = similar(ms.kwargs, Pair{Symbol, CompileType}) 60 | for i in eachindex(kwargstt) 61 | kwargstt[i] = ms.kwargs[i].first=>ms.kwargs[i].second.typ 62 | end 63 | printSignature(io, args[1].typ, argstt, kwargstt) 64 | return String(take!(io)) 65 | end 66 | 67 | @nocheck function toIndex(v::Vector{Int})::String 68 | join(map(x->ifelse(x==1,"the called function", string(x-1)), v), " ,") 69 | end 70 | 71 | 72 | @nocheck function getTypeSignature(args::Vector{FlowNode})::String 73 | io = IOBuffer() 74 | print(io, string(args[1].typ.val), '{') 75 | for i in 2:length(args) 76 | print(io, string(args[i].typ.val)) 77 | if i != length(args) 78 | print(io, ", ") 79 | end 80 | end 81 | print(io, '}') 82 | return String(take!(io)) 83 | end 84 | -------------------------------------------------------------------------------- /src/adaptor/JSAdaptor.jl: -------------------------------------------------------------------------------- 1 | export parseAndConstructHTML 2 | 3 | import ..SyntaxDefinition.formatLocation 4 | import Base.RefValue 5 | 6 | const wrapperString = open(joinpath(@__DIR__, "../www/index_template.html")) do f 7 | read(f, String) 8 | end 9 | 10 | @nocheck function parseAndConstructHTML(raw::String, filename::String, outdir::String) 11 | ast = parseJuAST(raw, filename) 12 | html = wrap(constructHTML(raw, ast)) 13 | js = serializeAST(ast.node) 14 | htmlf = open(joinpath(outdir, "index.html"), write = true) 15 | write(htmlf, html) 16 | close(htmlf) 17 | jsf = open(joinpath(outdir, "data.js"), write = true) 18 | write(jsf, js) 19 | close(jsf) 20 | return ast 21 | end 22 | 23 | function constructHTML(raw::String, ast::JuAST) 24 | rel = convertGreenNode(raw, ast.node) 25 | # we can't join them with '\n', which might introduce a 4px space between spans 26 | join(rel) 27 | end 28 | 29 | function _helper(x::SubString)::String 30 | escapseNewlineWs(String(x)) 31 | end 32 | 33 | function makeCodeSegment(raw::String, pos::RefValue{Int}, ast::MutableGreenNode) 34 | head = ast.head 35 | kind = head.kind 36 | start = pos[] + 1 37 | spanlen = ast.span.second - ast.span.first + 1 38 | last = pos[] + spanlen 39 | data = raw[start:last] 40 | pos[] = pos[] + spanlen 41 | range = "start = $start last = $last" 42 | if kind == NewlineWsKind 43 | s = join(map(_helper, split(data, '\n')), """""") 44 | return s 45 | elseif kind == WhitespaceKind 46 | data = escapseEmtpySpace(data) 47 | return """$data""" 48 | else 49 | data = htmlEscape(data) 50 | if kind == CommentKind 51 | return """$data""" 52 | elseif JuliaSyntax.is_trivia(head) 53 | return """$data""" 54 | else 55 | return """$data""" 56 | end 57 | end 58 | #= 59 | """$s""" 60 | """ """ 61 | """$s""" 62 | """fun""" 63 | """""" 64 | """)""" 65 | 66 | =# 67 | end 68 | 69 | function escapseEmtpySpace(s::String)::String 70 | replace(s, ' '=>" ") 71 | end 72 | 73 | function escapseNewlineWs(s::String)::String 74 | replace(s, ' '=>" ") 75 | end 76 | 77 | function htmlEscape(s::String)::String 78 | replace(s, '<'=>"<", 79 | '>'=>">", 80 | '&'=>"&", 81 | '\''=>"'", 82 | '"'=> """, 83 | ' '=>" " 84 | ) 85 | end 86 | 87 | function convertGreenNode!(rel, pos::RefValue{Int}, raw::String, ast::MutableGreenNode) 88 | args = ast.args 89 | if args isa Tuple{} 90 | push!(rel, makeCodeSegment(raw, pos, ast)) 91 | else 92 | # a non-terminal 93 | for i in args 94 | convertGreenNode!(rel, pos, raw, i) 95 | end 96 | end 97 | end 98 | 99 | function convertGreenNode(raw::String, ast::MutableGreenNode) 100 | rel = String[] 101 | pos::RefValue{Int} = RefValue{Int}(0) 102 | convertGreenNode!(rel, pos, raw, ast) 103 | return rel 104 | end 105 | 106 | function wrap(x::String) 107 | return replace(wrapperString, "__TEMPLATE_PLACEHOLDER__"=>x) 108 | end 109 | 110 | 111 | function serializeAST!(alloc::RefValue{Int}, rel::Vector{String}, ast::MutableGreenNode, parentid::String)::String 112 | alloc[] = alloc[] + 1 113 | id = alloc[] 114 | span = ast.span 115 | start = span.first 116 | last = span.second 117 | head = ast.head 118 | push!(rel, "var ast$id = new ASTNode(new SyntaxHead(\"$(summary(head))\", $(head.flags)),$parentid, $start, $last)") 119 | c = ast.args 120 | if c isa Tuple{} 121 | return "ast$id" 122 | else 123 | strs = similar(c, String) 124 | for i in eachindex(strs) 125 | strs[i] = serializeAST!(alloc, rel, c[i], "ast$id") 126 | end 127 | param = join(strs, ',') 128 | push!(rel, "ast$id.args = [$param]") 129 | return "ast$id" 130 | end 131 | end 132 | 133 | function serializeAST(ast::MutableGreenNode) 134 | alloc = RefValue{Int}(0) 135 | rel = String[] 136 | serializeAST!(alloc, rel, ast, "null") 137 | return join(rel, ";\n") 138 | end 139 | 140 | -------------------------------------------------------------------------------- /src/adaptor/JuExprValidator.jl: -------------------------------------------------------------------------------- 1 | # This file is used for debugging 2 | # It valids whether a JuExpr and its mapping faithfully represents a JuAST with no information loss 3 | 4 | function lookup(map::SourceMapping, ast::JuAST) 5 | if !haskey(map.ast2exMapping, ast) 6 | println(ast) 7 | end 8 | if ast.head == :quote 9 | return 10 | end 11 | if ast.head == :macrocall 12 | return 13 | end 14 | for i in ast.args 15 | lookup(map, i) 16 | end 17 | end 18 | 19 | function lookupToplevel(result) 20 | ast = result.ast 21 | if ast.head == :toplevel 22 | for i in ast.args 23 | lookupFunctionOrModule(result, i) 24 | end 25 | elseif ast.head == :module 26 | lookupFunctionOrModule(result, ast) 27 | else 28 | error("Not a valid ast") 29 | end 30 | return 31 | end 32 | 33 | function lookupFunctionOrModule(result::ConstructJuExprResult, ast::JuAST) 34 | if ast.head == :module 35 | for i in ast.args 36 | lookupFunctionOrModule(result, i) 37 | end 38 | elseif ast.head == :function 39 | lookup(result.map[result.revMap[ast]], ast) 40 | else 41 | end 42 | return 43 | end 44 | 45 | -------------------------------------------------------------------------------- /src/adaptor/Scope.jl: -------------------------------------------------------------------------------- 1 | #= 2 | Julia's scope behavior is extremely bad 3 | For example, the variable t in following code is a local variable of the function instead of loop 4 | 5 | function f() 6 | if true 7 | for i in 1:10 8 | t = 2 9 | println(t) 10 | end 11 | # t is 2 here 12 | else 13 | t = 1 14 | end 15 | end 16 | 17 | Also, Julia's if expression is not scoped, so following code is possible: 18 | function g() 19 | if true 20 | local x = 1 21 | end 22 | x = 1.0 23 | end 24 | 25 | That's said, Julia's unscoped if and random declaration causes a lot of troubles. 26 | 27 | 28 | We propose following restriction 29 | 1. A variable can declared at most once 30 | 2. If a variable is declared somewhere, then all assignments to this variable must be dominated by that declaration 31 | 3. If the declaration is in an if clause, then that declaration has local scope (local in that branch of if) 32 | (otherwise, the user should promote that declaration outside of if). 33 | 34 | This simplifies our type inference algorithm, there is no need to look forward to find the type declaration 35 | or perform join on the definition for a dead declaration in an if expression 36 | 37 | Our analysis proceeds in several steps: 38 | 1. Firstly, we scan the AST to collect all declarations (typed or untyped) and make sure that a variable can declared at most once 39 | If there are any multiple definitions, then we quit immediately 40 | 2. Then we scan the AST again, but this time we also pay attention to assignment expressions 41 | a) When encounter an assignment expression, 42 | Check whether the declaration is dead in step 3d) 43 | if so, then a declaration is used outside of a if, abort 44 | Then check whether the declaration is marked in step 2c), 45 | if not, then a declaration is occured after an assignment, abort 46 | b) When encounter an expression other than if (or && || ), we walk subexpressions recursively (in evaluation order) 47 | c) When encounter a declaration, mark this declaration 48 | c) When encounter an if expression, 49 | if (c1) 50 | b1 51 | elseif (c2) 52 | b2 53 | elseif (c3) 54 | b3 55 | end 56 | we check c1 then check b1, we mark the variable declared in b1 (that is, variables marked in b1 with step 3c) dead 57 | then we check c2 then b2, then we mark the variable declared in b2 dead 58 | 59 | function walkMark(v, markedInParent, markInChild, deadset) 60 | if v isa IfStmt 61 | walked!(cond, markedInParent, markInChild) 62 | newchild = [] 63 | cur = merge(markedInParent, markInChild) 64 | walked!(body, cur, newchild) 65 | # newchild is discarded, so they are all dead 66 | append(deadset, newchild) 67 | elseif v isa Assign 68 | if v in markedInParent || v in markInChild 69 | error("Unmarked) 70 | end 71 | return [] 72 | elseif v isa Declaration 73 | return [v] 74 | else 75 | for subexpression: 76 | walkMark(v, markedInParent, markInChild) 77 | end 78 | end 79 | end 80 | =# 81 | function preAnalyzeScope() 82 | end 83 | #= 84 | I decide to use a walker here, otherwise too repetitive... 85 | =# 86 | abstract type JuExprWalker end 87 | function walkLiteral(walker::JuExprWalker{T}, ex::JuExpr)::T where T 88 | end 89 | function walkAssign(walker::JuExprWalker{T}, ex::JuExpr)::T where T 90 | end 91 | function walkTypedAssign(walker::JuExprWalker{T}, ex::JuExpr)::T where T 92 | end 93 | function walkJuExpr(walker::JuExprWalker{T}, ex::JuExpr)::T where T 94 | # parent and current is always disjoint, because we can have at most one definition 95 | val = ast.val 96 | if val isa Literal 97 | return 98 | elseif val isa Assign 99 | id = val.lhs.id 100 | if haskey(parent, id) || haskey(id, current) 101 | return 102 | elseif haskey(parent, dead) 103 | error("Dead variable is used") 104 | else 105 | error("Assigned too early") 106 | end 107 | elseif val isa FunCall 108 | analyzeScope(val.f, parent, current, dead) 109 | for i in val.args 110 | analyzeScope(i, parent, current, dead) 111 | end 112 | for i in val.kwargs 113 | analyzeScope(i, parent, current, dead) 114 | end 115 | return 116 | elseif val isa Block 117 | for i in val.stmts 118 | analyzeScope(i, parent, current, dead) 119 | end 120 | elseif val isa Var 121 | if haskey(dead, val.id) 122 | error("Using dead variable outside of loop") 123 | end 124 | return 125 | elseif val isa CurlyCall 126 | analyzeScope(val.f, parent, current, dead) 127 | for i in val.args 128 | analyzeScope(i, parent, current, dead) 129 | end 130 | return 131 | elseif val isa IfStmt 132 | error() 133 | for i in val.branches 134 | analyzeScope(i, parent, current, i[1]) 135 | analyzeScope(i, parent, current, i[2]) 136 | end 137 | else_e = val.else_ 138 | if else_e isa Nothing 139 | return 140 | else 141 | decideScopeVariable!(rel, mod, shadow, ctx, else_e) 142 | end 143 | elseif val isa Return 144 | eval = val.e 145 | if eval isa Nothing 146 | return 147 | else 148 | analyzeScope(eval, parent, current, dead) 149 | return 150 | end 151 | elseif val isa ArrayRef 152 | analyzeScope(var.arr, parent, current, dead) 153 | for i in val.i 154 | analyzeScope(i, parent, current, dead) 155 | end 156 | return 157 | elseif val isa GetProperty 158 | analyzeScope(var.x, parent, current, dead) 159 | return 160 | elseif val isa SetProperty 161 | analyzeScope(var.x, parent, current, dead) 162 | analyzeScope(var.v, parent, current, dead) 163 | return 164 | elseif val isa ArraySet 165 | analyzeScope(var.x, parent, current, dead) 166 | for i in val.i 167 | analyzeScope(i, parent, current, dead) 168 | end 169 | analyzeScope(var.v, parent, current, dead) 170 | return 171 | elseif val isa TypedAssert 172 | error() 173 | id = val.lhs.id 174 | if haskey(parent, id) || haskey(id, current) 175 | return 176 | elseif haskey(parent, dead) 177 | error("Dead variable is used") 178 | else 179 | error("Assigned too early") 180 | end 181 | return 182 | elseif val isa ForStmt 183 | analyzeScope(val.iter, parent, current, dead) 184 | newscope = enterScope(parent, current, val.scope) 185 | newscope[val.var] = true 186 | analyzeScope(val.body, newscope, DefMap(), dead) 187 | elseif val isa WhileStmt 188 | analyzeScope(val.cond, parent, current, dead) 189 | newscope = enterScope(parent, current, val.scope) 190 | analyzeScope(val.body, newscope, DefMap(), dead) 191 | elseif val isa FunDef 192 | newscope = enterScope(parent, current, val.scope) 193 | error() 194 | else 195 | error("Unimplemented $ast") 196 | end 197 | end 198 | end 199 | 200 | function enterScope(parent::DefMap, current::DefMap, scope::DefMap) 201 | res = DefMap() 202 | for i in keys(scope) 203 | res[i] = false 204 | end 205 | for i in keys(current) 206 | if !haskey(res, i) 207 | res[i] = current[i] 208 | end 209 | end 210 | for i in keys(parent) 211 | if !haskey(res, i) 212 | res[i] = parent[i] 213 | end 214 | end 215 | return res 216 | end 217 | 218 | const DefMap = Dict{Symbol, JuExpr} 219 | 220 | function analyzeScope(ex::JuExpr, parent::DefMap, current::DefMap, dead::DefMap) 221 | # parent and current is always disjoint, because we can have at most one definition 222 | val = ast.val 223 | if val isa Literal 224 | return 225 | elseif val isa Assign 226 | id = val.lhs.id 227 | if haskey(parent, id) || haskey(id, current) 228 | return 229 | elseif haskey(parent, dead) 230 | error("Dead variable is used") 231 | else 232 | error("Assigned too early") 233 | end 234 | elseif val isa FunCall 235 | analyzeScope(val.f, parent, current, dead) 236 | for i in val.args 237 | analyzeScope(i, parent, current, dead) 238 | end 239 | for i in val.kwargs 240 | analyzeScope(i, parent, current, dead) 241 | end 242 | return 243 | elseif val isa Block 244 | for i in val.stmts 245 | analyzeScope(i, parent, current, dead) 246 | end 247 | elseif val isa Var 248 | if haskey(dead, val.id) 249 | error("Using dead variable outside of loop") 250 | end 251 | return 252 | elseif val isa CurlyCall 253 | analyzeScope(val.f, parent, current, dead) 254 | for i in val.args 255 | analyzeScope(i, parent, current, dead) 256 | end 257 | return 258 | elseif val isa IfStmt 259 | error() 260 | for i in val.branches 261 | analyzeScope(i, parent, current, i[1]) 262 | analyzeScope(i, parent, current, i[2]) 263 | end 264 | else_e = val.else_ 265 | if else_e isa Nothing 266 | return 267 | else 268 | decideScopeVariable!(rel, mod, shadow, ctx, else_e) 269 | end 270 | elseif val isa Return 271 | eval = val.e 272 | if eval isa Nothing 273 | return 274 | else 275 | analyzeScope(eval, parent, current, dead) 276 | return 277 | end 278 | elseif val isa ArrayRef 279 | analyzeScope(var.arr, parent, current, dead) 280 | for i in val.i 281 | analyzeScope(i, parent, current, dead) 282 | end 283 | return 284 | elseif val isa GetProperty 285 | analyzeScope(var.x, parent, current, dead) 286 | return 287 | elseif val isa SetProperty 288 | analyzeScope(var.x, parent, current, dead) 289 | analyzeScope(var.v, parent, current, dead) 290 | return 291 | elseif val isa ArraySet 292 | analyzeScope(var.x, parent, current, dead) 293 | for i in val.i 294 | analyzeScope(i, parent, current, dead) 295 | end 296 | analyzeScope(var.v, parent, current, dead) 297 | return 298 | elseif val isa TypedAssert 299 | error() 300 | id = val.lhs.id 301 | if haskey(parent, id) || haskey(id, current) 302 | return 303 | elseif haskey(parent, dead) 304 | error("Dead variable is used") 305 | else 306 | error("Assigned too early") 307 | end 308 | return 309 | elseif val isa ForStmt 310 | analyzeScope(val.iter, parent, current, dead) 311 | newscope = enterScope(parent, current, val.scope) 312 | newscope[val.var] = true 313 | analyzeScope(val.body, newscope, DefMap(), dead) 314 | elseif val isa WhileStmt 315 | analyzeScope(val.cond, parent, current, dead) 316 | newscope = enterScope(parent, current, val.scope) 317 | analyzeScope(val.body, newscope, DefMap(), dead) 318 | elseif val isa FunDef 319 | newscope = enterScope(parent, current, val.scope) 320 | error() 321 | else 322 | error("Unimplemented $ast") 323 | end 324 | end 325 | -------------------------------------------------------------------------------- /src/adaptor/ScopeChecking.jl: -------------------------------------------------------------------------------- 1 | function hasVar(info::ScopeInfo, var::Symbol)::Bool 2 | haskey(info.locals, var) || haskey(info.implicitlocals, var) || haskey(info.globals, var) 3 | end 4 | 5 | function hasVar(ctx::ScopeInfoContext, var::Symbol)::Bool 6 | for i in reverse(eachindex(ctx.chains)) 7 | info = ctx.chains[i] 8 | if hasVar(info, var) 9 | return true 10 | end 11 | end 12 | return false 13 | end 14 | 15 | function reportErrorASTAssignmentBeforeDeclaration(ctx::ScopeInfoContext, sym::Symbol, assign::JuAST, declare::JuAST) 16 | io = IOBuffer() 17 | println(io, "At $(formatLocation(assign.loc))\nDeclaration of variable $sym must be placed before an assignment") 18 | println(io, "Previously declared at $(formatLocation(declare.loc))") 19 | msg = String(take!(io)) 20 | reportASTError(ctx.ctx, declare, msg) 21 | end 22 | 23 | 24 | function updateVar(ctx::ScopeInfoContext, sym::Symbol, ast::JuAST)::Nothing 25 | # firstly we search through the scope chain 26 | for i in reverse(eachindex(ctx.chains)) 27 | local info = ctx.chains[i] 28 | # the variable is declared in this scope 29 | if hasVar(info, sym) 30 | # either the variable belongs to innermost scope 31 | # or the variable belongs to parent scope 32 | 33 | # If the variable belongs to innermost scope 34 | if i == lastindex(ctx.chains) 35 | # has one declaration 36 | if haskey(info.locals, sym) 37 | # we check whether we have encounter that declaration 38 | if !(sym in info.curlocals) 39 | reportErrorASTAssignmentBeforeDeclaration(ctx, sym, ast, info.locals[sym]) 40 | end 41 | elseif haskey(info.globals, sym) 42 | if !(sym in info.curglobals) 43 | reportErrorASTAssignmentBeforeDeclaration(ctx, sym, ast, info.globals[sym]) 44 | end 45 | end 46 | # has no declaration, the variable is implicitly defined 47 | push!(info.curimplicitlocals, sym) 48 | return 49 | else 50 | # If the variable belongs to outer scope 51 | # if the variable is updated 52 | # we require that the variable is already declared in outer scope 53 | if (sym in info.curlocals) || (sym in info.curglobals) || (sym in info.curimplicitlocals) 54 | # if a local variable is updated, we record it in this scope 55 | if !(sym in info.curglobals) 56 | # we populate every scope in the chain 57 | for j in i+1:lastindex(ctx.chains) 58 | local info = ctx.chains[j] 59 | if !haskey(info.modified, sym) 60 | info.modified[sym] = ast 61 | end 62 | end 63 | end 64 | return 65 | else 66 | if haskey(info.globals, sym) 67 | declareast = info.globals[sym] 68 | elseif haskey(info.locals, sym) 69 | declareast = info.locals[sym] 70 | elseif haskey(info.implicitlocals, sym) 71 | declareast = info.implicitlocals[sym] 72 | else 73 | throw(InternalError("error")) 74 | end 75 | reportErrorASTAssignmentBeforeDeclaration(ctx, sym, ast, declareast) 76 | end 77 | end 78 | end 79 | end 80 | throw(InternalError("error")) 81 | end 82 | 83 | function analyzeScopeVariableAssignLHS(ctx::ScopeInfoContext, lhs::JuAST, isLocalDeclare::Bool, isGlobalDeclare::Bool)::ScopeInfoContext 84 | # isDeclare decides whether this is the lhs of a explicit scope declaration 85 | # or an implicit one 86 | walkTurn = ctx.walkTurn 87 | info = ctx.chains[end] 88 | if lhs.head == :identifier 89 | sym = cast2Symbol(lhs.val) 90 | if walkTurn == 1 91 | # the first time we meet a declaration, we record it 92 | # otherwise we raise an error 93 | if isLocalDeclare 94 | if haskey(info.globals, sym) 95 | reportASTError(ctx.ctx, lhs, "Variable is already declared to be a global") 96 | end 97 | if haskey(info.locals, sym) 98 | reportASTError(ctx.ctx, lhs, "Local declares more than once") 99 | end 100 | info.locals[sym] = lhs 101 | elseif isGlobalDeclare 102 | if haskey(info.locals, sym) 103 | reportASTError(ctx.ctx, lhs, "Variable is already declared to be a local") 104 | end 105 | if haskey(info.globals, sym) 106 | reportASTError(ctx.ctx, lhs, "twice defintion") 107 | end 108 | info.globals[sym] = lhs 109 | end 110 | elseif walkTurn == 2 111 | # the second time we meet a non-declaration, we record it 112 | if !isLocalDeclare && !isGlobalDeclare 113 | # if we haven't encounter this variable anywhere, then it belongs to innermost scope 114 | if !hasVar(ctx, sym) 115 | info.implicitlocals[sym] = lhs 116 | end 117 | end 118 | elseif walkTurn == 3 119 | if isLocalDeclare 120 | push!(info.curlocals, sym) 121 | elseif isGlobalDeclare 122 | push!(info.curglobals, sym) 123 | else 124 | updateVar(ctx, sym, lhs) 125 | end 126 | end 127 | elseif lhs.head == :(::) 128 | # analyze type first 129 | analyzeScopeVariable(ctx, lhs.args[2]) 130 | var = lhs.args[1] 131 | if var.head == :identifier 132 | sym = cast2Symbol(var.val) 133 | if walkTurn == 1 134 | if (!isLocalDeclare && !isGlobalDeclare) && hasVar(ctx, sym) 135 | # Julia interpret this as an update operation 136 | # while we expect it to be an local declaration 137 | # report error here 138 | if !hasVar(ctx.chains[end], sym) 139 | reportASTError(ctx.ctx, lhs, "type assertion can only apply to variable in current scope") 140 | else 141 | reportASTError(ctx.ctx, lhs, "type assertion applies multiple times") 142 | end 143 | end 144 | # we treat this like a local... 145 | info.locals[sym] = var 146 | elseif walkTurn == 3 147 | push!(info.curlocals, sym) 148 | end 149 | end 150 | elseif lhs.head == :(.) || lhs.head == :ref 151 | analyzeScopeVariable(ctx, lhs) 152 | # not an assignment 153 | elseif lhs.head == :tuple 154 | for i in lhs.args 155 | analyzeScopeVariableAssignLHS(ctx, i, isLocalDeclare, isGlobalDeclare) 156 | end 157 | end 158 | # refuse to analyze in other cases 159 | return ctx 160 | end 161 | 162 | function enterScope(ctx::ScopeInfoContext, parent::JuAST, body::JuAST) 163 | if parent.head == :for 164 | iter = parent.args[1] 165 | if iter.head == :block 166 | iter = iter.args[1] 167 | end 168 | analyzeScopeVariable(ctx, iter.args[2]) 169 | end 170 | if ctx.walkTurn == 1 171 | info = ScopeInfo(parent) 172 | addInfo!(ctx, parent, info) 173 | else 174 | info = ctx.infos[parent] 175 | end 176 | push!(ctx.chains, info) 177 | if parent.head == :for 178 | iter = parent.args[1] 179 | # multiple binding 180 | if iter.head == :block 181 | for i in iter.args 182 | analyzeScopeVariableAssignLHS(ctx, i.args[1], true, false) 183 | end 184 | else 185 | lhs = iter.args[1] 186 | analyzeScopeVariableAssignLHS(ctx, lhs, true, false) 187 | end 188 | elseif parent.head == :let 189 | for i in parent.args[1].args 190 | # assignment form 191 | analyzeScopeVariableAssign(ctx, i, true, false) 192 | end 193 | else 194 | error() 195 | end 196 | analyzeScopeVariable(ctx, body) 197 | pop!(ctx.chains) 198 | return 199 | end 200 | 201 | 202 | function enterWhileScope(ctx::ScopeInfoContext, parent::JuAST, body::JuAST) 203 | if ctx.walkTurn == 1 204 | info = ScopeInfo(parent) 205 | addInfo!(ctx, parent, info) 206 | else 207 | info = ctx.infos[parent] 208 | end 209 | push!(ctx.chains, info) 210 | ctx.infos[parent] = info 211 | analyzeScopeVariable(ctx, body) 212 | pop!(ctx.chains) 213 | return 214 | end 215 | 216 | function analyzeScopeVariableAssign(ctx::ScopeInfoContext, ast::JuAST, isLocalDeclare::Bool, isGlobalDeclare::Bool)::ScopeInfoContext 217 | if ast.head == :(=) && length(ast.args) == 2 218 | analyzeScopeVariable(ctx, ast.args[2]) 219 | analyzeScopeVariableAssignLHS(ctx, ast.args[1], isLocalDeclare, isGlobalDeclare) 220 | else 221 | analyzeScopeVariableAssignLHS(ctx, ast, isLocalDeclare, isGlobalDeclare) 222 | end 223 | end 224 | 225 | # Every scope construct has an associating ScopeInfo 226 | function analyzeScopeVariable(ctx::ScopeInfoContext, ast::JuAST)::ScopeInfoContext 227 | if ast.head == :(=) 228 | return analyzeScopeVariableAssign(ctx, ast, false, false) 229 | elseif ast.head == :global 230 | for i in ast.args 231 | analyzeScopeVariableAssign(ctx, i, false, true) 232 | end 233 | return ctx 234 | elseif ast.head == :local 235 | for i in ast.args 236 | analyzeScopeVariableAssign(ctx, i, true, false) 237 | end 238 | return ctx 239 | elseif (ast.head == :literal || 240 | ast.head == :identifier || 241 | ast.head == :char || 242 | ast.head == :break || 243 | ast.head == :continue 244 | ) 245 | return ctx 246 | elseif ast.head == :generator 247 | reportASTError(ctx.ctx, ast, "Generator syntax doesn't support") 248 | elseif (ast.head == :block || 249 | ast.head == :tuple || 250 | ast.head == :quote || 251 | ast.head == :curly || 252 | ast.head == :return || 253 | ast.head == :(.) || 254 | ast.head == :ref || 255 | ast.head == :string || 256 | ast.head == :comprehension || 257 | ast.head == :comparison|| 258 | ast.head == :dotcall) 259 | for i in ast.args 260 | analyzeScopeVariable(ctx, i) 261 | end 262 | return ctx 263 | elseif ast.head == :macrocall 264 | # TODO : how we handle macro ??? 265 | # this is incorrect, we need to analyze macro 266 | return ctx 267 | elseif ast.head == :call 268 | for i in ast.args 269 | if i.head == :(=) 270 | analyzeScopeVariable(ctx, i.args[2]) 271 | elseif i.head == :parameters 272 | for ii in i.args 273 | analyzeScopeVariable(ctx, ii) 274 | end 275 | else 276 | analyzeScopeVariable(ctx, i) 277 | end 278 | end 279 | elseif (ast.head == :if || 280 | ast.head == :elseif || 281 | ast.head == :else) 282 | for i in ast.args 283 | analyzeScopeVariable(ctx, i) 284 | end 285 | elseif ast.head == :let 286 | enterScope(ctx, ast, ast.args[2]) 287 | elseif ast.head == :for 288 | enterScope(ctx, ast, ast.args[2]) 289 | elseif ast.head == :while 290 | cond = ast.args[1] 291 | body = ast.args[2] 292 | analyzeScopeVariable(ctx, cond) 293 | enterWhileScope(ctx, ast, body) 294 | else 295 | str = String(ast.head) 296 | if last(str) == '=' 297 | if length(ast.args) == 2 298 | analyzeScopeVariable(ctx, ast.args[2]) 299 | analyzeScopeVariableAssignLHS(ctx, ast.args[1], false, false) 300 | end 301 | end 302 | end 303 | return ctx 304 | end 305 | 306 | function analyzeScopeVariable(globalctx::GlobalContext, parent::FunDef)::ScopeInfoContext 307 | ctx = ScopeInfoContext(globalctx) 308 | ast = parent.ast 309 | info = ScopeInfo(ast) 310 | push!(ctx.chains, info) 311 | ctx.infos[ast] = info 312 | for i in parent.sparams 313 | info.locals[i.first] = ast 314 | push!(info.curlocals, i.first) 315 | end 316 | for i in parent.args 317 | info.locals[i.name] = ast 318 | push!(info.curlocals, i.name) 319 | end 320 | for i in parent.kwargs 321 | info.locals[i.name] = ast 322 | push!(info.curlocals, i.name) 323 | end 324 | for i in parent.optargs 325 | info.locals[i.name] = ast 326 | push!(info.curlocals, i.name) 327 | end 328 | # TODO : analyze optional argument here...!!! 329 | for i in 1:3 330 | ctx.walkTurn = i 331 | analyzeScopeVariable(ctx, ast.args[2]) 332 | end 333 | 334 | return ctx 335 | end 336 | -------------------------------------------------------------------------------- /src/adaptor/SyntaxAdaptor.jl: -------------------------------------------------------------------------------- 1 | # This file converts JuliaSyntax.GreenNode and JuliaSyntax.TreeNode to mutable structs 2 | # so that it's possible to establish pointer equality and convert the result to js object 3 | 4 | export parseJuAST 5 | 6 | import JuliaSyntax 7 | # Maybe is used only in this module, we don't expose it to other modules 8 | using ..SyntaxDefinition 9 | import ..SyntaxDefinition.formatLocation 10 | using ..Utility 11 | 12 | function attachNodeToJuAST(ast::JuAST, node::MutableGreenNode)::Nothing 13 | if !checkField(ast, :node) 14 | error("Already attached with a GreenNode") 15 | end 16 | ast.node = node 17 | return 18 | end 19 | 20 | struct GreenNodeConvertResult 21 | node::MutableGreenNode 22 | spanMapping::Dict{SpanInfo, Vector{MutableGreenNode}} 23 | end 24 | 25 | # Convert GreenNode to its mutable counterpart 26 | function convert2MutableGreenNode(ast::JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead}) 27 | pos = Base.RefValue{UInt32}(0) 28 | spanMapping = Dict{SpanInfo, Vector{MutableGreenNode}}() 29 | node = convert2MutableGreenNode!(spanMapping, pos, ast) 30 | return GreenNodeConvertResult(node, spanMapping) 31 | end 32 | 33 | # We identity every non-leaf GreenNode with its span 34 | function convert2MutableGreenNode!(spanMapping::Dict{SpanInfo, Vector{MutableGreenNode}}, 35 | pos::Base.RefValue{UInt32}, 36 | ast::JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead})::MutableGreenNode 37 | args = ast.args 38 | span = SpanInfo(pos[] + 1, pos[] + ast.span) 39 | if args isa Tuple{} 40 | node = MutableGreenNode(ast.head, span, Tuple{}(), nothing) 41 | pos[] = pos[] + ast.span 42 | else 43 | argnodes = Vector{MutableGreenNode}(undef, length(args)) 44 | for i in 1:length(argnodes) 45 | argnodes[i] = convert2MutableGreenNode!(spanMapping, pos, args[i]) 46 | end 47 | node = MutableGreenNode(ast.head, span, argnodes, nothing) 48 | end 49 | if haskey(spanMapping, span) 50 | push!(spanMapping[span], node) 51 | else 52 | spanMapping[span] = MutableGreenNode[node] 53 | end 54 | return node 55 | end 56 | 57 | @nocheck function getData(tree)::JuliaSyntax.SyntaxData 58 | data = tree.data 59 | if data isa Nothing 60 | error("JuliaSyntax.TreeNode has no associated green node") 61 | end 62 | return data 63 | end 64 | 65 | @nocheck function getNodeVal(tree)::JuASTVal 66 | makeConstJuASTVal(tree.data.val) 67 | end 68 | 69 | @nocheck function untokenize(head::JuliaSyntax.SyntaxHead) 70 | JuliaSyntax.untokenize(head;include_flag_suff=true) 71 | end 72 | 73 | # Convert TreeNode to JuAST 74 | # Ordering of JuAST and JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData} is different 75 | # For example, in infix-operator call, parameter order is swapped 76 | # maintain a mapping here 77 | function convert2JuAST!(tree::JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData}, 78 | parent::Maybe{JuAST})::JuAST 79 | data = getData(tree) 80 | start = UInt(data.position) 81 | span = SpanInfo(start, start + UInt(data.raw.span)-1) 82 | loc = Location(data.source, span) 83 | nchild_ = tree.children 84 | if nchild_ isa Nothing 85 | # this is a literal, like number, symbol or string 86 | v = getNodeVal(tree) 87 | # if the value is a symbol, then we represent it by an identifier instead of a symbol 88 | vv = v.val 89 | if vv isa Symbol 90 | ast = JuAST(:identifier, UInt16(0), JuAST[], v, loc, parent, tree) 91 | else 92 | ast = JuAST(:literal, UInt16(0), JuAST[], v, loc, parent, tree) 93 | end 94 | return ast 95 | end 96 | nchild = getChildren(tree) 97 | head = data.raw.head 98 | kind = Int64(reinterpret(Int16, head.kind)) 99 | flag = head.flags 100 | # TODO : we need to check the meaning of these flags here 101 | # Currently it works pretty fine for simple cases 102 | if (flag & ~JuliaSyntax.EMPTY_FLAGS & 103 | ~JuliaSyntax.INFIX_FLAG & 104 | ~(1<<5) & # MUTABLE_FLAG 105 | ~(1<<7) & # assignment 106 | ~(1<<6) & # string unescaped (unimportant for type inference) 107 | ~(1<<4) & 108 | # string macro 109 | ~JuliaSyntax.DOTOP_FLAG & # dotted op 110 | ~0x0040) != 0 # ! call 111 | println(tree, head) 112 | error("Bad flag : $(formatLocation(loc)) $(head.kind) : $flag $(untokenize(head))") 113 | end 114 | # TODO : we need to handle more strange syntax here... 115 | shead = Symbol(JuliaSyntax._kind_names[Int(kind)+1]) 116 | children = Vector{JuAST}(undef, length(nchild)) 117 | ast = JuAST(shead, flag, children, makeEmptyJuASTVal(), loc, parent, tree) 118 | for i in 1:length(nchild) 119 | children[i] = convert2JuAST!(nchild[i], Maybe{JuAST}(ast)) 120 | end 121 | # TODO : we need to handle some other cases there 122 | # basically we need to convert the order to Expr 123 | if (flag & JuliaSyntax.INFIX_FLAG !=0) && (shead == :call || shead == :dotcall) 124 | # swap the ordering of first children 125 | tmp = children[1] 126 | children[1] = children[2] 127 | children[2] = tmp 128 | end 129 | return ast 130 | end 131 | 132 | @nocheck function getChildren(tree) 133 | child = tree.children 134 | if child isa Nothing 135 | error("Mismatched shape : green node and ast should both have no children here") 136 | end 137 | return child 138 | end 139 | 140 | function fastCompareGreenNode(node::JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead}, mnode::MutableGreenNode)::Bool 141 | l = mnode.span.second - mnode.span.first + 1 142 | if node.span != l 143 | return false 144 | end 145 | if node.head != mnode.head 146 | return false 147 | end 148 | return true 149 | end 150 | 151 | function compareGreenNode(node::JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead}, mnode::MutableGreenNode)::Bool 152 | @warn "Slow Green Node comparing is used!" 153 | if !fastCompareGreenNode(node, mnode) 154 | return false 155 | end 156 | nchild = node.args 157 | mchild = mnode.args 158 | if nchild isa Tuple{} 159 | if mchild isa Tuple{} 160 | return true 161 | else 162 | return false 163 | end 164 | else 165 | if mchild isa Tuple{} 166 | return false 167 | else 168 | if length(nchild) != length(mchild) 169 | return false 170 | end 171 | for i in 1:length(nchild) 172 | if !compareGreenNode(nchild[i], mchild[i]) 173 | return false 174 | end 175 | end 176 | end 177 | return true 178 | end 179 | # const args::Union{Tuple{}, Vector{MutableGreenNodeInternal{T}}} 180 | end 181 | 182 | function matchGreenNode(ast::JuAST, nodes::Vector{MutableGreenNode})::MutableGreenNode 183 | if length(nodes) == 1 184 | return nodes[1] 185 | else 186 | candidate = MutableGreenNode[] 187 | data = getData(ast.tree) 188 | for i in nodes 189 | if fastCompareGreenNode(data.raw, i) 190 | push!(candidate, i) 191 | end 192 | end 193 | if length(candidate) == 1 194 | return candidate[1] 195 | elseif length(candidate) == 0 196 | # TODO : this is possible for try-catch without finally branch, where a literal false is generated and has no back pointer 197 | @debug "No extract mapping" 198 | return nodes[1] 199 | else 200 | num = 0 201 | index = -1 202 | for i in 1:length(candidate) 203 | if compareGreenNode(data.raw, candidate[i]) 204 | num = num + 1 205 | index = i 206 | end 207 | if num > 1 208 | error("More than one matching") 209 | end 210 | end 211 | if num == 0 212 | error("No exact matching") 213 | elseif num == 1 214 | return candidate[index] 215 | end 216 | end 217 | return candidate[1] 218 | end 219 | end 220 | 221 | function forwardPointer(spanMapping::Dict{SpanInfo, Vector{MutableGreenNode}}, 222 | ast::JuAST) 223 | span = ast.loc.span 224 | if haskey(spanMapping, span) 225 | node = matchGreenNode(ast, spanMapping[span]) 226 | ast.node = node 227 | node.backpointer = ast 228 | else 229 | error("AST has no matching green node") 230 | end 231 | for i in ast.args 232 | forwardPointer(spanMapping, i) 233 | end 234 | end 235 | 236 | @nocheck function checkField(ast::JuAST, field::Symbol)::Bool 237 | isdefined(ast, :node)::Bool 238 | end 239 | 240 | function validatePointer(ast::JuAST)::Bool 241 | span = ast.loc.span # some value is invalid, there is no need to map them 242 | if span.second < span.first 243 | return true 244 | end 245 | if !checkField(ast, :node) 246 | return false 247 | end 248 | bp = ast.node.backpointer 249 | if bp isa Nothing 250 | return false 251 | elseif bp != ast 252 | return false 253 | end 254 | for i in ast.args 255 | if !validatePointer(i) 256 | return false 257 | end 258 | end 259 | return true 260 | end 261 | 262 | function constructJuAST(tree::JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData})::JuAST 263 | data = getData(tree) 264 | ast = convert2JuAST!(tree, Maybe{JuAST}(nothing)) 265 | crel = convert2MutableGreenNode(data.raw) 266 | forwardPointer(crel.spanMapping, ast) 267 | if !validatePointer(ast) 268 | error("Internal implementation is incorrect") 269 | end 270 | return ast 271 | end 272 | 273 | @nocheck function parseJuAST(str::String, filename::String)::JuAST 274 | tree = JuliaSyntax.parseall(JuliaSyntax.SyntaxNode, str;filename) 275 | ast = constructJuAST(tree) 276 | return ast 277 | end 278 | -------------------------------------------------------------------------------- /src/adaptor/SyntaxDefinition.jl: -------------------------------------------------------------------------------- 1 | 2 | export SpanInfo, 3 | Location, 4 | JuAST, 5 | MutableGreenNode, 6 | JuASTVal, 7 | makeEmptyJuASTVal, 8 | makeConstJuASTVal 9 | 10 | import JuliaSyntax 11 | using ..Utility 12 | 13 | # An literal value in the AST tree 14 | struct JuASTVal 15 | # whether this represents a literal value 16 | isconst::Bool 17 | val::Any 18 | end 19 | 20 | # we use pair instead of tuple, because tuple is covariant 21 | # also, pair can be accessed by p.first, p.second 22 | const SpanInfo = Pair{UInt32, UInt32} 23 | 24 | struct Location 25 | file::JuliaSyntax.SourceFile 26 | span::SpanInfo 27 | end 28 | 29 | @nocheck function makeConstJuASTVal(val::Any) 30 | return JuASTVal(true, val) 31 | end 32 | 33 | function makeEmptyJuASTVal() 34 | return JuASTVal(false, nothing) 35 | end 36 | 37 | @nocheck function getNodeVal(tree::JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData})::JuASTVal 38 | makeConstJuASTVal(tree.data.val) 39 | end 40 | 41 | # Identical to JuliaSyntax.GreenNode{JuliaSyntax.SyntaxHead} 42 | # not every green node has associated JuAST, like trivia node 43 | mutable struct MutableGreenNodeInternal{T} 44 | const head::JuliaSyntax.SyntaxHead 45 | const span::SpanInfo 46 | const args::Union{Tuple{}, Vector{MutableGreenNodeInternal{T}}} 47 | backpointer::Union{Nothing, T} 48 | end 49 | 50 | # JuAST is similiar to Julia's Expr, except that it's associated with location information additionally 51 | # we use parameterized type to get rid of recursive type definition 52 | mutable struct JuAST 53 | # we should use JuliaSyntax.SyntaxHead here 54 | # unfortunately, that also requires us to use string macro K"=" for comparison 55 | # we instead use the old fashion symbol comparsion 56 | const head::Symbol 57 | const flag::UInt16 58 | const args::Vector{JuAST} 59 | const val::JuASTVal 60 | 61 | const loc::Location 62 | # Maybe use an uninitialized field here? 63 | const parent::Union{JuAST, Nothing} 64 | const tree::JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData} 65 | # this field is uninitialized and assigned later 66 | node::MutableGreenNodeInternal{JuAST} 67 | function JuAST(head::Symbol, flags::UInt16, args::Vector{JuAST}, val::JuASTVal, loc::Location, mparent::Maybe{JuAST}, tree::JuliaSyntax.TreeNode{JuliaSyntax.SyntaxData}) 68 | return new(head, flags, args, val, loc, mparent.val, tree) 69 | end 70 | end 71 | 72 | const MutableGreenNode = MutableGreenNodeInternal{JuAST} 73 | 74 | function convert2line(v::Vector{Int64}, i::Int)::Pair{Int, Int} 75 | @assert i >= v[1] "Invalid line" 76 | if i >= v[lastindex(v)] 77 | return lastindex(v) => i - lastindex(v) + 1 78 | else 79 | l = 1 80 | r = lastindex(v) 81 | while ((r-l) > 1) 82 | mid = div(l + r, 2) 83 | if v[mid] > i 84 | r = mid 85 | elseif v[mid] == i 86 | return mid => i - v[mid] + 1 87 | else 88 | l = mid 89 | end 90 | end 91 | return l => i - v[l] + 1 92 | end 93 | end 94 | 95 | function formatLocation(loc::Location)::String 96 | file = loc.file 97 | span = loc.span 98 | start = convert2line(file.line_starts, Int(span[1])) 99 | ff = file.filename 100 | if ff isa Nothing 101 | filename = "(none)" 102 | else 103 | filename = ff 104 | end 105 | return filename * ':' *string(start[1]) * ':'* string(start[2]) 106 | end 107 | 108 | @nocheck function Base.show(io::IO, ast::JuAST) 109 | println(io, formatLocation(ast.loc)) 110 | displayJuASTHelper(io, ast, 0) 111 | end 112 | 113 | @nocheck function displayJuASTHelper(io, ast::JuAST, id::Int) 114 | if ast.head == :literal 115 | s = ast.loc.span 116 | v = ast.val.val 117 | if v isa Symbol 118 | vstr = string(v) 119 | else 120 | vstr = Base.repr(v) 121 | end 122 | println(io, ' '^id, vstr, ' ', Int(s.first), ':', Int(s.second)) 123 | else 124 | s = ast.loc.span 125 | println(io, ' '^id, ast.head, ' ', Int(s.first), ':', Int(s.second)) 126 | for i in ast.args 127 | displayJuASTHelper(io, i, id + 1) 128 | end 129 | end 130 | return 131 | end 132 | -------------------------------------------------------------------------------- /src/adaptor/TreeQuery.jl: -------------------------------------------------------------------------------- 1 | export locateNode 2 | # Also used in JSAdaptor 3 | const NewlineWsKind = convert(JuliaSyntax.Kind, "NewlineWs") 4 | const WhitespaceKind = convert(JuliaSyntax.Kind, "Whitespace") 5 | const CommentKind = convert(JuliaSyntax.Kind, "Comment") 6 | 7 | function stringfy(x) 8 | return "$(Int(x.first)):$(Int(x.second))" 9 | end 10 | 11 | # This file provides a function such that given a span and a top-level ast, 12 | # we can narrow the range of span to a specific subtree in the ast 13 | function treeLookup(node::MutableGreenNode, targetSpan::SpanInfo)::Union{Nothing, MutableGreenNode} 14 | #println("Lookup $(stringfy(targetSpan)) in $(node.head), $(node.backpointer) $(stringfy(node.span))") 15 | span = node.span 16 | if (targetSpan.first > span.second || targetSpan.second < span.first) 17 | return nothing 18 | elseif targetSpan == span && !JuliaSyntax.is_trivia(node.head) 19 | return node 20 | else 21 | # inclusion 22 | if span.first <= targetSpan.first && targetSpan.second <= span.second 23 | args = node.args 24 | if JuliaSyntax.is_trivia(node.head) 25 | # a trivia, we alreadys give up on trivia 26 | return nothing 27 | elseif args isa Tuple{} 28 | # no children, stop here 29 | return node 30 | else 31 | for i in args 32 | rel = treeLookup(i, targetSpan) 33 | if rel isa Nothing 34 | continue 35 | else 36 | return rel 37 | end 38 | end 39 | end 40 | # inclusion, but not intersect with any element, a trivia 41 | # note, we need to handle trivia carefully 42 | return node 43 | else 44 | # partial intersection, shouldn't happend 45 | return nothing 46 | end 47 | end 48 | end 49 | 50 | function locateNode(node::MutableGreenNode, targetSpan::SpanInfo)::Union{Nothing, JuAST} 51 | val = treeLookup(node, targetSpan) 52 | if val isa Nothing 53 | return nothing 54 | end 55 | kind = val.head 56 | if kind == NewlineWsKind || kind == WhitespaceKind || kind == CommentKind 57 | # do nothing for these trivia 58 | return nothing 59 | else 60 | return val.backpointer 61 | end 62 | end -------------------------------------------------------------------------------- /src/adaptor/UtilityUnType.jl: -------------------------------------------------------------------------------- 1 | function hasField(ft::CompileType, p::Symbol)::Bool 2 | return hasfield(ft.typ, p) 3 | end 4 | 5 | function isConcreteType(ft::CompileType)::Bool 6 | tt = ft.typ 7 | return isconcretetype(tt) || tt <: Type 8 | end 9 | 10 | function getFieldType(ft::CompileType, p::Symbol)::CompileType 11 | tt = ft.typ 12 | if hasfield(tt, p) 13 | return makeType(fieldtype(tt, p)) 14 | else 15 | error("Internal error : getFieldType not checked") 16 | end 17 | end 18 | 19 | function getFromModule(mod::Core.Module, p::Symbol)::CompileType 20 | # todo handle typed global here 21 | if isdefined(mod, p) 22 | if isconst(mod, p) 23 | return makeConstVal(getproperty(mod, p)) 24 | else 25 | return makeType(Any) 26 | end 27 | else 28 | error("Internal error : getFromModule not checked") 29 | end 30 | end 31 | 32 | function isModuleDefined(mod::Core.Module, p::Symbol)::Bool 33 | return isdefined(mod, p)::Bool 34 | end 35 | 36 | 37 | 38 | function getReturnType(ctx::GlobalContext, sig)::Vector{CompileType} 39 | param1 = sig.parameters[1] 40 | if param1 <: Type 41 | getReturnType(ctx, sig, param1.parameters[1], Base.to_tuple_type(sig.parameters[2:end])) 42 | else 43 | getReturnType(ctx, sig, param1.instance, Base.to_tuple_type(sig.parameters[2:end])) 44 | end 45 | end 46 | 47 | function getReturnType(ctx::GlobalContext, f, tt)::Vector{CompileType} 48 | getReturnType(ctx, Tuple{Core.Typeof(f), tt...}, f, tt) 49 | end 50 | 51 | function getReturnType(ctx::GlobalContext, sig, f, types;world = Core.Compiler.get_world_counter())::Vector{CompileType} 52 | interp = Core.Compiler.NativeInterpreter(world; inf_params = Core.Compiler.InferenceParams()) 53 | if ccall(:jl_is_in_pure_context, Bool, ()) 54 | error("code reflection cannot be used from generated functions") 55 | end 56 | if isa(f, Core.OpaqueClosure) 57 | error("InternalError : OpaqueClosure is unsupported") 58 | end 59 | if isa(f, Core.Builtin) 60 | argtypes = Any[Base.to_tuple_type(types).parameters...] 61 | rt = Core.Compiler.builtin_tfunction(interp, f, argtypes, nothing) 62 | rt = Core.Compiler.ignorelimited(rt) 63 | if rt isa Core.Compiler.Const 64 | return CompileType[makeConstVal(rt.val)] 65 | else 66 | return CompileType[makeType(Core.Compiler.widenconst(rt))] 67 | end 68 | end 69 | rts = CompileType[] 70 | for match in Base._methods_by_ftype(sig, -1, world)::Vector 71 | match = match::Core.MethodMatch 72 | method = Base.func_for_method_checked(match.method, types, match.sparams) 73 | mi = Core.Compiler.specialize_method(method, match.spec_types, match.sparams)::Core.Compiler.MethodInstance 74 | result = Core.Compiler.InferenceResult(mi) 75 | Core.Compiler.typeinf(interp, result, :global) 76 | if result.result isa Core.Compiler.InferenceState 77 | push!(rts, makeType(Any)) 78 | else 79 | ty = Core.Compiler.ignorelimited(result.result) 80 | if ty isa Core.Compiler.Const 81 | push!(rts, makeConstVal(ty.val)) 82 | else 83 | push!(rts, makeType(Core.Compiler.widenconst(ty))) 84 | end 85 | end 86 | end 87 | if hasfield(Core.Compiler.NativeInterpreter, :cache) 88 | cache = interp.cache 89 | else 90 | cache = interp.inf_cache 91 | end 92 | for i in cache 93 | push!(ctx.queue, KwFunCall(i.linfo)) 94 | end 95 | return rts 96 | end 97 | 98 | function getKeywordMethodInstances(ctx::GlobalContext, sig)::Vector{Core.MethodInstance} 99 | # f(x...;kwargs...) => we lookup method for 100 | #= 101 | if isa(f, Core.Builtin) 102 | error("Builtin function should never be called with kewyord") 103 | end 104 | =# 105 | types = Base.to_tuple_type(sig.parameters[2:end]) 106 | results = Vector{Core.MethodInstance}() 107 | for match in Base._methods_by_ftype(sig, -1, Core.Compiler.get_world_counter())::Vector 108 | match = match::Core.MethodMatch 109 | method = Base.func_for_method_checked(match.method, types, match.sparams) 110 | mi = Core.Compiler.specialize_method(method, match.spec_types, match.sparams)::Core.Compiler.MethodInstance 111 | push!(results, mi) 112 | end 113 | return results 114 | end 115 | 116 | function cacheLookup(eng::Engine, sig)::Vector{CompileType} 117 | ctx = eng.globalCtx 118 | if haskey(ctx.cache, sig) 119 | return ctx.cache[sig] 120 | else 121 | # TODO : the major slowdown source 122 | # we should use result from Julia's builtin compiler 123 | v = getReturnType(eng.globalCtx, sig) 124 | ctx.cache[sig] = v 125 | return v 126 | end 127 | end 128 | 129 | @nocheck function makeNameTupleType(args::Vector{Pair{Symbol, CompileType}})::CompileType 130 | return makeType(NamedTuple{tuple([i.first for i in args]...), Tuple{[i.second.typ for i in args]...}}) 131 | end 132 | 133 | function getMethodMatches(eng::Engine, ms::MethodCallStruct)::Vector{CompileType} 134 | args = ms.fargs 135 | kwargs = ms.kwargs 136 | sig = Tuple{[i.typ.typ for i in args]...} 137 | if length(kwargs) > 0 138 | mis = getKeywordMethodInstances(eng.globalCtx, sig) 139 | if length(mis) == 0 140 | error("No method instance for keyword call") 141 | end 142 | kwargs_ = Vector{Pair{Symbol, CompileType}}() 143 | for i in mis 144 | for (k, node) in kwargs 145 | push!(kwargs_, k=>removeConst(node.typ)) 146 | end 147 | push!(eng.globalCtx.queue, KwFunCall(i, kwargs_)) 148 | end 149 | tup = NamedTuple{tuple([i.first for i in kwargs]...), Tuple{[i.second.typ.typ for i in kwargs]...}} 150 | sig = Tuple{typeof(Core.kwcall), tup, sig.parameters...} 151 | end 152 | result = cacheLookup(eng, sig) 153 | return result 154 | end 155 | 156 | function extractUniqueMatch(v)::CompileType 157 | v[1] 158 | end 159 | 160 | function checkFieldCompatibility(dest::CompileType, src::CompileType)::Bool 161 | return src.typ <: dest.typ 162 | end 163 | 164 | function isConstructor(v::CompileType)::Bool 165 | return isConstVal(v) && v.val isa Type 166 | end 167 | 168 | function tryApplyType(args::Vector{CompileType})::Maybe{CompileType} 169 | f = args[1].val 170 | try 171 | tt = Core.apply_type(f, (args[i].val for i in 2:length(args))...) 172 | return Just(makeConstVal(tt)) 173 | catch e 174 | return None(CompileType) 175 | end 176 | end 177 | 178 | #= 179 | Untility for merging flow type 180 | =# 181 | function tryMergeFlowType(v1::FlowNode, v2::FlowNode) 182 | v1typ = v1.typ 183 | v2typ = v2.typ 184 | if v2typ.typ <: v1typ.typ 185 | return true 186 | else 187 | return false 188 | end 189 | end 190 | 191 | function tryMergeCompileValue(v1::CompileType, v2::CompileType)::Bool 192 | return v2.typ <: v1.typ 193 | end 194 | 195 | function tryMergeReturnFlowNode(v1::FlowNode, v2::FlowNode)::Bool 196 | return tryMergeFlowType(v1, v2) 197 | end 198 | 199 | function collectUnion(x)::Vector{Any} 200 | if x isa Union 201 | return vcat(collectUnion(x.a), collectUnion(x.b)) 202 | else 203 | return Any[x] 204 | end 205 | end 206 | 207 | function splitUnion(x::CompileType, y::CompileType)::CompileType 208 | # note, we use the type of left value and the value of right type 209 | u1 = collectUnion(x.typ) 210 | u2 = collectUnion(y.typ) 211 | makeType(Union{setdiff(u1, u2)...}) 212 | end 213 | 214 | function isaType(x::CompileType)::Bool 215 | x.typ isa Type 216 | end 217 | 218 | function isaAny(x::CompileType)::Bool 219 | x.typ == Any 220 | end 221 | 222 | function isaBool(x::CompileType)::Bool 223 | x.typ == Bool 224 | end 225 | 226 | function isaUnion(x::CompileType)::Bool 227 | x.typ isa Union 228 | end 229 | 230 | function isaTuple(x::CompileType)::Bool 231 | x.typ <: Tuple 232 | end 233 | 234 | function getFirstParameter(x::CompileType)::CompileType 235 | return makeType(x.typ.parameters[1]) 236 | end 237 | 238 | function tryMergeConstVal(v1::CompileType, v2::CompileType)::Bool 239 | if isConstVal(v1) && isConstVal(v2) 240 | return v1.val === v2.val 241 | else 242 | return false 243 | end 244 | end 245 | #= 246 | function tryMergeVarDefFlowNode(eng::Engine, ast::JuAST, v::Vector{FlowNode}, allowUnion::Bool)::FlowNode 247 | # println("join node $([i.typ for i in v])") 248 | tmptyp = v[1].typ 249 | for i in v 250 | if !isBottomType(i.typ) 251 | tmptyp = i.typ 252 | break 253 | end 254 | end 255 | if isBottomType(tmptyp) 256 | # TODO : shouldn't be here 257 | return makePhiFlowNode(ast, v, makeBottomType()) 258 | end 259 | allConst = true 260 | for i in 1:length(v) 261 | # skip all bottom value 262 | if isBottomType(v[i].typ) 263 | continue 264 | end 265 | v1 = tmptyp.typ 266 | v2 = v[i].typ.typ 267 | if !allowUnion 268 | if v1 != v2 269 | reportErrorIfEnlargeType(eng, v[1], v[i]) 270 | end 271 | end 272 | # Note, if we allow union, then type may not equal 273 | if allConst && isConstVal(tmptyp) && isConstVal(v[i].typ) 274 | allConst = tryMergeConstVal(tmptyp, v[i].typ) 275 | else 276 | allConst = false 277 | end 278 | if !allConst 279 | tmptyp = makeType(Union{v1, v2}) 280 | end 281 | end 282 | return makePhiFlowNode(ast, v, tmptyp) 283 | end 284 | =# 285 | 286 | function tryMergeFlowNode(eng::Engine, ast::JuAST, v::Vector{FlowNode}, allowUnion::Bool)::FlowNode 287 | # println("join node $([i.typ for i in v])") 288 | tmptyp = v[1].typ 289 | for i in v 290 | if !isBottomType(i.typ) 291 | tmptyp = i.typ 292 | break 293 | end 294 | end 295 | if isBottomType(tmptyp) 296 | # TODO : shouldn't be here 297 | return makePhiFlowNode(ast, v, makeBottomType()) 298 | end 299 | allConst = true 300 | for i in 1:length(v) 301 | # skip all bottom value 302 | if isBottomType(v[i].typ) 303 | continue 304 | end 305 | v1 = tmptyp.typ 306 | v2 = v[i].typ.typ 307 | if !allowUnion 308 | if v1 != v2 309 | reportErrorIfEnlargeType(eng, v[1], v[i]) 310 | end 311 | end 312 | # Note, if we allow union, then type may not equal 313 | if allConst && isConstVal(tmptyp) && isConstVal(v[i].typ) 314 | allConst = tryMergeConstVal(tmptyp, v[i].typ) 315 | else 316 | allConst = false 317 | end 318 | if !allConst 319 | tmptyp = makeType(Union{v1, v2}) 320 | end 321 | end 322 | return makePhiFlowNode(ast, v, tmptyp) 323 | end 324 | 325 | @nocheck function makeTupleType(tt::Vector{FlowNode})::CompileType 326 | makeType(Tuple{[i.typ.typ for i in tt]...}) 327 | end 328 | -------------------------------------------------------------------------------- /src/adaptor/datastruct.jl: -------------------------------------------------------------------------------- 1 | export ImmutableDict, update, exist, fetch 2 | 3 | struct ImmutableDict{K,V} 4 | data::Dict{K, V} 5 | end 6 | 7 | function ImmutableDict{K,V}() where {K,V} 8 | return ImmutableDict{K,V}(Dict{K, V}()) 9 | end 10 | 11 | function shallow_copy(d::ImmutableDict{K,V})::ImmutableDict{K,V} where {K,V} 12 | return ImmutableDict{K,V}(copy(d.data)) 13 | end 14 | 15 | function update(d::ImmutableDict{K,V}, k::K, v::V)::ImmutableDict{K,V} where {K,V} 16 | newd = shallow_copy(d) 17 | newd.data[k] = v; 18 | return newd 19 | end 20 | 21 | function exist(d::ImmutableDict{K,V}, k::K)::Bool where {K,V} 22 | return Base.haskey(d.data, k) 23 | end 24 | 25 | function fetch(d::ImmutableDict{K,V}, k::K)::V where {K,V} 26 | if exist(d, k) 27 | return d.data[k] 28 | else 29 | throw(Base.KeyError(k)) 30 | end 31 | end 32 | 33 | -------------------------------------------------------------------------------- /src/adaptor/precompile.jl: -------------------------------------------------------------------------------- 1 | precompile(SimpleTypeChecker.SyntaxAdaptor.parseJuAST, (String, String)) 2 | precompile(SimpleTypeChecker.Inference.inferExpr, (SimpleTypeChecker.Inference.Engine, SimpleTypeChecker.Inference.Context, SimpleTypeChecker.Inference.JuAST)) 3 | precompile(SimpleTypeChecker.Inference.runCheck!, (SimpleTypeChecker.Inference.GlobalContext,)) 4 | precompile(SimpleTypeChecker.Inference.addFile!, (SimpleTypeChecker.Inference.GlobalContext, Core.Module, String)) 5 | precompile(SimpleTypeChecker.Inference.analyzeScopeVariable, (SimpleTypeChecker.Inference.GlobalContext, SimpleTypeChecker.Inference.FunDef)) 6 | precompile(SimpleTypeChecker.Inference.checkToplevelFunction, (SimpleTypeChecker.Inference.Engine, SimpleTypeChecker.Inference.FunDef, Core.MethodInstance)) -------------------------------------------------------------------------------- /src/adaptor/toplevel.jl: -------------------------------------------------------------------------------- 1 | module Utility 2 | include("utility.jl") 3 | include("datastruct.jl") 4 | end 5 | 6 | module SyntaxDefinition 7 | include("SyntaxDefinition.jl") 8 | end 9 | 10 | module SyntaxAdaptor 11 | include("SyntaxAdaptor.jl") 12 | include("JSAdaptor.jl") 13 | include("TreeQuery.jl") 14 | end 15 | 16 | module Inference 17 | include("InferenceDefinition.jl") 18 | include("ScopeChecking.jl") 19 | include("InferenceErrorUtility.jl") 20 | include("InferenceError.jl") 21 | include("UtilityUnType.jl") 22 | include("Inference.jl") 23 | end 24 | 25 | module Server 26 | include("../server/SimpleHTTPServer.jl") 27 | end 28 | 29 | module API 30 | include("API.jl") 31 | end 32 | include("precompile.jl") -------------------------------------------------------------------------------- /src/adaptor/type.jl: -------------------------------------------------------------------------------- 1 | # A wrapper for any Julia value 2 | struct JuVal 3 | val::Any 4 | isPoison::Bool 5 | isConst::Bool 6 | end 7 | 8 | isPoisonVal(v::JuVal) = v.isPoison 9 | isConstVal(v::JuVal) = v.isConst 10 | @inline makeConstVal(v::Any) = JuVal(v, false, true) 11 | @inline makeNonConstVal() = JuVal(nothing, false, false) 12 | @inline makePoisonVal() = JuVal(nothing, true, false) 13 | 14 | # A wrapper for any Julia type 15 | struct JuType 16 | val::Any 17 | isPoison::Bool 18 | end 19 | isPoisonType(v::JuType) = v.isPoison 20 | @inline makeJuType(v::Any) = JuType(v, false) 21 | @inline makePoisonType() = JuType(nothing, true) 22 | 23 | # A wrapper for any Julia value 24 | struct JuExpr 25 | val::Any 26 | end 27 | 28 | # AST construction 29 | const TypedAST = JuExpr 30 | 31 | struct Var 32 | ast::JuAST 33 | id::Symbol 34 | end 35 | 36 | struct Assign 37 | ast::JuAST 38 | lhs::Var 39 | rhs::TypedAST 40 | end 41 | 42 | struct TypedAssign 43 | ast::JuAST 44 | lhs::Var 45 | typ::TypedAST 46 | rhs::TypedAST 47 | end 48 | 49 | struct FunCall 50 | ast::JuAST 51 | f::TypedAST 52 | args::Vector{TypedAST} 53 | kwargs::Vector{TypedAST} 54 | end 55 | 56 | struct CurlyCall 57 | ast::JuAST 58 | f::TypedAST 59 | args::Vector{TypedAST} 60 | end 61 | 62 | struct Literal 63 | ast::JuAST 64 | val::JuVal 65 | end 66 | 67 | struct Block 68 | ast::JuAST 69 | stmts::Vector{TypedAST} 70 | end 71 | 72 | struct IfStmt 73 | ast::JuAST 74 | branches::Vector{Tuple{TypedAST, TypedAST}} 75 | else_::Union{TypedAST, Nothing} 76 | end 77 | 78 | struct WhileStmt 79 | ast::JuAST 80 | cond::TypedAST 81 | body::TypedAST 82 | end 83 | 84 | struct ForStmt 85 | ast::JuAST 86 | var::Var 87 | iter::TypedAST 88 | body::TypedAST 89 | end 90 | 91 | struct Return 92 | ast::JuAST 93 | e::Union{TypedAST, Nothing} 94 | end 95 | 96 | struct GetProperty 97 | ast::JuAST 98 | x::TypedAST 99 | p::Symbol 100 | end 101 | 102 | struct SetProperty 103 | ast::JuAST 104 | x::TypedAST 105 | p::Symbol 106 | v::TypedAST 107 | end 108 | 109 | struct ArrayRef 110 | ast::JuAST 111 | arr::TypedAST 112 | i::Vector{TypedAST} 113 | end 114 | 115 | struct ArraySet 116 | ast::JuAST 117 | arr::TypedAST 118 | i::Vector{TypedAST} 119 | v::TypedAST 120 | end 121 | 122 | struct TypedAssert 123 | ast::JuAST 124 | lhs::TypedAST 125 | rhs::TypedAST 126 | end 127 | 128 | struct FunDef 129 | ast::JuAST 130 | f::Symbol 131 | args::Vector{Pair{Symbol, Union{TypedAST, Nothing}}} 132 | kwargs::Vector{Pair{Symbol, Union{TypedAST, Nothing}}} 133 | params::Vector{Symbol} 134 | rt::Union{Nothing, TypedAST} 135 | body::TypedAST 136 | end 137 | 138 | struct ModDef 139 | ast::JuAST 140 | name::Symbol 141 | stmts::JuExpr 142 | end 143 | 144 | function getLiteralVal(l::Literal)::JuVal 145 | return l.val 146 | end 147 | 148 | # End of AST construction 149 | 150 | # Flow node, used to map type and ast 151 | @enum FlowNodeKind begin 152 | LiteralFlowNode 153 | AssignFlowNode 154 | EmptyBlockFlowNode 155 | EmptyElseFlowNode 156 | BlockFlowNode 157 | VarFlowNode 158 | GlobalVarFlowNode 159 | FunCallNode 160 | CurlyCallNode 161 | GetPropertyNode 162 | SetPropertyNode 163 | ArrayRefNode 164 | ArraySetNode 165 | 166 | ReturnNode 167 | EmptyReturnNode 168 | ExpectedReturnNode 169 | ParamNode 170 | SparamNode 171 | 172 | ForEndNode 173 | ForVarNode 174 | ForUpdateNode # variable that loses constantness in the loop 175 | 176 | IfFlowNode # PhiNode actually 177 | ConditionalFlowNode # variable that defines conditionally 178 | PiNode 179 | NegPiNode 180 | end 181 | 182 | mutable struct FlowNode 183 | const ast::JuAST 184 | const nodeKind::FlowNodeKind 185 | const source::Vector{FlowNode} 186 | const val::JuVal # constant value 187 | const typ::JuType 188 | end 189 | 190 | function makeLiteralFlowNode(ast::Literal, val::JuVal) 191 | FlowNode(ast.ast, LiteralFlowNode, FlowNode[], val, makeJuType(Core.Typeof(val.val))) 192 | end 193 | 194 | function makeBlockFlowNode(ast::Block, last::FlowNode) 195 | FlowNode(ast.ast, BlockFlowNode, FlowNode[], last.val, last.typ) 196 | end 197 | 198 | function makeEmptyBlockFlowNode(ast::Block) 199 | FlowNode(ast.ast, EmptyBlockFlowNode, FlowNode[], makeConstVal(nothing), makeJuType(Nothing)) 200 | end 201 | 202 | function makeAssignFlowNode(ast::Assign, rhs::FlowNode) 203 | FlowNode(ast.ast, AssignFlowNode, FlowNode[rhs], rhs.val, rhs.typ) 204 | end 205 | 206 | function makeVarFlowNode(ast::Var, ref::FlowNode) 207 | FlowNode(ast.ast, VarFlowNode, FlowNode[ref], ref.val, ref.typ) 208 | end 209 | 210 | function makeGlobalVarFlowNode(ast::Var, val::JuVal) 211 | FlowNode(ast.ast, GlobalVarFlowNode, FlowNode[], val, makeJuType(Core.Typeof(val.val))) 212 | end 213 | 214 | function makeFunCallFlowNode(ast::FunCall, args::Vector{FlowNode}, val::JuVal, typ::JuType) 215 | FlowNode(ast.ast, FunCallNode, args, val, typ) 216 | end 217 | 218 | function makeCurlyCallFlowNode(ast::CurlyCall, args::Vector{FlowNode}, val::JuVal, typ::JuType) 219 | FlowNode(ast.ast, CurlyCallNode, args, val, typ) 220 | end 221 | 222 | function makeReturnFlowNode(ast::Return, e::FlowNode) 223 | FlowNode(ast.ast, ReturnNode, FlowNode[e], e.val, e.typ) 224 | end 225 | 226 | function makeEmptyReturnFlowNode(ast::Return) 227 | FlowNode(ast.ast, EmptyReturnNode, FlowNode[], makeConstVal(nothing), makeJuType(Nothing)) 228 | end 229 | 230 | 231 | struct ContextValue 232 | typ::FlowNode # primary type of this context variable 233 | curtyp::FlowNode # inferred type on this path 234 | end 235 | 236 | # Context for variable binding and other useful things, immutable 237 | struct Context 238 | mapping::ImmutableDict{Symbol, ContextValue} 239 | end 240 | 241 | mutable struct Engine 242 | const mod::Core.Module 243 | # TODO : add a logger here! 244 | retVal::Union{Nothing, FlowNode} 245 | const errio::IO 246 | const debugio::IO 247 | end 248 | 249 | function Engine(mod::Core.Module) 250 | return Engine(mod, nothing, stdout, stdout) 251 | end 252 | 253 | function Context() 254 | return Context(ImmutableDict{Symbol, ContextValue}()) 255 | end 256 | 257 | function hasvar(ctx::Context, var::Symbol)::Bool 258 | return exist(ctx.mapping, var) 259 | end 260 | 261 | function lookup(ctx::Context, var::Symbol)::ContextValue 262 | return ctx.mapping.data[var] 263 | end 264 | 265 | function update(ctx::Context, var::Symbol, val::ContextValue)::Context 266 | return Context(update(ctx.mapping, var, val)) 267 | end 268 | 269 | # Inference result for one AST 270 | struct InferResult 271 | ctx::Context 272 | node::FlowNode 273 | end 274 | 275 | struct InferReport 276 | f::FunDef 277 | tt::Any 278 | eng::Engine 279 | rel::InferResult 280 | end -------------------------------------------------------------------------------- /src/adaptor/utility.jl: -------------------------------------------------------------------------------- 1 | macro nocheck(e) 2 | return esc(e) 3 | end 4 | 5 | struct Maybe{T} 6 | val::Union{Nothing, T} 7 | end 8 | 9 | function None(val::Type{T}) where T 10 | return Maybe{T}(nothing) 11 | end 12 | function Just(x::T) where T 13 | return Maybe{T}(x) 14 | end 15 | 16 | function isNone(x::Maybe{T})::Bool where T 17 | return x.val isa Nothing 18 | end 19 | 20 | function castJust(x::Maybe{T})::T where T 21 | val = x.val 22 | if val isa Nothing 23 | error("Not a Just") 24 | else 25 | return val 26 | end 27 | end 28 | export @nocheck, Maybe, None, Just, isNone, castJust -------------------------------------------------------------------------------- /src/server/SimpleHTTPServer.jl: -------------------------------------------------------------------------------- 1 | import ..SyntaxAdaptor: locateNode 2 | using ..SyntaxDefinition 3 | import ..Utility: @nocheck 4 | import ..Inference: FlowNode 5 | # TODO : remove this in the future 6 | # a really simple HTTP server 7 | # no TLS, and only support HTTP 1.0 8 | using Sockets 9 | 10 | struct ParseError <: Exception 11 | msg::String 12 | end 13 | 14 | function parseHTTPBody(s::String) 15 | m = findfirst("\r\n", s) 16 | if m isa Nothing 17 | Base.throw(ParseError("Failed to parse method from HTTP request")) 18 | end 19 | method = s[1:first(m)-1] 20 | body = findfirst("\r\n\r\n", s) 21 | if body isa Nothing 22 | Base.throw(ParseError("Failed to parse HTTP body from HTTP request")) 23 | end 24 | bodystr = s[last(body)+1:end] 25 | return method => bodystr 26 | end 27 | 28 | function wrapResponseHeader(str::String) 29 | header = """ 30 | HTTP/1.1 200 OK 31 | Content-Type: text/plain ;charset=utf-8 32 | Access-Control-Allow-Origin: * 33 | Connection: Closed 34 | Content-Length: $(sizeof(str)) 35 | 36 | """ 37 | return replace(header, '\n'=> "\r\n") * str 38 | end 39 | 40 | struct QueryFunctor 41 | f::Function 42 | ast::JuAST 43 | flowMapping::Dict{JuAST, Vector{FlowNode}} 44 | end 45 | 46 | @nocheck function query(f::QueryFunctor, method::String, body::String)::String 47 | greennode = f.ast.node 48 | params = split(body, ' ') 49 | if params[1] == "locateNode" 50 | node = locateNode(greennode, SpanInfo(Base.parse(Int, params[2]), Base.parse(Int, params[3]))) 51 | if node isa Nothing 52 | return "nothing" 53 | else 54 | if haskey(f.flowMapping, node) 55 | flownode = f.flowMapping[node] 56 | else 57 | flownode = nothing 58 | end 59 | f.f(node, flownode) 60 | end 61 | else 62 | return "Unsupported call" 63 | end 64 | end 65 | 66 | @nocheck function handleQuery(f::QueryFunctor) 67 | println("Launching LSP server") 68 | s = listen(Sockets.IPv4("127.0.0.1"), 3467) 69 | # process message serially 70 | # maybe need to fix this 71 | try 72 | while true 73 | tcpsocket = accept(s) 74 | println(" Receving a request") 75 | meg = readavailable(tcpsocket) 76 | try 77 | p = parseHTTPBody(String(meg)) 78 | method = p.first 79 | body = p.second 80 | value = query(f, method, body) 81 | resp = wrapResponseHeader(value) 82 | write(tcpsocket, resp) 83 | catch e 84 | if e isa ParseError 85 | println(" Failed to parse the request") 86 | else 87 | # capture by outside try-catch 88 | rethrow(e) 89 | end 90 | finally 91 | close(tcpsocket) 92 | end 93 | println(" End processing the request\n") 94 | end 95 | catch e 96 | if e isa InterruptException 97 | println("Closing the server, exit normally") 98 | else 99 | println("Closing the server, exit abnormally") 100 | rethrow(e) 101 | end 102 | finally 103 | close(s) 104 | end 105 | end 106 | -------------------------------------------------------------------------------- /src/www/class.js: -------------------------------------------------------------------------------- 1 | class SyntaxHead { 2 | kind; 3 | flag; 4 | constructor(kind, flag) { 5 | this.kind = kind; 6 | this.flag = flag; 7 | } 8 | } 9 | class ASTNode { 10 | head; 11 | parent; 12 | children; 13 | start; 14 | last; 15 | constructor(head, parent, start, last) { 16 | this.head = head; 17 | this.children = []; 18 | this.parent = parent; 19 | if (parent != null) { 20 | parent.children.push(this); 21 | } 22 | this.start = start; 23 | this.last = last; 24 | } 25 | selectAST(begin, end) { 26 | // not intersect 27 | if (begin > this.last || end < this.start) { 28 | return null; 29 | } 30 | if (this.start == begin && this.last == end) { 31 | return this; 32 | } 33 | // inclusion 34 | if (this.start <= begin && this.last >= end) { 35 | for (var i = 0; i < this.children.length; i++) { 36 | var rel = this.children[i].selectAST(begin, end); 37 | if (rel === null) { 38 | continue; 39 | } else { 40 | return rel; 41 | } 42 | } 43 | // inclusion, but not intersect with any element, a trivia 44 | return this; 45 | } 46 | // other case - partial insection 47 | return null; 48 | } 49 | } 50 | 51 | const onMouseHover = function () { 52 | start = this.getAttribute("start"); 53 | if (start == null) { 54 | return; 55 | } 56 | last = this.getAttribute("last"); 57 | if (last == null) { 58 | return; 59 | } 60 | var start = parseInt(start); 61 | var last = parseInt(last); 62 | //var rel = ast1.selectAST(start, last); 63 | //if (rel === null){ 64 | // return null; 65 | //} 66 | console.log(start); 67 | console.log(start); 68 | makeRequest(this, "locateNode " + start.toString() + " " + last.toString()); 69 | }; 70 | var codes = document.getElementsByClassName("code-text")[0].children; 71 | for (var i = 0; i < codes.length; i++) { 72 | var elem = codes[i]; 73 | elem.onmouseover = onMouseHover; 74 | } 75 | 76 | const TypeHintPopperElement = document.querySelector("#tooltip"); 77 | const TypeHintPopperSpanElement = document.querySelector("#tooltip-content"); 78 | function makeRequest(parent, str) { 79 | xhttp = new XMLHttpRequest(); 80 | xhttp.open("POST", "http://127.0.0.1:3467"); 81 | xhttp.onload = function () { 82 | console.log(this.responseText); 83 | TypeHintPopperSpanElement.innerHTML = this.responseText; 84 | Popper.createPopper(parent, TypeHintPopperElement, {placement : "bottom-end"}); 85 | }; 86 | xhttp.send(str); 87 | } 88 | -------------------------------------------------------------------------------- /src/www/data.js: -------------------------------------------------------------------------------- 1 | var ast1 = new ASTNode(new SyntaxHead("toplevel", 0),null, 1, 361); 2 | var ast2 = new ASTNode(new SyntaxHead("block", 0),ast1, 1, 360); 3 | var ast3 = new ASTNode(new SyntaxHead("begin", 1),ast2, 1, 5); 4 | var ast4 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 6, 10); 5 | var ast5 = new ASTNode(new SyntaxHead("=", 128),ast2, 11, 15); 6 | var ast6 = new ASTNode(new SyntaxHead("Identifier", 0),ast5, 11, 11); 7 | var ast7 = new ASTNode(new SyntaxHead("Whitespace", 1),ast5, 12, 12); 8 | var ast8 = new ASTNode(new SyntaxHead("=", 1),ast5, 13, 13); 9 | var ast9 = new ASTNode(new SyntaxHead("Whitespace", 1),ast5, 14, 14); 10 | var ast10 = new ASTNode(new SyntaxHead("Integer", 0),ast5, 15, 15); 11 | ast5.args = [ast6,ast7,ast8,ast9,ast10]; 12 | var ast11 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 16, 20); 13 | var ast12 = new ASTNode(new SyntaxHead("=", 128),ast2, 21, 25); 14 | var ast13 = new ASTNode(new SyntaxHead("Identifier", 0),ast12, 21, 21); 15 | var ast14 = new ASTNode(new SyntaxHead("Whitespace", 1),ast12, 22, 22); 16 | var ast15 = new ASTNode(new SyntaxHead("=", 1),ast12, 23, 23); 17 | var ast16 = new ASTNode(new SyntaxHead("Whitespace", 1),ast12, 24, 24); 18 | var ast17 = new ASTNode(new SyntaxHead("Integer", 0),ast12, 25, 25); 19 | ast12.args = [ast13,ast14,ast15,ast16,ast17]; 20 | var ast18 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 26, 30); 21 | var ast19 = new ASTNode(new SyntaxHead("=", 128),ast2, 31, 40); 22 | var ast20 = new ASTNode(new SyntaxHead("Identifier", 0),ast19, 31, 31); 23 | var ast21 = new ASTNode(new SyntaxHead("Whitespace", 1),ast19, 32, 32); 24 | var ast22 = new ASTNode(new SyntaxHead("=", 1),ast19, 33, 33); 25 | var ast23 = new ASTNode(new SyntaxHead("Whitespace", 1),ast19, 34, 34); 26 | var ast24 = new ASTNode(new SyntaxHead("tuple", 0),ast19, 35, 40); 27 | var ast25 = new ASTNode(new SyntaxHead("(", 1),ast24, 35, 35); 28 | var ast26 = new ASTNode(new SyntaxHead("Identifier", 0),ast24, 36, 36); 29 | var ast27 = new ASTNode(new SyntaxHead(",", 1),ast24, 37, 37); 30 | var ast28 = new ASTNode(new SyntaxHead("Whitespace", 1),ast24, 38, 38); 31 | var ast29 = new ASTNode(new SyntaxHead("Identifier", 0),ast24, 39, 39); 32 | var ast30 = new ASTNode(new SyntaxHead(")", 1),ast24, 40, 40); 33 | ast24.args = [ast25,ast26,ast27,ast28,ast29,ast30]; 34 | ast19.args = [ast20,ast21,ast22,ast23,ast24]; 35 | var ast31 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 41, 45); 36 | var ast32 = new ASTNode(new SyntaxHead("+=", 128),ast2, 46, 51); 37 | var ast33 = new ASTNode(new SyntaxHead("Identifier", 0),ast32, 46, 46); 38 | var ast34 = new ASTNode(new SyntaxHead("Whitespace", 1),ast32, 47, 47); 39 | var ast35 = new ASTNode(new SyntaxHead("+=", 1),ast32, 48, 49); 40 | var ast36 = new ASTNode(new SyntaxHead("Whitespace", 1),ast32, 50, 50); 41 | var ast37 = new ASTNode(new SyntaxHead("Integer", 0),ast32, 51, 51); 42 | ast32.args = [ast33,ast34,ast35,ast36,ast37]; 43 | var ast38 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 52, 56); 44 | var ast39 = new ASTNode(new SyntaxHead("=", 128),ast2, 57, 63); 45 | var ast40 = new ASTNode(new SyntaxHead("Identifier", 0),ast39, 57, 57); 46 | var ast41 = new ASTNode(new SyntaxHead("Whitespace", 1),ast39, 58, 58); 47 | var ast42 = new ASTNode(new SyntaxHead("=", 1),ast39, 59, 59); 48 | var ast43 = new ASTNode(new SyntaxHead("Whitespace", 1),ast39, 60, 60); 49 | var ast44 = new ASTNode(new SyntaxHead("char", 0),ast39, 61, 63); 50 | var ast45 = new ASTNode(new SyntaxHead("'", 1),ast44, 61, 61); 51 | var ast46 = new ASTNode(new SyntaxHead("Char", 0),ast44, 62, 62); 52 | var ast47 = new ASTNode(new SyntaxHead("'", 1),ast44, 63, 63); 53 | ast44.args = [ast45,ast46,ast47]; 54 | ast39.args = [ast40,ast41,ast42,ast43,ast44]; 55 | var ast48 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 64, 68); 56 | var ast49 = new ASTNode(new SyntaxHead("=", 128),ast2, 69, 74); 57 | var ast50 = new ASTNode(new SyntaxHead("Identifier", 0),ast49, 69, 69); 58 | var ast51 = new ASTNode(new SyntaxHead("Whitespace", 1),ast49, 70, 70); 59 | var ast52 = new ASTNode(new SyntaxHead("=", 1),ast49, 71, 71); 60 | var ast53 = new ASTNode(new SyntaxHead("Whitespace", 1),ast49, 72, 72); 61 | var ast54 = new ASTNode(new SyntaxHead("quote", 0),ast49, 73, 74); 62 | var ast55 = new ASTNode(new SyntaxHead(":", 1),ast54, 73, 73); 63 | var ast56 = new ASTNode(new SyntaxHead("Identifier", 0),ast54, 74, 74); 64 | ast54.args = [ast55,ast56]; 65 | ast49.args = [ast50,ast51,ast52,ast53,ast54]; 66 | var ast57 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 75, 79); 67 | var ast58 = new ASTNode(new SyntaxHead("=", 128),ast2, 80, 94); 68 | var ast59 = new ASTNode(new SyntaxHead("Identifier", 0),ast58, 80, 80); 69 | var ast60 = new ASTNode(new SyntaxHead("Whitespace", 1),ast58, 81, 81); 70 | var ast61 = new ASTNode(new SyntaxHead("=", 1),ast58, 82, 82); 71 | var ast62 = new ASTNode(new SyntaxHead("Whitespace", 1),ast58, 83, 83); 72 | var ast63 = new ASTNode(new SyntaxHead("quote", 0),ast58, 84, 94); 73 | var ast64 = new ASTNode(new SyntaxHead("block", 0),ast63, 84, 94); 74 | var ast65 = new ASTNode(new SyntaxHead("quote", 1),ast64, 84, 88); 75 | var ast66 = new ASTNode(new SyntaxHead("Whitespace", 1),ast64, 89, 89); 76 | var ast67 = new ASTNode(new SyntaxHead("Identifier", 0),ast64, 90, 90); 77 | var ast68 = new ASTNode(new SyntaxHead("Whitespace", 1),ast64, 91, 91); 78 | var ast69 = new ASTNode(new SyntaxHead("end", 1),ast64, 92, 94); 79 | ast64.args = [ast65,ast66,ast67,ast68,ast69]; 80 | ast63.args = [ast64]; 81 | ast58.args = [ast59,ast60,ast61,ast62,ast63]; 82 | var ast70 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 95, 99); 83 | var ast71 = new ASTNode(new SyntaxHead("=", 128),ast2, 100, 124); 84 | var ast72 = new ASTNode(new SyntaxHead("Identifier", 0),ast71, 100, 100); 85 | var ast73 = new ASTNode(new SyntaxHead("Whitespace", 1),ast71, 101, 101); 86 | var ast74 = new ASTNode(new SyntaxHead("=", 1),ast71, 102, 102); 87 | var ast75 = new ASTNode(new SyntaxHead("Whitespace", 1),ast71, 103, 103); 88 | var ast76 = new ASTNode(new SyntaxHead("call", 0),ast71, 104, 124); 89 | var ast77 = new ASTNode(new SyntaxHead("curly", 0),ast76, 104, 114); 90 | var ast78 = new ASTNode(new SyntaxHead("Identifier", 0),ast77, 104, 109); 91 | var ast79 = new ASTNode(new SyntaxHead("{", 1),ast77, 110, 110); 92 | var ast80 = new ASTNode(new SyntaxHead("Identifier", 0),ast77, 111, 113); 93 | var ast81 = new ASTNode(new SyntaxHead("}", 1),ast77, 114, 114); 94 | ast77.args = [ast78,ast79,ast80,ast81]; 95 | var ast82 = new ASTNode(new SyntaxHead("(", 1),ast76, 115, 115); 96 | var ast83 = new ASTNode(new SyntaxHead("Identifier", 0),ast76, 116, 120); 97 | var ast84 = new ASTNode(new SyntaxHead(",", 1),ast76, 121, 121); 98 | var ast85 = new ASTNode(new SyntaxHead("Whitespace", 1),ast76, 122, 122); 99 | var ast86 = new ASTNode(new SyntaxHead("Integer", 0),ast76, 123, 123); 100 | var ast87 = new ASTNode(new SyntaxHead(")", 1),ast76, 124, 124); 101 | ast76.args = [ast77,ast82,ast83,ast84,ast85,ast86,ast87]; 102 | ast71.args = [ast72,ast73,ast74,ast75,ast76]; 103 | var ast88 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 125, 129); 104 | var ast89 = new ASTNode(new SyntaxHead("call", 0),ast2, 130, 145); 105 | var ast90 = new ASTNode(new SyntaxHead("Identifier", 0),ast89, 130, 133); 106 | var ast91 = new ASTNode(new SyntaxHead("(", 1),ast89, 134, 134); 107 | var ast92 = new ASTNode(new SyntaxHead("Identifier", 0),ast89, 135, 135); 108 | var ast93 = new ASTNode(new SyntaxHead("parameters", 0),ast89, 136, 144); 109 | var ast94 = new ASTNode(new SyntaxHead(";", 1),ast93, 136, 136); 110 | var ast95 = new ASTNode(new SyntaxHead("=", 0),ast93, 137, 144); 111 | var ast96 = new ASTNode(new SyntaxHead("Identifier", 0),ast95, 137, 139); 112 | var ast97 = new ASTNode(new SyntaxHead("=", 1),ast95, 140, 140); 113 | var ast98 = new ASTNode(new SyntaxHead("true", 0),ast95, 141, 144); 114 | ast95.args = [ast96,ast97,ast98]; 115 | ast93.args = [ast94,ast95]; 116 | var ast99 = new ASTNode(new SyntaxHead(")", 1),ast89, 145, 145); 117 | ast89.args = [ast90,ast91,ast92,ast93,ast99]; 118 | var ast100 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 146, 150); 119 | var ast101 = new ASTNode(new SyntaxHead("=", 128),ast2, 151, 186); 120 | var ast102 = new ASTNode(new SyntaxHead("Identifier", 0),ast101, 151, 151); 121 | var ast103 = new ASTNode(new SyntaxHead("Whitespace", 1),ast101, 152, 152); 122 | var ast104 = new ASTNode(new SyntaxHead("=", 1),ast101, 153, 153); 123 | var ast105 = new ASTNode(new SyntaxHead("Whitespace", 1),ast101, 154, 154); 124 | var ast106 = new ASTNode(new SyntaxHead("ref", 0),ast101, 155, 186); 125 | var ast107 = new ASTNode(new SyntaxHead("call", 0),ast106, 155, 183); 126 | var ast108 = new ASTNode(new SyntaxHead("curly", 0),ast107, 155, 181); 127 | var ast109 = new ASTNode(new SyntaxHead("Identifier", 0),ast108, 155, 160); 128 | var ast110 = new ASTNode(new SyntaxHead("{", 1),ast108, 161, 161); 129 | var ast111 = new ASTNode(new SyntaxHead("curly", 0),ast108, 162, 180); 130 | var ast112 = new ASTNode(new SyntaxHead("Identifier", 0),ast111, 162, 166); 131 | var ast113 = new ASTNode(new SyntaxHead("{", 1),ast111, 167, 167); 132 | var ast114 = new ASTNode(new SyntaxHead("Identifier", 0),ast111, 168, 170); 133 | var ast115 = new ASTNode(new SyntaxHead(",", 1),ast111, 171, 171); 134 | var ast116 = new ASTNode(new SyntaxHead("Whitespace", 1),ast111, 172, 172); 135 | var ast117 = new ASTNode(new SyntaxHead("Identifier", 0),ast111, 173, 179); 136 | var ast118 = new ASTNode(new SyntaxHead("}", 1),ast111, 180, 180); 137 | ast111.args = [ast112,ast113,ast114,ast115,ast116,ast117,ast118]; 138 | var ast119 = new ASTNode(new SyntaxHead("}", 1),ast108, 181, 181); 139 | ast108.args = [ast109,ast110,ast111,ast119]; 140 | var ast120 = new ASTNode(new SyntaxHead("(", 1),ast107, 182, 182); 141 | var ast121 = new ASTNode(new SyntaxHead(")", 1),ast107, 183, 183); 142 | ast107.args = [ast108,ast120,ast121]; 143 | var ast122 = new ASTNode(new SyntaxHead("[", 1),ast106, 184, 184); 144 | var ast123 = new ASTNode(new SyntaxHead("Integer", 0),ast106, 185, 185); 145 | var ast124 = new ASTNode(new SyntaxHead("]", 1),ast106, 186, 186); 146 | ast106.args = [ast107,ast122,ast123,ast124]; 147 | ast101.args = [ast102,ast103,ast104,ast105,ast106]; 148 | var ast125 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 187, 191); 149 | var ast126 = new ASTNode(new SyntaxHead("if", 0),ast2, 192, 267); 150 | var ast127 = new ASTNode(new SyntaxHead("if", 1),ast126, 192, 193); 151 | var ast128 = new ASTNode(new SyntaxHead("call", 8),ast126, 194, 203); 152 | var ast129 = new ASTNode(new SyntaxHead("Whitespace", 1),ast128, 194, 194); 153 | var ast130 = new ASTNode(new SyntaxHead("Identifier", 0),ast128, 195, 195); 154 | var ast131 = new ASTNode(new SyntaxHead("Whitespace", 1),ast128, 196, 196); 155 | var ast132 = new ASTNode(new SyntaxHead("isa", 0),ast128, 197, 199); 156 | var ast133 = new ASTNode(new SyntaxHead("Whitespace", 1),ast128, 200, 200); 157 | var ast134 = new ASTNode(new SyntaxHead("Identifier", 0),ast128, 201, 203); 158 | ast128.args = [ast129,ast130,ast131,ast132,ast133,ast134]; 159 | var ast135 = new ASTNode(new SyntaxHead("block", 0),ast126, 204, 239); 160 | var ast136 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast135, 204, 212); 161 | var ast137 = new ASTNode(new SyntaxHead("call", 0),ast135, 213, 220); 162 | var ast138 = new ASTNode(new SyntaxHead("Identifier", 0),ast137, 213, 217); 163 | var ast139 = new ASTNode(new SyntaxHead("(", 1),ast137, 218, 218); 164 | var ast140 = new ASTNode(new SyntaxHead("Identifier", 0),ast137, 219, 219); 165 | var ast141 = new ASTNode(new SyntaxHead(")", 1),ast137, 220, 220); 166 | ast137.args = [ast138,ast139,ast140,ast141]; 167 | var ast142 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast135, 221, 229); 168 | var ast143 = new ASTNode(new SyntaxHead("=", 128),ast135, 230, 234); 169 | var ast144 = new ASTNode(new SyntaxHead("Identifier", 0),ast143, 230, 230); 170 | var ast145 = new ASTNode(new SyntaxHead("Whitespace", 1),ast143, 231, 231); 171 | var ast146 = new ASTNode(new SyntaxHead("=", 1),ast143, 232, 232); 172 | var ast147 = new ASTNode(new SyntaxHead("Whitespace", 1),ast143, 233, 233); 173 | var ast148 = new ASTNode(new SyntaxHead("Integer", 0),ast143, 234, 234); 174 | ast143.args = [ast144,ast145,ast146,ast147,ast148]; 175 | var ast149 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast135, 235, 239); 176 | ast135.args = [ast136,ast137,ast142,ast143,ast149]; 177 | var ast150 = new ASTNode(new SyntaxHead("else", 1),ast126, 240, 243); 178 | var ast151 = new ASTNode(new SyntaxHead("block", 0),ast126, 244, 264); 179 | var ast152 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast151, 244, 252); 180 | var ast153 = new ASTNode(new SyntaxHead("call", 0),ast151, 253, 259); 181 | var ast154 = new ASTNode(new SyntaxHead("Identifier", 0),ast153, 253, 257); 182 | var ast155 = new ASTNode(new SyntaxHead("(", 1),ast153, 258, 258); 183 | var ast156 = new ASTNode(new SyntaxHead(")", 1),ast153, 259, 259); 184 | ast153.args = [ast154,ast155,ast156]; 185 | var ast157 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast151, 260, 264); 186 | ast151.args = [ast152,ast153,ast157]; 187 | var ast158 = new ASTNode(new SyntaxHead("end", 1),ast126, 265, 267); 188 | ast126.args = [ast127,ast128,ast135,ast150,ast151,ast158]; 189 | var ast159 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 268, 272); 190 | var ast160 = new ASTNode(new SyntaxHead("+=", 128),ast2, 273, 281); 191 | var ast161 = new ASTNode(new SyntaxHead("ref", 0),ast160, 273, 276); 192 | var ast162 = new ASTNode(new SyntaxHead("Identifier", 0),ast161, 273, 273); 193 | var ast163 = new ASTNode(new SyntaxHead("[", 1),ast161, 274, 274); 194 | var ast164 = new ASTNode(new SyntaxHead("Integer", 0),ast161, 275, 275); 195 | var ast165 = new ASTNode(new SyntaxHead("]", 1),ast161, 276, 276); 196 | ast161.args = [ast162,ast163,ast164,ast165]; 197 | var ast166 = new ASTNode(new SyntaxHead("Whitespace", 1),ast160, 277, 277); 198 | var ast167 = new ASTNode(new SyntaxHead("+=", 1),ast160, 278, 279); 199 | var ast168 = new ASTNode(new SyntaxHead("Whitespace", 1),ast160, 280, 280); 200 | var ast169 = new ASTNode(new SyntaxHead("Integer", 0),ast160, 281, 281); 201 | ast160.args = [ast161,ast166,ast167,ast168,ast169]; 202 | var ast170 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 282, 286); 203 | var ast171 = new ASTNode(new SyntaxHead("+=", 128),ast2, 287, 297); 204 | var ast172 = new ASTNode(new SyntaxHead("ref", 0),ast171, 287, 290); 205 | var ast173 = new ASTNode(new SyntaxHead("Identifier", 0),ast172, 287, 287); 206 | var ast174 = new ASTNode(new SyntaxHead("[", 1),ast172, 288, 288); 207 | var ast175 = new ASTNode(new SyntaxHead("Integer", 0),ast172, 289, 289); 208 | var ast176 = new ASTNode(new SyntaxHead("]", 1),ast172, 290, 290); 209 | ast172.args = [ast173,ast174,ast175,ast176]; 210 | var ast177 = new ASTNode(new SyntaxHead("Whitespace", 1),ast171, 291, 291); 211 | var ast178 = new ASTNode(new SyntaxHead("+=", 1),ast171, 292, 293); 212 | var ast179 = new ASTNode(new SyntaxHead("Whitespace", 1),ast171, 294, 294); 213 | var ast180 = new ASTNode(new SyntaxHead("string", 0),ast171, 295, 297); 214 | var ast181 = new ASTNode(new SyntaxHead(""", 1),ast180, 295, 295); 215 | var ast182 = new ASTNode(new SyntaxHead("String", 0),ast180, 296, 296); 216 | var ast183 = new ASTNode(new SyntaxHead(""", 1),ast180, 297, 297); 217 | ast180.args = [ast181,ast182,ast183]; 218 | ast171.args = [ast172,ast177,ast178,ast179,ast180]; 219 | var ast184 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 298, 302); 220 | var ast185 = new ASTNode(new SyntaxHead("=", 128),ast2, 303, 330); 221 | var ast186 = new ASTNode(new SyntaxHead("tuple", 0),ast185, 303, 316); 222 | var ast187 = new ASTNode(new SyntaxHead("(", 1),ast186, 303, 303); 223 | var ast188 = new ASTNode(new SyntaxHead("Identifier", 0),ast186, 304, 305); 224 | var ast189 = new ASTNode(new SyntaxHead(",", 1),ast186, 306, 306); 225 | var ast190 = new ASTNode(new SyntaxHead("Whitespace", 1),ast186, 307, 307); 226 | var ast191 = new ASTNode(new SyntaxHead("tuple", 0),ast186, 308, 315); 227 | var ast192 = new ASTNode(new SyntaxHead("(", 1),ast191, 308, 308); 228 | var ast193 = new ASTNode(new SyntaxHead("Identifier", 0),ast191, 309, 310); 229 | var ast194 = new ASTNode(new SyntaxHead(",", 1),ast191, 311, 311); 230 | var ast195 = new ASTNode(new SyntaxHead("Whitespace", 1),ast191, 312, 312); 231 | var ast196 = new ASTNode(new SyntaxHead("Identifier", 0),ast191, 313, 314); 232 | var ast197 = new ASTNode(new SyntaxHead(")", 1),ast191, 315, 315); 233 | ast191.args = [ast192,ast193,ast194,ast195,ast196,ast197]; 234 | var ast198 = new ASTNode(new SyntaxHead(")", 1),ast186, 316, 316); 235 | ast186.args = [ast187,ast188,ast189,ast190,ast191,ast198]; 236 | var ast199 = new ASTNode(new SyntaxHead("Whitespace", 1),ast185, 317, 317); 237 | var ast200 = new ASTNode(new SyntaxHead("=", 1),ast185, 318, 318); 238 | var ast201 = new ASTNode(new SyntaxHead("Whitespace", 1),ast185, 319, 319); 239 | var ast202 = new ASTNode(new SyntaxHead("tuple", 0),ast185, 320, 330); 240 | var ast203 = new ASTNode(new SyntaxHead("(", 1),ast202, 320, 320); 241 | var ast204 = new ASTNode(new SyntaxHead("Integer", 0),ast202, 321, 321); 242 | var ast205 = new ASTNode(new SyntaxHead(",", 1),ast202, 322, 322); 243 | var ast206 = new ASTNode(new SyntaxHead("tuple", 0),ast202, 323, 329); 244 | var ast207 = new ASTNode(new SyntaxHead("(", 1),ast206, 323, 323); 245 | var ast208 = new ASTNode(new SyntaxHead("Integer", 0),ast206, 324, 324); 246 | var ast209 = new ASTNode(new SyntaxHead(",", 1),ast206, 325, 325); 247 | var ast210 = new ASTNode(new SyntaxHead("char", 0),ast206, 326, 328); 248 | var ast211 = new ASTNode(new SyntaxHead("'", 1),ast210, 326, 326); 249 | var ast212 = new ASTNode(new SyntaxHead("Char", 0),ast210, 327, 327); 250 | var ast213 = new ASTNode(new SyntaxHead("'", 1),ast210, 328, 328); 251 | ast210.args = [ast211,ast212,ast213]; 252 | var ast214 = new ASTNode(new SyntaxHead(")", 1),ast206, 329, 329); 253 | ast206.args = [ast207,ast208,ast209,ast210,ast214]; 254 | var ast215 = new ASTNode(new SyntaxHead(")", 1),ast202, 330, 330); 255 | ast202.args = [ast203,ast204,ast205,ast206,ast215]; 256 | ast185.args = [ast186,ast199,ast200,ast201,ast202]; 257 | var ast216 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 331, 335); 258 | var ast217 = new ASTNode(new SyntaxHead("=", 128),ast2, 336, 343); 259 | var ast218 = new ASTNode(new SyntaxHead("Identifier", 0),ast217, 336, 337); 260 | var ast219 = new ASTNode(new SyntaxHead("Whitespace", 1),ast217, 338, 338); 261 | var ast220 = new ASTNode(new SyntaxHead("=", 1),ast217, 339, 339); 262 | var ast221 = new ASTNode(new SyntaxHead("Whitespace", 1),ast217, 340, 340); 263 | var ast222 = new ASTNode(new SyntaxHead("char", 0),ast217, 341, 343); 264 | var ast223 = new ASTNode(new SyntaxHead("'", 1),ast222, 341, 341); 265 | var ast224 = new ASTNode(new SyntaxHead("Char", 0),ast222, 342, 342); 266 | var ast225 = new ASTNode(new SyntaxHead("'", 1),ast222, 343, 343); 267 | ast222.args = [ast223,ast224,ast225]; 268 | ast217.args = [ast218,ast219,ast220,ast221,ast222]; 269 | var ast226 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 344, 348); 270 | var ast227 = new ASTNode(new SyntaxHead("=", 128),ast2, 349, 356); 271 | var ast228 = new ASTNode(new SyntaxHead("Identifier", 0),ast227, 349, 350); 272 | var ast229 = new ASTNode(new SyntaxHead("Whitespace", 1),ast227, 351, 351); 273 | var ast230 = new ASTNode(new SyntaxHead("=", 1),ast227, 352, 352); 274 | var ast231 = new ASTNode(new SyntaxHead("Whitespace", 1),ast227, 353, 353); 275 | var ast232 = new ASTNode(new SyntaxHead("Float", 0),ast227, 354, 356); 276 | ast227.args = [ast228,ast229,ast230,ast231,ast232]; 277 | var ast233 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast2, 357, 357); 278 | var ast234 = new ASTNode(new SyntaxHead("end", 1),ast2, 358, 360); 279 | ast2.args = [ast3,ast4,ast5,ast11,ast12,ast18,ast19,ast31,ast32,ast38,ast39,ast48,ast49,ast57,ast58,ast70,ast71,ast88,ast89,ast100,ast101,ast125,ast126,ast159,ast160,ast170,ast171,ast184,ast185,ast216,ast217,ast226,ast227,ast233,ast234]; 280 | var ast235 = new ASTNode(new SyntaxHead("NewlineWs", 1),ast1, 361, 361); 281 | ast1.args = [ast2,ast235] -------------------------------------------------------------------------------- /src/www/dune: -------------------------------------------------------------------------------- 1 | (rule 2 | (copy ../main.bc.js hazel.js)) 3 | 4 | (rule 5 | (copy ../worker.bc.js worker.js)) 6 | -------------------------------------------------------------------------------- /src/www/fonts/FiraCode-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/fonts/FiraCode-Bold.woff2 -------------------------------------------------------------------------------- /src/www/fonts/FiraCode-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/fonts/FiraCode-Regular.woff2 -------------------------------------------------------------------------------- /src/www/fonts/HelveticaNeue-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/fonts/HelveticaNeue-Bold.woff2 -------------------------------------------------------------------------------- /src/www/fonts/HelveticaNeue-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/fonts/HelveticaNeue-Regular.woff2 -------------------------------------------------------------------------------- /src/www/fonts/SourceCodePro-Black.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/fonts/SourceCodePro-Black.otf.woff2 -------------------------------------------------------------------------------- /src/www/fonts/SourceCodePro-Bold.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/fonts/SourceCodePro-Bold.otf.woff2 -------------------------------------------------------------------------------- /src/www/fonts/SourceCodePro-Regular.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/fonts/SourceCodePro-Regular.otf.woff2 -------------------------------------------------------------------------------- /src/www/img/hazelnut.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/www/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/img/loading.gif -------------------------------------------------------------------------------- /src/www/index_template.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | title 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 | __TEMPLATE_PLACEHOLDER__ 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/www/mystyle.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Fira Code'; 3 | font-style: normal; 4 | font-weight: normal; 5 | src: url('fonts/FiraCode-Regular.woff2') format('woff2'); 6 | } 7 | 8 | @font-face { 9 | font-family: 'Fira Code'; 10 | font-style: normal; 11 | font-weight: bold; 12 | src: url('fonts/FiraCode-Bold.woff2') format('woff2'); 13 | } 14 | 15 | @font-face { 16 | font-family: 'Helvetica Neue'; 17 | font-style: normal; 18 | font-weight: normal; 19 | src: url('fonts/HelveticaNeue-Regular.woff2') format('woff2'); 20 | } 21 | 22 | @font-face { 23 | font-family: 'Helvetica Neue'; 24 | font-style: normal; 25 | font-weight: bold; 26 | src: url('fonts/HelveticaNeue-Bold.woff2') format('woff2'); 27 | } 28 | 29 | @font-face { 30 | font-family: 'Source Code Pro'; 31 | font-weight: normal; 32 | font-style: normal; 33 | font-stretch: normal; 34 | src: url('fonts/SourceCodePro-Regular.otf.woff2') format('woff2'); 35 | } 36 | 37 | @font-face { 38 | font-family: 'Source Code Pro'; 39 | font-weight: bold; 40 | font-style: normal; 41 | font-stretch: normal; 42 | src: url('fonts/SourceCodePro-Bold.otf.woff2') format('woff2'); 43 | } 44 | 45 | @font-face { 46 | font-family: 'Source Code Pro'; 47 | font-weight: 900; 48 | font-style: normal; 49 | font-stretch: normal; 50 | src: url('fonts/SourceCodePro-Black.otf.woff2') format('woff2'); 51 | } 52 | 53 | .code { 54 | display: flex; 55 | margin: 0; 56 | justify-content: left; 57 | } 58 | 59 | .linebreak { 60 | display: block; 61 | } -------------------------------------------------------------------------------- /src/www/popper.min.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @popperjs/core v2.6.0 - MIT License 3 | */ 4 | 5 | "use strict";!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((e=e||self).Popper={})}(this,(function(e){function t(e){return{width:(e=e.getBoundingClientRect()).width,height:e.height,top:e.top,right:e.right,bottom:e.bottom,left:e.left,x:e.left,y:e.top}}function n(e){return"[object Window]"!==e.toString()?(e=e.ownerDocument)&&e.defaultView||window:e}function r(e){return{scrollLeft:(e=n(e)).pageXOffset,scrollTop:e.pageYOffset}}function o(e){return e instanceof n(e).Element||e instanceof Element}function i(e){return e instanceof n(e).HTMLElement||e instanceof HTMLElement}function a(e){return e?(e.nodeName||"").toLowerCase():null}function s(e){return((o(e)?e.ownerDocument:e.document)||window.document).documentElement}function f(e){return t(s(e)).left+r(e).scrollLeft}function c(e){return n(e).getComputedStyle(e)}function p(e){return e=c(e),/auto|scroll|overlay|hidden/.test(e.overflow+e.overflowY+e.overflowX)}function l(e,o,c){void 0===c&&(c=!1);var l=s(o);e=t(e);var u=i(o),d={scrollLeft:0,scrollTop:0},m={x:0,y:0};return(u||!u&&!c)&&(("body"!==a(o)||p(l))&&(d=o!==n(o)&&i(o)?{scrollLeft:o.scrollLeft,scrollTop:o.scrollTop}:r(o)),i(o)?((m=t(o)).x+=o.clientLeft,m.y+=o.clientTop):l&&(m.x=f(l))),{x:e.left+d.scrollLeft-m.x,y:e.top+d.scrollTop-m.y,width:e.width,height:e.height}}function u(e){return{x:e.offsetLeft,y:e.offsetTop,width:e.offsetWidth,height:e.offsetHeight}}function d(e){return"html"===a(e)?e:e.assignedSlot||e.parentNode||e.host||s(e)}function m(e,t){void 0===t&&(t=[]);var r=function e(t){return 0<=["html","body","#document"].indexOf(a(t))?t.ownerDocument.body:i(t)&&p(t)?t:e(d(t))}(e);e="body"===a(r);var o=n(r);return r=e?[o].concat(o.visualViewport||[],p(r)?r:[]):r,t=t.concat(r),e?t:t.concat(m(d(r)))}function h(e){if(!i(e)||"fixed"===c(e).position)return null;if(e=e.offsetParent){var t=s(e);if("body"===a(e)&&"static"===c(e).position&&"static"!==c(t).position)return t}return e}function g(e){for(var t=n(e),r=h(e);r&&0<=["table","td","th"].indexOf(a(r))&&"static"===c(r).position;)r=h(r);if(r&&"body"===a(r)&&"static"===c(r).position)return t;if(!r)e:{for(e=d(e);i(e)&&0>["html","body"].indexOf(a(e));){if("none"!==(r=c(e)).transform||"none"!==r.perspective||r.willChange&&"auto"!==r.willChange){r=e;break e}e=e.parentNode}r=null}return r||t}function v(e){var t=new Map,n=new Set,r=[];return e.forEach((function(e){t.set(e.name,e)})),e.forEach((function(e){n.has(e.name)||function e(o){n.add(o.name),[].concat(o.requires||[],o.requiresIfExists||[]).forEach((function(r){n.has(r)||(r=t.get(r))&&e(r)})),r.push(o)}(e)})),r}function b(e){var t;return function(){return t||(t=new Promise((function(n){Promise.resolve().then((function(){t=void 0,n(e())}))}))),t}}function y(e){return e.split("-")[0]}function O(e,t){var r,o=t.getRootNode&&t.getRootNode();if(e.contains(t))return!0;if((r=o)&&(r=o instanceof(r=n(o).ShadowRoot)||o instanceof ShadowRoot),r)do{if(t&&e.isSameNode(t))return!0;t=t.parentNode||t.host}while(t);return!1}function w(e){return Object.assign(Object.assign({},e),{},{left:e.x,top:e.y,right:e.x+e.width,bottom:e.y+e.height})}function x(e,o){if("viewport"===o){o=n(e);var a=s(e);o=o.visualViewport;var p=a.clientWidth;a=a.clientHeight;var l=0,u=0;o&&(p=o.width,a=o.height,/^((?!chrome|android).)*safari/i.test(navigator.userAgent)||(l=o.offsetLeft,u=o.offsetTop)),e=w(e={width:p,height:a,x:l+f(e),y:u})}else i(o)?((e=t(o)).top+=o.clientTop,e.left+=o.clientLeft,e.bottom=e.top+o.clientHeight,e.right=e.left+o.clientWidth,e.width=o.clientWidth,e.height=o.clientHeight,e.x=e.left,e.y=e.top):(u=s(e),e=s(u),l=r(u),o=u.ownerDocument.body,p=Math.max(e.scrollWidth,e.clientWidth,o?o.scrollWidth:0,o?o.clientWidth:0),a=Math.max(e.scrollHeight,e.clientHeight,o?o.scrollHeight:0,o?o.clientHeight:0),u=-l.scrollLeft+f(u),l=-l.scrollTop,"rtl"===c(o||e).direction&&(u+=Math.max(e.clientWidth,o?o.clientWidth:0)-p),e=w({width:p,height:a,x:u,y:l}));return e}function j(e,t,n){return t="clippingParents"===t?function(e){var t=m(d(e)),n=0<=["absolute","fixed"].indexOf(c(e).position)&&i(e)?g(e):e;return o(n)?t.filter((function(e){return o(e)&&O(e,n)&&"body"!==a(e)})):[]}(e):[].concat(t),(n=(n=[].concat(t,[n])).reduce((function(t,n){return n=x(e,n),t.top=Math.max(n.top,t.top),t.right=Math.min(n.right,t.right),t.bottom=Math.min(n.bottom,t.bottom),t.left=Math.max(n.left,t.left),t}),x(e,n[0]))).width=n.right-n.left,n.height=n.bottom-n.top,n.x=n.left,n.y=n.top,n}function M(e){return 0<=["top","bottom"].indexOf(e)?"x":"y"}function E(e){var t=e.reference,n=e.element,r=(e=e.placement)?y(e):null;e=e?e.split("-")[1]:null;var o=t.x+t.width/2-n.width/2,i=t.y+t.height/2-n.height/2;switch(r){case"top":o={x:o,y:t.y-n.height};break;case"bottom":o={x:o,y:t.y+t.height};break;case"right":o={x:t.x+t.width,y:i};break;case"left":o={x:t.x-n.width,y:i};break;default:o={x:t.x,y:t.y}}if(null!=(r=r?M(r):null))switch(i="y"===r?"height":"width",e){case"start":o[r]-=t[i]/2-n[i]/2;break;case"end":o[r]+=t[i]/2-n[i]/2}return o}function D(e){return Object.assign(Object.assign({},{top:0,right:0,bottom:0,left:0}),e)}function P(e,t){return t.reduce((function(t,n){return t[n]=e,t}),{})}function L(e,n){void 0===n&&(n={});var r=n;n=void 0===(n=r.placement)?e.placement:n;var i=r.boundary,a=void 0===i?"clippingParents":i,f=void 0===(i=r.rootBoundary)?"viewport":i;i=void 0===(i=r.elementContext)?"popper":i;var c=r.altBoundary,p=void 0!==c&&c;r=D("number"!=typeof(r=void 0===(r=r.padding)?0:r)?r:P(r,T));var l=e.elements.reference;c=e.rects.popper,a=j(o(p=e.elements[p?"popper"===i?"reference":"popper":i])?p:p.contextElement||s(e.elements.popper),a,f),p=E({reference:f=t(l),element:c,strategy:"absolute",placement:n}),c=w(Object.assign(Object.assign({},c),p)),f="popper"===i?c:f;var u={top:a.top-f.top+r.top,bottom:f.bottom-a.bottom+r.bottom,left:a.left-f.left+r.left,right:f.right-a.right+r.right};if(e=e.modifiersData.offset,"popper"===i&&e){var d=e[n];Object.keys(u).forEach((function(e){var t=0<=["right","bottom"].indexOf(e)?1:-1,n=0<=["top","bottom"].indexOf(e)?"y":"x";u[e]+=d[n]*t}))}return u}function k(){for(var e=arguments.length,t=Array(e),n=0;n(v.devicePixelRatio||1)?"translate("+e+"px, "+l+"px)":"translate3d("+e+"px, "+l+"px, 0)",d)):Object.assign(Object.assign({},r),{},((t={})[h]=a?l+"px":"",t[m]=u?e+"px":"",t.transform="",t))}function A(e){return e.replace(/left|right|bottom|top/g,(function(e){return G[e]}))}function H(e){return e.replace(/start|end/g,(function(e){return J[e]}))}function R(e,t,n){return void 0===n&&(n={x:0,y:0}),{top:e.top-t.height-n.y,right:e.right-t.width+n.x,bottom:e.bottom-t.height+n.y,left:e.left-t.width-n.x}}function S(e){return["top","right","bottom","left"].some((function(t){return 0<=e[t]}))}var T=["top","bottom","right","left"],q=T.reduce((function(e,t){return e.concat([t+"-start",t+"-end"])}),[]),C=[].concat(T,["auto"]).reduce((function(e,t){return e.concat([t,t+"-start",t+"-end"])}),[]),N="beforeRead read afterRead beforeMain main afterMain beforeWrite write afterWrite".split(" "),V={placement:"bottom",modifiers:[],strategy:"absolute"},I={passive:!0},_={name:"eventListeners",enabled:!0,phase:"write",fn:function(){},effect:function(e){var t=e.state,r=e.instance,o=(e=e.options).scroll,i=void 0===o||o,a=void 0===(e=e.resize)||e,s=n(t.elements.popper),f=[].concat(t.scrollParents.reference,t.scrollParents.popper);return i&&f.forEach((function(e){e.addEventListener("scroll",r.update,I)})),a&&s.addEventListener("resize",r.update,I),function(){i&&f.forEach((function(e){e.removeEventListener("scroll",r.update,I)})),a&&s.removeEventListener("resize",r.update,I)}},data:{}},U={name:"popperOffsets",enabled:!0,phase:"read",fn:function(e){var t=e.state;t.modifiersData[e.name]=E({reference:t.rects.reference,element:t.rects.popper,strategy:"absolute",placement:t.placement})},data:{}},z={top:"auto",right:"auto",bottom:"auto",left:"auto"},F={name:"computeStyles",enabled:!0,phase:"beforeWrite",fn:function(e){var t=e.state,n=e.options;e=void 0===(e=n.gpuAcceleration)||e;var r=n.adaptive;r=void 0===r||r,n=void 0===(n=n.roundOffsets)||n,e={placement:y(t.placement),popper:t.elements.popper,popperRect:t.rects.popper,gpuAcceleration:e},null!=t.modifiersData.popperOffsets&&(t.styles.popper=Object.assign(Object.assign({},t.styles.popper),W(Object.assign(Object.assign({},e),{},{offsets:t.modifiersData.popperOffsets,position:t.options.strategy,adaptive:r,roundOffsets:n})))),null!=t.modifiersData.arrow&&(t.styles.arrow=Object.assign(Object.assign({},t.styles.arrow),W(Object.assign(Object.assign({},e),{},{offsets:t.modifiersData.arrow,position:"absolute",adaptive:!1,roundOffsets:n})))),t.attributes.popper=Object.assign(Object.assign({},t.attributes.popper),{},{"data-popper-placement":t.placement})},data:{}},X={name:"applyStyles",enabled:!0,phase:"write",fn:function(e){var t=e.state;Object.keys(t.elements).forEach((function(e){var n=t.styles[e]||{},r=t.attributes[e]||{},o=t.elements[e];i(o)&&a(o)&&(Object.assign(o.style,n),Object.keys(r).forEach((function(e){var t=r[e];!1===t?o.removeAttribute(e):o.setAttribute(e,!0===t?"":t)})))}))},effect:function(e){var t=e.state,n={popper:{position:t.options.strategy,left:"0",top:"0",margin:"0"},arrow:{position:"absolute"},reference:{}};return Object.assign(t.elements.popper.style,n.popper),t.elements.arrow&&Object.assign(t.elements.arrow.style,n.arrow),function(){Object.keys(t.elements).forEach((function(e){var r=t.elements[e],o=t.attributes[e]||{};e=Object.keys(t.styles.hasOwnProperty(e)?t.styles[e]:n[e]).reduce((function(e,t){return e[t]="",e}),{}),i(r)&&a(r)&&(Object.assign(r.style,e),Object.keys(o).forEach((function(e){r.removeAttribute(e)})))}))}},requires:["computeStyles"]},Y={name:"offset",enabled:!0,phase:"main",requires:["popperOffsets"],fn:function(e){var t=e.state,n=e.name,r=void 0===(e=e.options.offset)?[0,0]:e,o=(e=C.reduce((function(e,n){var o=t.rects,i=y(n),a=0<=["left","top"].indexOf(i)?-1:1,s="function"==typeof r?r(Object.assign(Object.assign({},o),{},{placement:n})):r;return o=(o=s[0])||0,s=((s=s[1])||0)*a,i=0<=["left","right"].indexOf(i)?{x:s,y:o}:{x:o,y:s},e[n]=i,e}),{}))[t.placement],i=o.x;o=o.y,null!=t.modifiersData.popperOffsets&&(t.modifiersData.popperOffsets.x+=i,t.modifiersData.popperOffsets.y+=o),t.modifiersData[n]=e}},G={left:"right",right:"left",bottom:"top",top:"bottom"},J={start:"end",end:"start"},K={name:"flip",enabled:!0,phase:"main",fn:function(e){var t=e.state,n=e.options;if(e=e.name,!t.modifiersData[e]._skip){var r=n.mainAxis;r=void 0===r||r;var o=n.altAxis;o=void 0===o||o;var i=n.fallbackPlacements,a=n.padding,s=n.boundary,f=n.rootBoundary,c=n.altBoundary,p=n.flipVariations,l=void 0===p||p,u=n.allowedAutoPlacements;p=y(n=t.options.placement),i=i||(p!==n&&l?function(e){if("auto"===y(e))return[];var t=A(e);return[H(e),t,H(t)]}(n):[A(n)]);var d=[n].concat(i).reduce((function(e,n){return e.concat("auto"===y(n)?function(e,t){void 0===t&&(t={});var n=t.boundary,r=t.rootBoundary,o=t.padding,i=t.flipVariations,a=t.allowedAutoPlacements,s=void 0===a?C:a,f=t.placement.split("-")[1];0===(i=(t=f?i?q:q.filter((function(e){return e.split("-")[1]===f})):T).filter((function(e){return 0<=s.indexOf(e)}))).length&&(i=t);var c=i.reduce((function(t,i){return t[i]=L(e,{placement:i,boundary:n,rootBoundary:r,padding:o})[y(i)],t}),{});return Object.keys(c).sort((function(e,t){return c[e]-c[t]}))}(t,{placement:n,boundary:s,rootBoundary:f,padding:a,flipVariations:l,allowedAutoPlacements:u}):n)}),[]);n=t.rects.reference,i=t.rects.popper;var m=new Map;p=!0;for(var h=d[0],g=0;gi[x]&&(O=A(O)),x=A(O),w=[],r&&w.push(0>=j[b]),o&&w.push(0>=j[O],0>=j[x]),w.every((function(e){return e}))){h=v,p=!1;break}m.set(v,w)}if(p)for(r=function(e){var t=d.find((function(t){if(t=m.get(t))return t.slice(0,e).every((function(e){return e}))}));if(t)return h=t,"break"},o=l?3:1;0 #arrow { 35 | bottom: -4px; 36 | } 37 | 38 | #tooltip[data-popper-placement^='bottom'] > #arrow { 39 | top: -4px; 40 | } 41 | 42 | #tooltip[data-popper-placement^='left'] > #arrow { 43 | right: -4px; 44 | } 45 | 46 | #tooltip[data-popper-placement^='right'] > #arrow { 47 | left: -4px; 48 | } 49 | -------------------------------------------------------------------------------- /src/www/www/dune: -------------------------------------------------------------------------------- 1 | (rule 2 | (copy ../main.bc.js hazel.js)) 3 | 4 | (rule 5 | (copy ../worker.bc.js worker.js)) 6 | -------------------------------------------------------------------------------- /src/www/www/fonts/FiraCode-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/www/fonts/FiraCode-Bold.woff2 -------------------------------------------------------------------------------- /src/www/www/fonts/FiraCode-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/www/fonts/FiraCode-Regular.woff2 -------------------------------------------------------------------------------- /src/www/www/fonts/HelveticaNeue-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/www/fonts/HelveticaNeue-Bold.woff2 -------------------------------------------------------------------------------- /src/www/www/fonts/HelveticaNeue-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/www/fonts/HelveticaNeue-Regular.woff2 -------------------------------------------------------------------------------- /src/www/www/fonts/SourceCodePro-Black.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/www/fonts/SourceCodePro-Black.otf.woff2 -------------------------------------------------------------------------------- /src/www/www/fonts/SourceCodePro-Bold.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/www/fonts/SourceCodePro-Bold.otf.woff2 -------------------------------------------------------------------------------- /src/www/www/fonts/SourceCodePro-Regular.otf.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/www/fonts/SourceCodePro-Regular.otf.woff2 -------------------------------------------------------------------------------- /src/www/www/img/hazelnut.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/www/www/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/src/www/www/img/loading.gif -------------------------------------------------------------------------------- /src/www/www/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | hazel 10 | 11 | 12 | 13 | 14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 |

loading

22 |
23 |
24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /test/abstract/abstract.jl: -------------------------------------------------------------------------------- 1 | module AbstractTest 2 | 3 | function mysum(v) 4 | i = zero(eltype(v)) 5 | for z in v 6 | i = i + z 7 | end 8 | return i 9 | end 10 | 11 | function main() 12 | v = Int[1,2,3] 13 | z = mysum(v) 14 | mysum2(v) 15 | mysum3(v) 16 | mysum4(v) 17 | mysum5(v) 18 | if z > 0 19 | main() 20 | end 21 | end 22 | 23 | function mysum2(v::Vector{T}) where T 24 | i = zero(T) 25 | for z in v 26 | i = i + z 27 | end 28 | return i 29 | end 30 | 31 | 32 | function mysum3(v::Vector{T}) where T 33 | i = zero(T) 34 | for z in v 35 | i = i + 1.0 36 | end 37 | return i 38 | end 39 | 40 | function mysum4(v::Vector{T}) where T <: Real 41 | i = zero(T) 42 | for z in v 43 | i = i + 1.0 44 | end 45 | return i 46 | end 47 | 48 | function mysum5(v::Vector{T}) where T 49 | i = Vector{T}()[1] 50 | for z in v 51 | i = i + 1.0 52 | end 53 | return i 54 | end 55 | 56 | 57 | 58 | 59 | end -------------------------------------------------------------------------------- /test/abstract/abstracttest.jl: -------------------------------------------------------------------------------- 1 | module MyTest 2 | import SimpleTypeChecker 3 | include("abstract.jl") 4 | ctx = SimpleTypeChecker.Inference.GlobalContext() 5 | SimpleTypeChecker.API.addFile!(ctx, AbstractTest, abspath("test/abstract.jl")) 6 | SimpleTypeChecker.API.runCheck!(ctx) 7 | 8 | end -------------------------------------------------------------------------------- /test/case/case.jl: -------------------------------------------------------------------------------- 1 | module CaseTest 2 | 3 | const ExpectError = Dict{Any, Any}() 4 | # check literal type 5 | function f1()::Float64 6 | return 1 7 | end 8 | 9 | # assign with different type is disallowed 10 | function f2() 11 | x = 1 12 | x = 2 13 | x = 1.0 14 | end 15 | 16 | # call with union type is disallowed 17 | function f3(x::Union{Nothing, Int}) 18 | print(x) 19 | end 20 | 21 | # call with abstract type is disallowed 22 | function f4(x::Ref{Int}) 23 | print(x) 24 | end 25 | 26 | # x should be non-constant 27 | function f5(y::Bool) 28 | if y 29 | x = 1 30 | else 31 | x = 2 32 | end 33 | print(x) 34 | end 35 | 36 | # x should be constant 37 | function f6(y::Bool) 38 | if y 39 | x = 1 40 | else 41 | x = 1 42 | end 43 | return Array{Int, x} 44 | end 45 | 46 | # non-constant apply is disallowed 47 | function f7(y::Bool) 48 | if y 49 | x = 1 50 | else 51 | x = 2 52 | end 53 | return Array{Int, x} 54 | end 55 | 56 | # join of union type is disallowed 57 | function f8(y::Bool) 58 | if y 59 | x = 1.0 60 | else 61 | x = 1 62 | end 63 | end 64 | 65 | # unreachable can narrow type 66 | function f9(x::Union{Int, Nothing}) 67 | if x isa Int 68 | print(x) 69 | error() 70 | else 71 | end 72 | print(x) 73 | end 74 | 75 | # unreachable can narrow type 76 | function f10(x::Union{Int, Nothing}) 77 | if x isa Int 78 | print(x) 79 | error() 80 | end 81 | print(x) 82 | end 83 | 84 | function f11(y::Int) 85 | if y > 0 86 | x = 1 87 | error() 88 | elseif y > 1 89 | # x is not defined in this branch 90 | z = 3 91 | else 92 | x = 3 93 | error() 94 | end 95 | print(x) 96 | end 97 | 98 | # assignment in unreachable branch is still checked 99 | function f12(y::Int) 100 | if y > 0 101 | x = 1 102 | else 103 | x = 1.0 104 | error() 105 | end 106 | print(x) 107 | end 108 | 109 | function f13() 110 | error() 111 | return 1 112 | end 113 | 114 | function f14() 115 | return xxxxxxxxxxx 116 | end 117 | 118 | function f15(x::Vector{Int}) 119 | y = similar(x, Vector{eltype(x)}) 120 | z = y[1][1] 121 | print(z) 122 | end 123 | 124 | function f16(x::Vector{Float64}) 125 | y = Dict{Dict{eltype(x), Vector{eltype(x)}}, Int} 126 | z = Dict{y, y}() 127 | print(z) 128 | end 129 | 130 | function f17(x::Vector{Int}) 131 | y = 3 132 | t = 3 133 | if y > 0 134 | for i in x 135 | t = 1 136 | end 137 | print(t) 138 | else 139 | t = 4 140 | end 141 | print(t) 142 | end 143 | 144 | function f18(x::Int)::Union{Int, String} 145 | if x == 1 146 | z = 1 147 | else 148 | y = "" 149 | end 150 | end 151 | 152 | function f19(y::Bool) 153 | local x::Union{Int, Float64} 154 | if y 155 | x = 1 156 | else 157 | x = 1.0 158 | end 159 | return x 160 | end 161 | 162 | function f20(y::Bool) 163 | local x::Union{Int, Float64} 164 | if y 165 | x = 1 166 | else 167 | x = 1.0 168 | end 169 | print(x) 170 | end 171 | 172 | function f21() 173 | x = 1.0 174 | for i in 1:10 175 | x = 1 176 | print(x) 177 | end 178 | end 179 | 180 | function f22() 181 | x = 1.0 182 | for i in 1:10 183 | local x = 1 184 | print(x) 185 | end 186 | end 187 | 188 | function f23() 189 | local x 190 | print(x) 191 | end 192 | 193 | function f23(y::Bool) 194 | local x 195 | if y 196 | x = 1 197 | end 198 | print(x) 199 | end 200 | 201 | function f24(y::Bool) 202 | # our inferencer can't handle this correctly... 203 | local x::Int 204 | if y 205 | x = 1 206 | end 207 | print(x) 208 | end 209 | 210 | function f25(y::Bool) 211 | local x::Int 212 | if y 213 | x = 1 214 | else 215 | x = 2 216 | end 217 | print(x) 218 | end 219 | 220 | function f26() 221 | local y::Int 222 | for i in 1:10 223 | y = 1 224 | end 225 | print(y) 226 | end 227 | 228 | function f27() 229 | local y::Int 230 | for i in 1:10 231 | y = 1 232 | if y > 0 233 | z = 1 234 | end 235 | print(z) 236 | end 237 | print(y) 238 | end 239 | 240 | function f28() 241 | local y::Int 242 | y = 2 243 | for i in 1:10 244 | y = 1 245 | if y > 0 246 | z = 1 247 | end 248 | z = 3 249 | print(z) 250 | end 251 | print(y) 252 | end 253 | 254 | function f29(y::Bool) 255 | if y 256 | return 1 257 | end 258 | return 2 259 | end 260 | 261 | function f30() 262 | x = 1 263 | for i in 1:10 264 | println(i) 265 | x = 3 266 | end 267 | println(Vector{Int, x}) 268 | end 269 | 270 | function f31() 271 | x = 1 272 | for i in 1:10 273 | println(i) 274 | x = 1 275 | end 276 | println(Array{Int, x}) 277 | end 278 | 279 | 280 | function f32() 281 | (x,y) = (1,2) 282 | print((2,3)) 283 | end 284 | 285 | function f33(x::Int) 286 | x += 1 287 | end 288 | 289 | function f34(x::Int) 290 | x += 1.0 291 | end 292 | 293 | function f35(x::Float64) 294 | x += 1 295 | end 296 | 297 | function f36(z::Float64) 298 | local x::Int = 1 299 | y::Int = 3 300 | end 301 | 302 | # disallowed 303 | function f37(z::Float64) 304 | local x::Int = 1.0 305 | end 306 | 307 | function f38() 308 | xref = Base.RefValue{Union{Float64, Int, Float32}}(1) 309 | x = xref[] 310 | if x isa Int 311 | println(x) 312 | elseif x isa Float32 313 | println(x) 314 | else 315 | println(x) 316 | end 317 | end 318 | 319 | function f39() 320 | xref = Base.RefValue{Union{Float64, Int, Float32}}(1) 321 | x = xref[] 322 | if x isa Int 323 | println(x) 324 | elseif x isa Float32 325 | println(x) 326 | else 327 | println(x) 328 | end 329 | end 330 | 331 | function f40(::Type{Vector{Int}}) 332 | return 1 333 | end 334 | 335 | function f41(y::Int) 336 | local x::Union{Int, Nothing} 337 | if y > 0 338 | x = 1 339 | else 340 | x = nothing 341 | end 342 | return x === nothing 343 | end 344 | 345 | function f42(y::Vector{Int}) 346 | return y.+1 347 | end 348 | 349 | function f43(y::Vector{Int}) 350 | y .+= 3 351 | y .= y.+y 352 | println(y[1]) 353 | end 354 | 355 | struct A 356 | x::Vector{Int} 357 | end 358 | 359 | function f44(y::Vector{Int}) 360 | y .+= 3 361 | y .= y.+y 362 | println(y[1]) 363 | end 364 | 365 | function f45(y::Vector{Int}) 366 | y .+= 3 367 | 368 | y .= y 369 | y .= 1 370 | y[1:3] .= 1 371 | end 372 | 373 | function f45(y::Vector{Vector{Int}}) 374 | y[1] .= y[2] 375 | 376 | end 377 | function optional_test(x::Vector{Int}) 378 | sort(x, by = identity) 379 | sort(x; by = identity) 380 | end 381 | 382 | end -------------------------------------------------------------------------------- /test/case/casetest.jl: -------------------------------------------------------------------------------- 1 | module MyTest 2 | 3 | include("../../src/SimpleTypeChecker.jl") 4 | include("case.jl") 5 | testpath = abspath(joinpath(@__DIR__, "case.jl")) 6 | ctx = SimpleTypeChecker.Inference.GlobalContext() 7 | SimpleTypeChecker.API.addFile!(ctx, CaseTest, testpath) 8 | SimpleTypeChecker.API.runCheck!(ctx) 9 | 10 | end -------------------------------------------------------------------------------- /test/case/scope.jl: -------------------------------------------------------------------------------- 1 | module ScopeTest 2 | function f() 3 | local x 4 | local y::Int 5 | local z::Int = 1 6 | 7 | y = 1 8 | x = 1 9 | println(x, y, z) 10 | end 11 | 12 | function inorder_test() 13 | x = 1 14 | local x 15 | return 16 | end 17 | 18 | function inner_inorder_test() 19 | for i in 1:10 20 | t = 1 21 | end 22 | local t 23 | print(t) 24 | end 25 | 26 | function double_declare_test() 27 | local x 28 | local x 29 | end 30 | 31 | end -------------------------------------------------------------------------------- /test/eval.jl: -------------------------------------------------------------------------------- 1 | 2 | #= 3 | Core.eval(MyTest.Wrapper, 4 | quote 5 | const I = SimpleTypeChecker.Inference 6 | function refff(x::I.FlowNode) 7 | vv = x.typ 8 | if vv.isConst 9 | return "Const($(repr(vv.val)))" 10 | else 11 | return repr(vv.val) 12 | end 13 | end 14 | function ff(ast::I.JuAST, node)::String 15 | locstr = I.formatLocation(ast.loc) 16 | str = "

$locstr

" 17 | if !(node isa Nothing) 18 | str *= "

Inferred:

$(refff(node[1]))

" 19 | end 20 | return str 21 | end 22 | let 23 | ctx = I.GlobalContext() 24 | mi = which(sin, (Float64,)).specializations[1] 25 | mod = Base 26 | e = I.Engine(ctx, mi, mod) 27 | c = I.Context() 28 | str = """ 29 | begin 30 | x = 1 31 | y = 2 32 | z = (x, y) 33 | x += 1 34 | l = 'a' 35 | k = :x 36 | o = quote a end 37 | p = Vector{Int}(undef, 1) 38 | sort(p;rev=true) 39 | w = Vector{Union{Int, Nothing}}()[1] 40 | if w isa Int 41 | print(w) 42 | k = 1 43 | else 44 | error() 45 | end 46 | p[1] += 1 47 | p[1] += "x" 48 | (x1, (x2, x3)) = (3,(4,'a')) 49 | x1 = 'a' 50 | x1 = 1.0 51 | end 52 | """ 53 | ast = SimpleTypeChecker.SyntaxAdaptor.parseAndConstructHTML(str, "none", joinpath(@__DIR__, "../src/www/")).args[1] 54 | try 55 | r = I.inferExpr(e, c, ast) 56 | I.displayContext(stdout, r.ctx) 57 | f = SimpleTypeChecker.Server.QueryFunctor(ff, ast, e.flowMapping) 58 | SimpleTypeChecker.Server.handleQuery(f) 59 | catch e 60 | if e isa I.InferenceError 61 | println("────────────────────────────────────────────────────────────────") 62 | print(String(take!(ctx.errio.io))) 63 | println("────────────────────────────────────────────────────────────────") 64 | elseif e isa I.SyntaxError 65 | println(e.msg) 66 | else 67 | rethrow(e) 68 | end 69 | end 70 | 71 | end 72 | end 73 | ) 74 | 75 | =# 76 | 77 | #= 78 | Core.eval(MyTest.Wrapper, 79 | quote 80 | include("test.jl") 81 | ctx = SimpleTypeChecker.Inference.GlobalContext() 82 | SimpleTypeChecker.Inference.addFile!(ctx, TTest, abspath("test/test.jl")) 83 | SimpleTypeChecker.Inference.runCheck!(ctx) 84 | for (k,v) in ctx.hasChecked 85 | if v isa SimpleTypeChecker.Inference.InferenceError 86 | z = split(string(typeof(v)), '.')[end] 87 | x = z[length("InferenceError")+1:end] 88 | x = "displayError" * x 89 | f = getproperty(SimpleTypeChecker.Inference, Symbol(x)) 90 | f(v) 91 | end 92 | end 93 | println(String(take!(ctx.errio.io))) 94 | end 95 | ) 96 | =# 97 | #= 98 | Core.eval(MyTest.Wrapper, 99 | quote 100 | include("test.jl") 101 | ctx = SimpleTypeChecker.Inference.GlobalContext() 102 | SimpleTypeChecker.Inference.addFile!(ctx, TTest, abspath("test/test.jl")) 103 | for (meth, def) in ctx.methodDefs 104 | local sictx 105 | try 106 | sictx = SimpleTypeChecker.Inference.analyzeScopeVariable(ctx, def) 107 | catch e 108 | println(String(take!(ctx.errio.io))) 109 | rethrow(e) 110 | end 111 | println("For method : ", meth) 112 | for (_, info) in sort(collect(sictx.infos);by = x->x.second.parent.loc.span) 113 | println(info) 114 | end 115 | println("────────────────────────────────────────────────────────────────") 116 | end 117 | 118 | SimpleTypeChecker.Inference.runCheck!(ctx) 119 | end 120 | ) 121 | =# 122 | module MyTest 123 | include("../src/SimpleTypeChecker.jl") 124 | end 125 | Core.eval(MyTest, 126 | quote 127 | ctx = SimpleTypeChecker.Inference.GlobalContext() 128 | const path = abspath(joinpath(@__DIR__, "..")) 129 | SimpleTypeChecker.Inference.addFile!(ctx, SimpleTypeChecker.SyntaxAdaptor, joinpath(path, "src/adaptor/JSAdaptor.jl")) 130 | SimpleTypeChecker.Inference.addFile!(ctx, SimpleTypeChecker.SyntaxAdaptor, joinpath(path, "src/adaptor/TreeQuery.jl")) 131 | SimpleTypeChecker.Inference.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceDefinition.jl")) 132 | SimpleTypeChecker.Inference.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/Inference.jl")) 133 | SimpleTypeChecker.Inference.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceErrorUtility.jl")) 134 | SimpleTypeChecker.Inference.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceError.jl")) 135 | SimpleTypeChecker.Inference.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/ScopeChecking.jl")) 136 | SimpleTypeChecker.Inference.addFile!(ctx, SimpleTypeChecker.Server, joinpath(path, "src/server/SimpleHTTPServer.jl")) 137 | SimpleTypeChecker.Inference.runCheck!(ctx) 138 | #= 139 | for (meth, def) in ctx.methodDefs 140 | local sictx 141 | try 142 | sictx = SimpleTypeChecker.Inference.analyzeScopeVariable(ctx, def) 143 | catch e 144 | println(String(take!(ctx.errio.io))) 145 | rethrow(e) 146 | end 147 | println("For method : ", meth) 148 | for (_, info) in sort(collect(sictx.infos);by = x->x.second.parent.loc.span) 149 | println(info) 150 | end 151 | println("────────────────────────────────────────────────────────────────") 152 | end 153 | 154 | SimpleTypeChecker.Inference.runCheck!(ctx) 155 | =# 156 | end 157 | ) 158 | 159 | 160 | Core.eval(MyTest, quote 161 | ctx = SimpleTypeChecker.Inference.GlobalContext() 162 | const path = abspath(joinpath(@__DIR__, "..")) 163 | include(joinpath(path, "test/case/scope.jl")) 164 | SimpleTypeChecker.Inference.addFile!(ctx, ScopeTest, joinpath(path, "test/case/scope.jl")) 165 | SimpleTypeChecker.Inference.runCheck!(ctx) 166 | end) -------------------------------------------------------------------------------- /test/refactor.jl: -------------------------------------------------------------------------------- 1 | module MyTest 2 | 3 | import SimpleTypeChecker 4 | 5 | module Wrapper 6 | include("../src/SimpleTypeChecker.jl") 7 | end 8 | 9 | const path = abspath(".") 10 | ctx = SimpleTypeChecker.Inference.GlobalContext() 11 | SimpleTypeChecker.API.addFile!(ctx, Wrapper.SimpleTypeChecker.SyntaxDefinition, joinpath(path, "src/adaptor/SyntaxDefinition.jl")) 12 | SimpleTypeChecker.API.addFile!(ctx, Wrapper.SimpleTypeChecker.SyntaxAdaptor, joinpath(path, "src/adaptor/SyntaxAdaptor.jl")) 13 | SimpleTypeChecker.API.addFile!(ctx, Wrapper.SimpleTypeChecker.SyntaxAdaptor, joinpath(path, "src/adaptor/JSAdaptor.jl")) 14 | SimpleTypeChecker.API.addFile!(ctx, Wrapper.SimpleTypeChecker.SyntaxAdaptor, joinpath(path, "src/adaptor/TreeQuery.jl")) 15 | SimpleTypeChecker.API.addFile!(ctx, Wrapper.SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceDefinition.jl")) 16 | SimpleTypeChecker.API.addFile!(ctx, Wrapper.SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/Inference.jl")) 17 | SimpleTypeChecker.API.addFile!(ctx, Wrapper.SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceErrorUtility.jl")) 18 | SimpleTypeChecker.API.addFile!(ctx, Wrapper.SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceError.jl")) 19 | SimpleTypeChecker.API.addFile!(ctx, Wrapper.SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/ScopeChecking.jl")) 20 | SimpleTypeChecker.API.addFile!(ctx, Wrapper.SimpleTypeChecker.Server, joinpath(path, "src/server/SimpleHTTPServer.jl")) 21 | SimpleTypeChecker.API.runCheck!(ctx) 22 | end 23 | #include("eval.jl") -------------------------------------------------------------------------------- /test/runtests.jl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChenNingCong/SimpleTypeChecker/9222046080acd52f53e3a72fa7807c90c8e057cf/test/runtests.jl -------------------------------------------------------------------------------- /test/self/macro.jl: -------------------------------------------------------------------------------- 1 | Core.eval(SimpleTypeChecker.Inference, quote 2 | function expander_view(eng::Engine, ctx::Context, ast::JuAST)::InferResult 3 | args = FlowNode[] 4 | for i in ast.args 5 | rel = inferExpr(eng, ctx, i) 6 | ctx = rel.ctx 7 | push!(args, rel.node) 8 | end 9 | ms = MethodCallStruct(makeLiteralFlowNode(ast, makeConstJuASTVal(Base.maybeview)), args) 10 | mm = getMethodMatches(eng, ms) 11 | if length(mm) >= 2 || length(mm) == 0 12 | # TODO : report mismatched method table here 13 | reportErrorFunCall(eng, ast, ms, length(mm), false) 14 | end 15 | tt = extractUniqueMatch(mm) 16 | if isBottomType(tt) 17 | reportErrorBroadcastBottom(eng, ast, ms) 18 | end 19 | node = makeFunCallFlowNode(ast, ms, tt) 20 | addFlowMapping!(eng, ast, node) 21 | return InferResult(ctx, node) 22 | end 23 | 24 | function expander_nocheck(eng::Engine, ctx::Context, ast::JuAST)::InferResult 25 | InferResult(ctx, makeLiteralFlowNode(ast, makeConstJuASTVal(nothing))) 26 | end 27 | 28 | function expander_inline(eng::Engine, ctx::Context, ast::JuAST)::InferResult 29 | return inferExpr(ast.args[1]) 30 | end 31 | 32 | addGlobalExpander!(Base, Symbol("@view"), expander_view) 33 | addGlobalExpander!(Base, Symbol("@inline"), expander_inline) 34 | addGlobalExpander!(@__MODULE__, Symbol("@nocheck"), expander_nocheck) 35 | 36 | end) -------------------------------------------------------------------------------- /test/self/selftest.jl: -------------------------------------------------------------------------------- 1 | module MyTest 2 | 3 | include("../../src/SimpleTypeChecker.jl") 4 | include("macro.jl") 5 | const path = abspath(".") 6 | ctx = SimpleTypeChecker.Inference.GlobalContext() 7 | SimpleTypeChecker.Inference.getGlobalExpander(ctx) 8 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.SyntaxDefinition, joinpath(path, "src/adaptor/SyntaxDefinition.jl")) 9 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.SyntaxAdaptor, joinpath(path, "src/adaptor/SyntaxAdaptor.jl")) 10 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.SyntaxAdaptor, joinpath(path, "src/adaptor/JSAdaptor.jl")) 11 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.SyntaxAdaptor, joinpath(path, "src/adaptor/TreeQuery.jl")) 12 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceDefinition.jl")) 13 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/Inference.jl")) 14 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceErrorUtility.jl")) 15 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/InferenceError.jl")) 16 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Inference, joinpath(path, "src/adaptor/ScopeChecking.jl")) 17 | SimpleTypeChecker.API.addFile!(ctx, SimpleTypeChecker.Server, joinpath(path, "src/server/SimpleHTTPServer.jl")) 18 | SimpleTypeChecker.API.runCheck!(ctx) 19 | 20 | end -------------------------------------------------------------------------------- /test/watch.jl: -------------------------------------------------------------------------------- 1 | import Dates 2 | if !isdefined(Main, :modtime) 3 | modtime = nothing 4 | global function watchDirectory(path::String) 5 | maxtime::Float64 = 0.0 6 | for (root, dirs, files) in walkdir(path) 7 | for file in files 8 | maxtime = max(mtime(joinpath(root, file)), maxtime) 9 | end 10 | end 11 | return Dates.unix2datetime(maxtime) 12 | end 13 | end 14 | newtime = watchDirectory(joinpath(@__DIR__, "../src/adaptor")) 15 | if modtime isa Nothing || modtime != newtime 16 | println("Update code $modtime $newtime") 17 | modtime = newtime 18 | include("eval.jl") 19 | end 20 | --------------------------------------------------------------------------------