Merge pull request #380 from bjh21/bjh21-bbc-basic
[jackhill/mal.git] / io / step7_quote.io
CommitLineData
8f22ab1a
DM
1MalTypes
2MalReader
3
4READ := method(str, MalReader read_str(str))
5
6isPair := method(obj,
7 obj ?isSequential and(obj isEmpty not)
8)
9
10quasiquote := 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
19eval_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
35EVAL := 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
88PRINT := method(exp, exp malPrint(true))
89
90repl_env := Env with(nil)
91
92RE := method(str, EVAL(READ(str), repl_env))
93
94REP := method(str, PRINT(RE(str)))
95
96MalCore NS foreach(k, v, repl_env set(MalSymbol with(k), v))
97repl_env set(MalSymbol with("eval"), block(a, EVAL(a at(0), repl_env)))
98repl_env set(MalSymbol with("*ARGV*"), MalList with(System args slice(2)))
99
100// core.mal: defined using the language itself
101RE("(def! not (fn* (a) (if a false true)))")
102RE("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
103
104if(System args size > 1,
105 REP("(load-file \"" .. (System args at(1)) .. "\")")
106 System exit(0)
107)
108
109loop(
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)