Commit | Line | Data |
---|---|---|
9d42904e JM |
1 | #!/usr/bin/env lua |
2 | ||
3 | local table = require('table') | |
4 | ||
5 | local readline = require('readline') | |
6 | local utils = require('utils') | |
7 | local types = require('types') | |
8 | local reader = require('reader') | |
9 | local printer = require('printer') | |
10 | local Env = require('env') | |
11 | local core = require('core') | |
12 | local List, Vector, HashMap = types.List, types.Vector, types.HashMap | |
13 | ||
14 | -- read | |
15 | function READ(str) | |
16 | return reader.read_str(str) | |
17 | end | |
18 | ||
19 | -- eval | |
20 | function eval_ast(ast, env) | |
21 | if types._symbol_Q(ast) then | |
22 | return env:get(ast) | |
23 | elseif types._list_Q(ast) then | |
24 | return List:new(utils.map(function(x) return EVAL(x,env) end,ast)) | |
25 | elseif types._vector_Q(ast) then | |
26 | return Vector:new(utils.map(function(x) return EVAL(x,env) end,ast)) | |
27 | elseif types._hash_map_Q(ast) then | |
28 | local new_hm = {} | |
29 | for k,v in pairs(ast) do | |
30 | new_hm[EVAL(k, env)] = EVAL(v, env) | |
31 | end | |
32 | return HashMap:new(new_hm) | |
33 | else | |
34 | return ast | |
35 | end | |
36 | end | |
37 | ||
38 | function EVAL(ast, env) | |
39 | while true do | |
40 | --print("EVAL: "..printer._pr_str(ast,true)) | |
41 | if not types._list_Q(ast) then return eval_ast(ast, env) end | |
42 | ||
43 | local a0,a1,a2,a3 = ast[1], ast[2],ast[3],ast[4] | |
3c59a767 | 44 | if not a0 then return ast end |
9d42904e JM |
45 | local a0sym = types._symbol_Q(a0) and a0.val or "" |
46 | if 'def!' == a0sym then | |
47 | return env:set(a1, EVAL(a2, env)) | |
48 | elseif 'let*' == a0sym then | |
49 | local let_env = Env:new(env) | |
50 | for i = 1,#a1,2 do | |
51 | let_env:set(a1[i], EVAL(a1[i+1], let_env)) | |
52 | end | |
53 | env = let_env | |
54 | ast = a2 -- TCO | |
55 | elseif 'do' == a0sym then | |
56 | local el = eval_ast(ast:slice(2,#ast-1), env) | |
57 | ast = ast[#ast] -- TCO | |
58 | elseif 'if' == a0sym then | |
59 | local cond = EVAL(a1, env) | |
60 | if cond == types.Nil or cond == false then | |
a68c26af | 61 | if #ast > 3 then ast = a3 else return types.Nil end -- TCO |
9d42904e JM |
62 | else |
63 | ast = a2 -- TCO | |
64 | end | |
65 | elseif 'fn*' == a0sym then | |
66 | return types.MalFunc:new(function(...) | |
a68c26af | 67 | return EVAL(a2, Env:new(env, a1, table.pack(...))) |
9d42904e JM |
68 | end, a2, env, a1) |
69 | else | |
70 | local args = eval_ast(ast, env) | |
71 | local f = table.remove(args, 1) | |
72 | if types._malfunc_Q(f) then | |
73 | ast = f.ast | |
74 | env = Env:new(f.env, f.params, args) -- TCO | |
75 | else | |
a68c26af | 76 | return f(table.unpack(args)) |
9d42904e JM |
77 | end |
78 | end | |
79 | end | |
80 | end | |
81 | ||
82 | ||
83 | function PRINT(exp) | |
84 | return printer._pr_str(exp, true) | |
85 | end | |
86 | ||
87 | -- repl | |
88 | local repl_env = Env:new() | |
89 | function rep(str) | |
90 | return PRINT(EVAL(READ(str),repl_env)) | |
91 | end | |
92 | ||
93 | -- core.lua: defined using Lua | |
94 | for k,v in pairs(core.ns) do | |
95 | repl_env:set(types.Symbol:new(k), v) | |
96 | end | |
97 | ||
98 | -- core.mal: defined using mal | |
99 | rep("(def! not (fn* (a) (if a false true)))") | |
100 | ||
3e0b36dc JM |
101 | if #arg > 0 and arg[1] == "--raw" then |
102 | readline.raw = true | |
103 | end | |
104 | ||
9d42904e JM |
105 | while true do |
106 | line = readline.readline("user> ") | |
107 | if not line then break end | |
108 | xpcall(function() | |
109 | print(rep(line)) | |
110 | end, function(exc) | |
111 | if exc then | |
112 | if types._malexception_Q(exc) then | |
113 | exc = printer._pr_str(exc.val, true) | |
114 | end | |
115 | print("Error: " .. exc) | |
116 | print(debug.traceback()) | |
117 | end | |
118 | end) | |
119 | end |