forked from kanaka/mal
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstep9_try.io
154 lines (136 loc) · 5.05 KB
/
step9_try.io
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
MalTypes
MalReader
READ := method(str, MalReader read_str(str))
isPair := method(obj,
obj ?isSequential and(obj isEmpty not)
)
quasiquote := method(ast,
if(isPair(ast) not, return(MalList with(list(MalSymbol with("quote"), ast))))
a0 := ast at(0)
if(a0 == MalSymbol with("unquote"), return(ast at(1)))
if(isPair(a0) and (a0 at(0) == MalSymbol with("splice-unquote")),
return(MalList with(list(MalSymbol with("concat"), a0 at(1), quasiquote(ast rest)))),
return(MalList with(list(MalSymbol with("cons"), quasiquote(a0), quasiquote(ast rest)))))
)
isMacroCall := method(ast, env,
if(ast type != "MalList", return false)
a0 := ast first
if(a0 type != "MalSymbol", return false)
if(env find(a0) isNil, return false)
f := env get(a0)
(f type == "MalFunc") and (f isMacro)
)
macroexpand := method(ast, env,
while(isMacroCall(ast, env),
macro := env get(ast at(0))
ast = macro blk call(ast rest)
)
ast
)
eval_ast := method(ast, env,
(ast type) switch(
"MalSymbol", env get(ast),
"MalList", MalList with(ast map(a, EVAL(a, env))),
"MalVector", MalVector with(ast map(a, EVAL(a, env))),
"MalMap",
m := MalMap clone
ast foreach(k, v,
keyObj := MalMap keyToObj(k)
m atPut(MalMap objToKey(EVAL(keyObj, env)), EVAL(v, env))
)
m,
ast
)
)
EVAL := method(ast, env,
loop(
if(ast type != "MalList", return(eval_ast(ast, env)))
ast = macroexpand(ast, env)
if(ast type != "MalList", return(eval_ast(ast, env)))
if(ast isEmpty, return ast)
if(ast at(0) type == "MalSymbol",
ast at(0) val switch(
"def!",
return(env set(ast at(1), EVAL(ast at(2), env))),
"do",
eval_ast(ast slice(1,-1), env)
ast = ast last
continue, // TCO
"if",
ast = if(EVAL(ast at(1), env), ast at(2), ast at(3))
continue, // TCO
"fn*",
return(MalFunc with(ast at(2), ast at(1), env, block(a, EVAL(ast at(2), Env with(env, ast at(1), a))))),
"let*",
letEnv := Env with(env)
varName := nil
ast at(1) foreach(i, e,
if(i % 2 == 0,
varName := e,
letEnv set(varName, EVAL(e, letEnv))
)
)
ast = ast at(2)
env = letEnv
continue, // TCO
"quote",
return(ast at(1)),
"quasiquote",
ast = quasiquote(ast at(1))
continue, // TCO
"defmacro!",
return(env set(ast at(1), EVAL(ast at(2), env) setIsMacro(true))),
"macroexpand",
return(macroexpand(ast at(1), env)),
"try*",
e := try(result := EVAL(ast at(1), env))
e catch(Exception,
exc := if(e type == "MalException", e val, e error)
catchAst := ast at(2)
catchEnv := Env with(env)
catchEnv set(catchAst at(1), exc)
result := EVAL(catchAst at(2), catchEnv)
)
return(result)
)
)
// Apply
el := eval_ast(ast, env)
f := el at(0)
args := el rest
f type switch(
"Block",
return(f call(args)),
"MalFunc",
ast = f ast
env = Env with(f env, f params, args)
continue, // TCO
Exception raise("Unknown function type")
)
)
)
PRINT := method(exp, exp malPrint(true))
repl_env := Env with(nil)
RE := method(str, EVAL(READ(str), repl_env))
REP := method(str, PRINT(RE(str)))
MalCore NS foreach(k, v, repl_env set(MalSymbol with(k), v))
repl_env set(MalSymbol with("eval"), block(a, EVAL(a at(0), repl_env)))
repl_env set(MalSymbol with("*ARGV*"), MalList with(System args slice(2)))
// core.mal: defined using the language itself
RE("(def! not (fn* (a) (if a false true)))")
RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
RE("(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)))))))")
RE("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
if(System args size > 1,
REP("(load-file \"" .. (System args at(1)) .. "\")")
System exit(0)
)
loop(
line := MalReadline readLine("user> ")
if(line isNil, break)
if(line isEmpty, continue)
e := try(REP(line) println)
e catch(Exception,
("Error: " .. (e error)) println
)
)