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