tests: make throw of non-strings optional/soft.
[jackhill/mal.git] / io / step4_if_fn_do.io
1 MalTypes
2 MalReader
3
4 READ := method(str, MalReader read_str(str))
5
6 eval_ast := method(ast, env,
7 (ast type) switch(
8 "MalSymbol", env get(ast),
9 "MalList", MalList with(ast map(a, EVAL(a, env))),
10 "MalVector", MalVector with(ast map(a, EVAL(a, env))),
11 "MalMap",
12 m := MalMap clone
13 ast foreach(k, v,
14 keyObj := MalMap keyToObj(k)
15 m atPut(MalMap objToKey(EVAL(keyObj, env)), EVAL(v, env))
16 )
17 m,
18 ast
19 )
20 )
21
22 EVAL := method(ast, env,
23 if(ast type != "MalList", return(eval_ast(ast, env)))
24 if(ast isEmpty, return ast)
25 if(ast at(0) type == "MalSymbol",
26 ast at(0) val switch(
27 "def!",
28 return(env set(ast at(1), EVAL(ast at(2), env))),
29 "do",
30 return(eval_ast(ast rest, env) last),
31 "if",
32 return(EVAL(if(EVAL(ast at(1), env), ast at(2), ast at(3)), env)),
33 "fn*",
34 return(block(a, EVAL(ast at(2), Env with(env, ast at(1), a)))),
35 "let*",
36 letEnv := Env with(env)
37 varName := nil
38 ast at(1) foreach(i, e,
39 if(i % 2 == 0,
40 varName := e,
41 letEnv set(varName, EVAL(e, letEnv))
42 )
43 )
44 return(EVAL(ast at(2), letEnv))
45 )
46 )
47
48 // Apply
49 el := eval_ast(ast, env)
50 f := el at(0)
51 args := el rest
52 f call(args)
53 )
54
55 PRINT := method(exp, exp malPrint(true))
56
57 RE := method(str, EVAL(READ(str), repl_env))
58
59 REP := method(str, PRINT(RE(str)))
60
61 repl_env := Env with(nil)
62 MalCore NS foreach(k, v, repl_env set(MalSymbol with(k), v))
63
64 // core.mal: defined using the language itself
65 RE("(def! not (fn* (a) (if a false true)))")
66
67 loop(
68 line := MalReadline readLine("user> ")
69 if(line isNil, break)
70 if(line isEmpty, continue)
71 e := try(REP(line) println)
72 e catch(Exception,
73 ("Error: " .. (e error)) println
74 )
75 )