github.com/coyove/nj@v0.0.0-20221110084952-c7f8db1065c3/tests/eval.nj.lua (about) 1 local env = createprototype("env", function(p) 2 this.store = {} 3 this.funcs = {} 4 this.top = if(p, p.top, this) 5 this.parent = p 6 end) 7 8 env._break = [] 9 env._continue = [] 10 env._return = {} 11 12 function env.set(k, v) 13 this.store[k] = v 14 end 15 16 function env.setp(k, v) 17 if this.store.hasownproperty(k) then 18 this.store[k] = v 19 return true 20 end 21 if this.parent then 22 if this.parent.setp(k, v) then 23 return false 24 end 25 end 26 this.store[k] = v 27 return true 28 end 29 30 function env.get(k) 31 if k == "nil" then return nil, true end 32 if k == "true" then return true, true end 33 if k == "false" then return false, true end 34 local v = this.store[k:self] 35 if v != self then 36 return v, true 37 end 38 if this.parent then 39 return this.parent.get(k) 40 end 41 if this.top == this then 42 local v = this.funcs[k:self] 43 if v != self then 44 return v, true 45 end 46 end 47 return nil, false 48 end 49 50 function runUnary(node, e) 51 if node.Op == eval.op.ret then 52 panic(new(env._return, {result=run(node.A, e)})) 53 end 54 end 55 56 function runBinary(node, e) 57 if node.Op == eval.op.add then 58 return run(node.A, e) + run(node.B, e) 59 elseif node.Op == eval.op.sub then 60 return run(node.A, e) - run(node.B, e) 61 elseif node.Op == eval.op.less then 62 return run(node.A, e) < run(node.B, e) 63 elseif node.Op == eval.op.inc then 64 local res = run(node.A, e) + run(node.B, e) 65 e.setp(node.A.Name, res) 66 return res 67 end 68 end 69 70 runners = {} 71 72 function run(node, e) 73 local run = runners[node.typename()] 74 if run then return run(node, e) end 75 local tmp = buffer() 76 node.Dump(tmp) 77 panic("unknown node: %v".format(tmp.value())) 78 end 79 80 runners["*parser.Prog"] = function(node, e) 81 local e2 = if(node.DoBlock, env(e), e) 82 local last 83 for _, stat in node.Stats do 84 last = run(stat, e2) 85 if last == env._break or last == env._continue then return last end 86 end 87 return last 88 end 89 runners["*parser.If"] = function(node, e) 90 if run(node.Cond, e) then 91 res = run(node.True, env(e)) 92 elseif node.False then 93 res = run(node.False, env(e)) 94 end 95 return res 96 end 97 runners["*parser.Loop"] = function(node, e) 98 while true do 99 local res = run(node.Body, e) 100 if res == env._break then break end 101 if res == env._continue then 102 run(node.Continue, e) 103 end 104 end 105 end 106 runners["*parser.And"] = function(node, e) 107 return run(node.A, e) and run(node.B, e) 108 end 109 runners["*parser.Or"] = function(node, e) 110 return run(node.A, e) or run(node.B, e) 111 end 112 runners["*parser.Declare"] = function(node, e) 113 e.set(node.Name.Name, run(node.Value, e)) 114 end 115 runners["*parser.Assign"] = function(node, e) 116 e.setp(node.Name.Name, run(node.Value, e)) 117 end 118 runners["*parser.Release"] = function(node, e) 119 -- omit 120 end 121 runners["*parser.LoadConst"] = function(node, e) 122 for f in node.natptrat("Funcs") do e.top.funcs[f] = nil end 123 end 124 runners["parser.Primitive"] = function(node, e) 125 return node.Value() 126 end 127 runners["*parser.Binary"] = function(node, e) 128 return runBinary(node, e) 129 end 130 runners["*parser.Unary"] = function(node, e) 131 return runUnary(node, e) 132 end 133 runners["*parser.BreakContinue"] = function(node, e) 134 return if(node.Break, env._break, env._continue) 135 end 136 runners["*parser.Symbol"] = function(node, e) 137 local v, ok = e.get(node.Name) 138 assert(ok, true, "unknown symbol: %v".format(node.Name)) 139 return v 140 end 141 runners["parser.ExprList"] = function(node, e) 142 lst = [] 143 for _, n in node do 144 lst[#lst] = run(n, e) 145 end 146 return lst 147 end 148 runners["parser.ExprAssignList"] = function(node, e) 149 lst = {} 150 for _, n in node do 151 lst[run(n[0], e)] = run(n[1], e) 152 end 153 return lst 154 end 155 runners["*parser.Call"] = function(node, e) 156 local callee = run(node.Callee, e) 157 if callee is callable then 158 return callee(run(node.Args, e)...) 159 end 160 local e2 = env(e.top) 161 for i, name in callee.Args do 162 e2.set(name.Name, if(i < #node.Args, run(node.Args[i], e), nil)) 163 end 164 local res = run.try(callee.Body, e2) 165 if res is error and res.error() is env._return then 166 return res.error().result 167 end 168 return res 169 end 170 runners["*parser.Function"] = function(node, e) 171 e.top.funcs[node.Name] = node 172 e.store[node.Name] = node 173 return node 174 end 175 176 function newenv(a) 177 local e = env(nil) 178 e.store.merge(globals()) 179 e.store.merge(a) 180 return e 181 end 182 183 function loadstring(expr, e) 184 res = run.try(eval.parse(expr), e) 185 if res is error and res.error() is env._return then 186 return res.error().result 187 end 188 return res 189 end 190 191 local N=1e3 192 local res = loadstring("a=0 for i=1,%v+1 do a+=i end a".format(N), newenv(nil)) 193 assert(res, N*(N+1)/2) 194 195 start = time() 196 code = [[ 197 function fib(n) 198 if n < 2 then return n end 199 return fib(n - 1) + fib(n-2) 200 end 201 return (fib(N)) 202 ]] 203 res = loadstring(code, newenv({N=20})) 204 assert(res, eval(code, {N=20})) 205 print(time() - start, ' res=', res)