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)