Merge pull request #387 from asarhaddon/test-macroexpand-no-quasiquote
[jackhill/mal.git] / skew / step4_if_fn_do.sk
1 def READ(str string) MalVal {
2 return read_str(str)
3 }
4
5 def eval_ast(ast MalVal, env Env) MalVal {
6 if ast is MalSymbol {
7 return env.get(ast as MalSymbol)
8 } else if ast is MalList {
9 return MalList.new((ast as MalList).val.map<MalVal>(e => EVAL(e, env)))
10 } else if ast is MalVector {
11 return MalVector.new((ast as MalVector).val.map<MalVal>(e => EVAL(e, env)))
12 } else if ast is MalHashMap {
13 var result List<MalVal> = []
14 (ast as MalHashMap).val.each((k string, v MalVal) => {
15 result.append(EVAL(MalVal.fromHashKey(k), env))
16 result.append(EVAL(v, env))
17 })
18 return MalHashMap.fromList(result)
19 } else {
20 return ast
21 }
22 }
23
24 def EVAL(ast MalVal, env Env) MalVal {
25 if !(ast is MalList) { return eval_ast(ast, env) }
26 const astList = ast as MalList
27 if astList.isEmpty { return ast }
28 const a0sym = astList[0] as MalSymbol
29 if a0sym.val == "def!" {
30 return env.set(astList[1] as MalSymbol, EVAL(astList[2], env))
31 } else if a0sym.val == "let*" {
32 var letenv = Env.new(env)
33 const assigns = astList[1] as MalSequential
34 for i = 0; i < assigns.count; i += 2 {
35 letenv.set(assigns[i] as MalSymbol, EVAL(assigns[i + 1], letenv))
36 }
37 return EVAL(astList[2], letenv)
38 } else if a0sym.val == "do" {
39 const r = eval_ast(MalList.new(astList.val.slice(1)), env) as MalList
40 return r[r.count - 1]
41 } else if a0sym.val == "if" {
42 const condRes = EVAL(astList[1], env)
43 if condRes is MalNil || condRes is MalFalse {
44 return astList.count > 3 ? EVAL(astList[3], env) : gNil
45 } else {
46 return EVAL(astList[2], env)
47 }
48 } else if a0sym.val == "fn*" {
49 const argsNames = (astList[1] as MalSequential).val
50 return MalNativeFunc.new((args List<MalVal>) => EVAL(astList[2], Env.new(env, argsNames, args)))
51 } else {
52 const evaledList = eval_ast(ast, env) as MalList
53 const fn = evaledList[0] as MalNativeFunc
54 return fn.call(evaledList.val.slice(1))
55 }
56 }
57
58 def PRINT(exp MalVal) string {
59 return exp?.print(true)
60 }
61
62 var repl_env = Env.new(null)
63
64 def RE(str string) MalVal {
65 return EVAL(READ(str), repl_env)
66 }
67
68 def REP(str string) string {
69 return PRINT(RE(str))
70 }
71
72 @entry
73 def main {
74 # core.sk: defined using Skew
75 ns.each((name, func) => repl_env.set(MalSymbol.new(name), MalNativeFunc.new(func)))
76
77 # core.mal: defined using the language itself
78 RE("(def! not (fn* (a) (if a false true)))")
79
80 var line string
81 while (line = readLine("user> ")) != null {
82 if line == "" { continue }
83 try {
84 printLn(REP(line))
85 }
86 catch e MalError {
87 printLn("Error: \(e.message)")
88 }
89 }
90 }