3 local table = require('table')
5 package
.path
= '../lua/?.lua;' .. package
.path
6 local readline
= require('readline')
7 local utils
= require('utils')
8 local types
= require('types')
9 local reader
= require('reader')
10 local printer
= require('printer')
11 local Env
= require('env')
12 local core
= require('core')
13 local List
, Vector
, HashMap
= types
.List
, types
.Vector
, types
.HashMap
17 return reader
.read_str(str
)
22 return types
._sequential_Q(x
) and #x
> 0
25 function quasiquote(ast
)
26 if not is_pair(ast
) then
27 return types
.List
:new({types
.Symbol
:new("quote"), ast
})
28 elseif types
._symbol_Q(ast
[1]) and ast
[1].val
== 'unquote' then
30 elseif is_pair(ast
[1]) and
31 types
._symbol_Q(ast
[1][1]) and
32 ast
[1][1].val
== 'splice-unquote' then
33 return types
.List
:new({types
.Symbol
:new("concat"),
35 quasiquote(ast
:slice(2))})
37 return types
.List
:new({types
.Symbol
:new("cons"),
39 quasiquote(ast
:slice(2))})
43 function is_macro_call(ast
, env
)
44 if types
._list_Q(ast
) and
45 types
._symbol_Q(ast
[1]) and
47 local f
= env
:get(ast
[1])
48 return types
._malfunc_Q(f
) and f
.ismacro
52 function macroexpand(ast
, env
)
53 while is_macro_call(ast
, env
) do
54 local mac
= env
:get(ast
[1])
55 ast
= mac
.fn(table.unpack(ast
:slice(2)))
60 function eval_ast(ast
, env
)
61 if types
._symbol_Q(ast
) then
63 elseif types
._list_Q(ast
) then
64 return List
:new(utils
.map(function(x
) return EVAL(x
,env
) end,ast
))
65 elseif types
._vector_Q(ast
) then
66 return Vector
:new(utils
.map(function(x
) return EVAL(x
,env
) end,ast
))
67 elseif types
._hash_map_Q(ast
) then
69 for k
,v
in pairs(ast
) do
70 new_hm
[EVAL(k
, env
)] = EVAL(v
, env
)
72 return HashMap
:new(new_hm
)
78 function EVAL(ast
, env
)
80 --print("EVAL: "..printer._pr_str(ast,true))
81 if not types
._list_Q(ast
) then return eval_ast(ast
, env
) end
84 ast
= macroexpand(ast
, env
)
85 if not types
._list_Q(ast
) then return eval_ast(ast
, env
) end
87 local a0
,a1
,a2
,a3
= ast
[1], ast
[2],ast
[3],ast
[4]
88 if not a0
then return ast
end
89 local a0sym
= types
._symbol_Q(a0
) and a0
.val
or ""
90 if 'def!' == a0sym
then
91 return env
:set(a1
, EVAL(a2
, env
))
92 elseif 'let*' == a0sym
then
93 local let_env
= Env
:new(env
)
95 let_env
:set(a1
[i
], EVAL(a1
[i
+1], let_env
))
99 elseif 'quote' == a0sym
then
101 elseif 'quasiquote' == a0sym
then
102 ast
= quasiquote(a1
) -- TCO
103 elseif 'defmacro!' == a0sym
then
104 local mac
= EVAL(a2
, env
)
106 return env
:set(a1
, mac
)
107 elseif 'macroexpand' == a0sym
then
108 return macroexpand(a1
, env
)
109 elseif 'try*' == a0sym
then
110 local exc
, result
= nil, nil
112 result
= EVAL(a1
, env
)
117 if types
._malexception_Q(exc
) then
120 if a2
and a2
[1].val
== 'catch*' then
121 result
= EVAL(a2
[3], Env
:new(env
, {a2
[2]}, {exc
}))
127 elseif 'do' == a0sym
then
128 local el
= eval_ast(ast
:slice(2,#ast
-1), env
)
129 ast
= ast
[#ast
] -- TCO
130 elseif 'if' == a0sym
then
131 local cond
= EVAL(a1
, env
)
132 if cond
== types
.Nil
or cond
== false then
133 if #ast
> 3 then ast
= a3
else return types
.Nil
end -- TCO
137 elseif 'fn*' == a0sym
then
138 return types
.MalFunc
:new(function(...)
139 return EVAL(a2
, Env
:new(env
, a1
, table.pack(...)))
142 local args
= eval_ast(ast
, env
)
143 local f
= table.remove(args
, 1)
144 if types
._malfunc_Q(f
) then
146 env
= Env
:new(f
.env
, f
.params
, args
) -- TCO
148 return f(table.unpack(args
))
156 return printer
._pr_str(exp, true)
160 local repl_env
= Env
:new()
162 return PRINT(EVAL(READ(str
),repl_env
))
165 -- core.lua: defined using Lua
166 for k
,v
in pairs(core
.ns
) do
167 repl_env
:set(types
.Symbol
:new(k
), v
)
169 repl_env
:set(types
.Symbol
:new('eval'),
170 function(ast
) return EVAL(ast
, repl_env
) end)
171 repl_env
:set(types
.Symbol
:new('*ARGV*'), types
.List
:new(types
.slice(arg
,2)))
173 -- core.mal: defined using mal
174 rep("(def! *host-language* \"lua\")")
175 rep("(def! not (fn* (a) (if a false true)))")
176 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
177 rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
178 rep("(def! inc (fn* [x] (+ x 1)))")
179 rep("(def! gensym (let* [counter (atom 0)] (fn* [] (symbol (str \"G__\" (swap! counter inc))))))")
180 rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) (let* (condvar (gensym)) `(let* (~condvar ~(first xs)) (if ~condvar ~condvar (or ~@(rest xs)))))))))")
182 function print_exception(exc
)
184 if types
._malexception_Q(exc
) then
185 exc
= printer
._pr_str(exc
.val
, true)
187 print("Error: " .. exc
)
188 print(debug
.traceback())
192 if #arg
> 0 and arg
[1] == "--raw" then
198 xpcall(function() rep("(load-file \""..arg
[1].."\")") end,
203 rep("(println (str \"Mal [\" *host-language* \"]\"))")
205 line
= readline
.readline("user> ")
206 if not line
then break end
207 xpcall(function() print(rep(line
)) end,