Merge pull request #345 from asarhaddon/ada.2
[jackhill/mal.git] / yorick / step5_tco.i
1 set_path, get_env("YORICK_MAL_PATH") + ":" + get_path()
2 require, "reader.i"
3 require, "printer.i"
4 require, "core.i"
5 require, "env.i"
6
7 func READ(str)
8 {
9 return read_str(str)
10 }
11
12 func eval_ast(ast, env)
13 {
14 type = structof(ast)
15 if (type == MalSymbol) {
16 return env_get(env, ast.val)
17 } else if (type == MalList) {
18 seq = *(ast.val)
19 if (numberof(seq) == 0) return ast
20 res = array(pointer, numberof(seq))
21 for (i = 1; i <= numberof(seq); ++i) {
22 e = EVAL(*seq(i), env)
23 if (structof(e) == MalError) return e
24 res(i) = &e
25 }
26 return MalList(val=&res)
27 } else if (type == MalVector) {
28 seq = *(ast.val)
29 if (numberof(seq) == 0) return ast
30 res = array(pointer, numberof(seq))
31 for (i = 1; i <= numberof(seq); ++i) {
32 e = EVAL(*seq(i), env)
33 if (structof(e) == MalError) return e
34 res(i) = &e
35 }
36 return MalVector(val=&res)
37 } else if (type == MalHashmap) {
38 h = *(ast.val)
39 if (numberof(*h.keys) == 0) return ast
40 res = hash_new()
41 for (i = 1; i <= numberof(*h.keys); ++i) {
42 new_key = EVAL(hashmap_key_to_obj((*h.keys)(i)), env)
43 if (structof(new_key) == MalError) return new_key
44 new_val = EVAL(*((*h.vals)(i)), env)
45 if (structof(new_val) == MalError) return new_val
46 hash_set, res, hashmap_obj_to_key(new_key), new_val
47 }
48 return MalHashmap(val=&res)
49 } else return ast
50 }
51
52 func EVAL(ast, env)
53 {
54 while (1) {
55 if (structof(ast) == MalError) return ast
56 if (structof(ast) != MalList) return eval_ast(ast, env)
57 lst = *ast.val
58 if (numberof(lst) == 0) return ast
59 a1 = lst(1)->val
60 if (a1 == "def!") {
61 new_value = EVAL(*lst(3), env)
62 if (structof(new_value) == MalError) return new_value
63 return env_set(env, lst(2)->val, new_value)
64 } else if (a1 == "let*") {
65 let_env = env_new(&env)
66 args_lst = *(lst(2)->val)
67 for (i = 1; i <= numberof(args_lst); i += 2) {
68 var_name = args_lst(i)->val
69 var_value = EVAL(*args_lst(i + 1), let_env)
70 if (structof(var_value) == MalError) return var_value
71 env_set, let_env, var_name, var_value
72 }
73 ast = *lst(3)
74 env = let_env
75 // TCO
76 } else if (a1 == "do") {
77 for (i = 2; i < numberof(lst); ++i) {
78 ret = EVAL(*lst(i), env)
79 if (structof(ret) == MalError) return ret
80 }
81 ast = *lst(numberof(lst))
82 // TCO
83 } else if (a1 == "if") {
84 cond_val = EVAL(*lst(2), env)
85 if (structof(cond_val) == MalError) return cond_val
86 if ((structof(cond_val) == MalNil) || (structof(cond_val) == MalFalse)) {
87 if (numberof(lst) > 3) {
88 ast = *lst(4)
89 } else {
90 return MAL_NIL
91 }
92 } else {
93 ast = *lst(3)
94 }
95 // TCO
96 } else if (a1 == "fn*") {
97 return MalFunction(env=&env, binds=lst(2)->val, ast=lst(3))
98 } else {
99 el = eval_ast(ast, env)
100 if (structof(el) == MalError) return el
101 seq = *el.val
102 if (structof(*seq(1)) == MalNativeFunction) {
103 args = (numberof(seq) > 1) ? seq(2:) : []
104 return call_core_fn(seq(1)->val, args)
105 } else if (structof(*seq(1)) == MalFunction) {
106 fn = *seq(1)
107 exprs = numberof(seq) > 1 ? seq(2:) : []
108 fn_env = env_new(fn.env, binds=*fn.binds, exprs=exprs)
109 ast = *fn.ast
110 env = fn_env
111 // TCO
112 } else {
113 return MalError(message="Unknown function type")
114 }
115 }
116 }
117 }
118
119 func PRINT(exp)
120 {
121 if (structof(exp) == MalError) return exp
122 return pr_str(exp, 1)
123 }
124
125 func RE(str, env)
126 {
127 return EVAL(READ(str), env)
128 }
129
130 func REP(str, env)
131 {
132 return PRINT(EVAL(READ(str), env))
133 }
134
135 func main(void)
136 {
137 repl_env = env_new(pointer(0))
138
139 // core.i: defined using Yorick
140 core_symbols = h_keys(core_ns)
141 for (i = 1; i <= numberof(core_symbols); ++i) {
142 env_set, repl_env, core_symbols(i), MalNativeFunction(val=core_symbols(i))
143 }
144
145 // core.mal: defined using the language itself
146 RE, "(def! not (fn* (a) (if a false true)))", repl_env
147
148 stdin_file = open("/dev/stdin", "r")
149 while (1) {
150 write, format="%s", "user> "
151 line = rdline(stdin_file, prompt="")
152 if (!line) break
153 if (strlen(line) > 0) {
154 result = REP(line, repl_env)
155 if (structof(result) == MalError) write, format="Error: %s\n", result.message
156 else write, format="%s\n", result
157 }
158 }
159 write, ""
160 }
161
162 main;