Commit | Line | Data |
---|---|---|
8f22ab1a 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 | eval_ast := method(ast, env, | |
20 | (ast type) switch( | |
21 | "MalSymbol", env get(ast), | |
22 | "MalList", MalList with(ast map(a, EVAL(a, env))), | |
23 | "MalVector", MalVector with(ast map(a, EVAL(a, env))), | |
24 | "MalMap", | |
25 | m := MalMap clone | |
26 | ast foreach(k, v, | |
27 | keyObj := MalMap keyToObj(k) | |
28 | m atPut(MalMap objToKey(EVAL(keyObj, env)), EVAL(v, env)) | |
29 | ) | |
30 | m, | |
31 | ast | |
32 | ) | |
33 | ) | |
34 | ||
35 | EVAL := method(ast, env, | |
36 | loop( | |
37 | if(ast type != "MalList", return(eval_ast(ast, env))) | |
9c7a452e | 38 | if(ast isEmpty, return ast) |
8f22ab1a DM |
39 | if(ast at(0) type == "MalSymbol", |
40 | ast at(0) val switch( | |
41 | "def!", | |
42 | return(env set(ast at(1), EVAL(ast at(2), env))), | |
43 | "do", | |
44 | eval_ast(ast slice(1,-1), env) | |
45 | ast = ast last | |
46 | continue, // TCO | |
47 | "if", | |
48 | ast = if(EVAL(ast at(1), env), ast at(2), ast at(3)) | |
49 | continue, // TCO | |
50 | "fn*", | |
51 | return(MalFunc with(ast at(2), ast at(1), env, block(a, EVAL(ast at(2), Env with(env, ast at(1), a))))), | |
52 | "let*", | |
53 | letEnv := Env with(env) | |
54 | varName := nil | |
55 | ast at(1) foreach(i, e, | |
56 | if(i % 2 == 0, | |
57 | varName := e, | |
58 | letEnv set(varName, EVAL(e, letEnv)) | |
59 | ) | |
60 | ) | |
61 | ast = ast at(2) | |
62 | env = letEnv | |
63 | continue, // TCO | |
64 | "quote", | |
65 | return(ast at(1)), | |
66 | "quasiquote", | |
67 | ast = quasiquote(ast at(1)) | |
68 | continue // TCO | |
69 | ) | |
70 | ) | |
71 | ||
72 | // Apply | |
73 | el := eval_ast(ast, env) | |
74 | f := el at(0) | |
75 | args := el rest | |
76 | f type switch( | |
77 | "Block", | |
78 | return(f call(args)), | |
79 | "MalFunc", | |
80 | ast = f ast | |
81 | env = Env with(f env, f params, args) | |
82 | continue, // TCO | |
83 | Exception raise("Unknown function type") | |
84 | ) | |
85 | ) | |
86 | ) | |
87 | ||
88 | PRINT := method(exp, exp malPrint(true)) | |
89 | ||
90 | repl_env := Env with(nil) | |
91 | ||
92 | RE := method(str, EVAL(READ(str), repl_env)) | |
93 | ||
94 | REP := method(str, PRINT(RE(str))) | |
95 | ||
96 | MalCore NS foreach(k, v, repl_env set(MalSymbol with(k), v)) | |
97 | repl_env set(MalSymbol with("eval"), block(a, EVAL(a at(0), repl_env))) | |
98 | repl_env set(MalSymbol with("*ARGV*"), MalList with(System args slice(2))) | |
99 | ||
100 | // core.mal: defined using the language itself | |
101 | RE("(def! not (fn* (a) (if a false true)))") | |
102 | RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))") | |
103 | ||
104 | if(System args size > 1, | |
105 | REP("(load-file \"" .. (System args at(1)) .. "\")") | |
106 | System exit(0) | |
107 | ) | |
108 | ||
109 | loop( | |
110 | line := MalReadline readLine("user> ") | |
111 | if(line isNil, break) | |
112 | if(line isEmpty, continue) | |
113 | e := try(REP(line) println) | |
114 | e catch(Exception, | |
dd7a4f55 JM |
115 | if(e type == "MalException", |
116 | ("Error: " .. ((e val) malPrint(true))) println, | |
117 | ("Error: " .. (e error)) println | |
118 | ) | |
8f22ab1a DM |
119 | ) |
120 | ) |