Add long running perf test.
[jackhill/mal.git] / go / src / stepA_interop / stepA_interop.go
1 package main
2
3 import (
4 "fmt"
5 "strings"
6 "errors"
7 "os"
8 )
9
10 import (
11 "readline"
12 . "types"
13 "reader"
14 "printer"
15 . "env"
16 "core"
17 )
18
19 // read
20 func READ(str string) (MalType, error) {
21 return reader.Read_str(str)
22 }
23
24 // eval
25 func is_pair(x MalType) bool {
26 slc, e := GetSlice(x)
27 if e != nil { return false }
28 return len(slc) > 0
29 }
30
31 func quasiquote(ast MalType) MalType {
32 if !is_pair(ast) {
33 return List{[]MalType{Symbol{"quote"}, ast},nil}
34 } else {
35 slc, _ := GetSlice(ast)
36 a0 := slc[0]
37 if Symbol_Q(a0) && (a0.(Symbol).Val == "unquote") {
38 return slc[1]
39 } else if is_pair(a0) {
40 slc0, _ := GetSlice(a0)
41 a00 := slc0[0]
42 if Symbol_Q(a00) && (a00.(Symbol).Val == "splice-unquote") {
43 return List{[]MalType{Symbol{"concat"},
44 slc0[1],
45 quasiquote(List{slc[1:],nil})},nil}
46 }
47 }
48 return List{[]MalType{Symbol{"cons"},
49 quasiquote(a0),
50 quasiquote(List{slc[1:],nil})},nil}
51 }
52 }
53
54 func is_macro_call(ast MalType, env EnvType) bool {
55 if List_Q(ast) {
56 slc, _ := GetSlice(ast)
57 a0 := slc[0]
58 if Symbol_Q(a0) && env.Find(a0.(Symbol)) != nil {
59 mac, e := env.Get(a0.(Symbol))
60 if e != nil { return false }
61 if MalFunc_Q(mac) {
62 return mac.(MalFunc).GetMacro()
63 }
64 }
65 }
66 return false
67 }
68
69 func macroexpand(ast MalType, env EnvType) (MalType, error) {
70 var mac MalType
71 var e error
72 for ; is_macro_call(ast, env) ; {
73 slc, _ := GetSlice(ast)
74 a0 := slc[0]
75 mac, e = env.Get(a0.(Symbol)); if e != nil { return nil, e }
76 fn := mac.(MalFunc)
77 ast, e = Apply(fn, slc[1:]); if e != nil { return nil, e }
78 }
79 return ast, nil
80 }
81
82 func eval_ast(ast MalType, env EnvType) (MalType, error) {
83 //fmt.Printf("eval_ast: %#v\n", ast)
84 if Symbol_Q(ast) {
85 return env.Get(ast.(Symbol))
86 } else if List_Q(ast) {
87 lst := []MalType{}
88 for _, a := range ast.(List).Val {
89 exp, e := EVAL(a, env)
90 if e != nil { return nil, e }
91 lst = append(lst, exp)
92 }
93 return List{lst,nil}, nil
94 } else if Vector_Q(ast) {
95 lst := []MalType{}
96 for _, a := range ast.(Vector).Val {
97 exp, e := EVAL(a, env)
98 if e != nil { return nil, e }
99 lst = append(lst, exp)
100 }
101 return Vector{lst,nil}, nil
102 } else if HashMap_Q(ast) {
103 m := ast.(HashMap)
104 new_hm := HashMap{map[string]MalType{},nil}
105 for k, v := range m.Val {
106 ke, e1 := EVAL(k, env)
107 if e1 != nil { return nil, e1 }
108 if _, ok := ke.(string); !ok {
109 return nil, errors.New("non string hash-map key")
110 }
111 kv, e2 := EVAL(v, env)
112 if e2 != nil { return nil, e2 }
113 new_hm.Val[ke.(string)] = kv
114 }
115 return new_hm, nil
116 } else {
117 return ast, nil
118 }
119 }
120
121 func EVAL(ast MalType, env EnvType) (MalType, error) {
122 var e error
123 for {
124
125 //fmt.Printf("EVAL: %v\n", printer.Pr_str(ast, true))
126 switch ast.(type) {
127 case List: // continue
128 default: return eval_ast(ast, env)
129 }
130
131 // apply list
132 ast, e = macroexpand(ast, env); if e != nil { return nil, e }
133 if (!List_Q(ast)) { return ast, nil }
134
135 a0 := ast.(List).Val[0]
136 var a1 MalType = nil; var a2 MalType = nil
137 switch len(ast.(List).Val) {
138 case 1:
139 a1 = nil; a2 = nil
140 case 2:
141 a1 = ast.(List).Val[1]; a2 = nil
142 default:
143 a1 = ast.(List).Val[1]; a2 = ast.(List).Val[2]
144 }
145 a0sym := "__<*fn*>__"
146 if Symbol_Q(a0) { a0sym = a0.(Symbol).Val }
147 switch a0sym {
148 case "def!":
149 res, e := EVAL(a2, env)
150 if e != nil { return nil, e }
151 return env.Set(a1.(Symbol), res), nil
152 case "let*":
153 let_env, e := NewEnv(env, nil, nil)
154 if e != nil { return nil, e }
155 arr1, e := GetSlice(a1)
156 if e != nil { return nil, e }
157 for i := 0; i < len(arr1); i+=2 {
158 if !Symbol_Q(arr1[i]) {
159 return nil, errors.New("non-symbol bind value")
160 }
161 exp, e := EVAL(arr1[i+1], let_env)
162 if e != nil { return nil, e }
163 let_env.Set(arr1[i].(Symbol), exp)
164 }
165 ast = a2
166 env = let_env
167 case "quote":
168 return a1, nil
169 case "quasiquote":
170 ast = quasiquote(a1)
171 case "defmacro!":
172 fn, e := EVAL(a2, env)
173 fn = fn.(MalFunc).SetMacro()
174 if e != nil { return nil, e }
175 return env.Set(a1.(Symbol), fn), nil
176 case "macroexpand":
177 return macroexpand(a1, env)
178 case "try*":
179 var exc MalType
180 exp, e := EVAL(a1, env)
181 if e == nil {
182 return exp, nil
183 } else {
184 if a2 != nil && List_Q(a2) {
185 a2s, _ := GetSlice(a2)
186 if Symbol_Q(a2s[0]) && (a2s[0].(Symbol).Val == "catch*") {
187 switch e.(type) {
188 case MalError: exc = e.(MalError).Obj
189 default: exc = e.Error()
190 }
191 binds := NewList(a2s[1])
192 new_env, e := NewEnv(env, binds, NewList(exc))
193 if e != nil { return nil, e }
194 exp, e = EVAL(a2s[2], new_env)
195 if e == nil { return exp, nil }
196 }
197 }
198 return nil, e
199 }
200 case "do":
201 lst := ast.(List).Val
202 _, e := eval_ast(List{lst[1:len(lst)-1],nil}, env)
203 if e != nil { return nil, e }
204 if len(lst) == 1 { return nil, nil }
205 ast = lst[len(lst)-1]
206 case "if":
207 cond, e := EVAL(a1, env)
208 if e != nil { return nil, e }
209 if cond == nil || cond == false {
210 if len(ast.(List).Val) >= 4 {
211 ast = ast.(List).Val[3]
212 } else {
213 return nil, nil
214 }
215 } else {
216 ast = a2
217 }
218 case "fn*":
219 fn := MalFunc{EVAL, a2, env, a1, false, NewEnv, nil}
220 return fn, nil
221 default:
222 el, e := eval_ast(ast, env)
223 if e != nil { return nil, e }
224 f := el.(List).Val[0]
225 if MalFunc_Q(f) {
226 fn := f.(MalFunc)
227 ast = fn.Exp
228 env, e = NewEnv(fn.Env, fn.Params, List{el.(List).Val[1:],nil})
229 if e != nil { return nil, e }
230 } else {
231 fn, ok := f.(Func)
232 if !ok { return nil, errors.New("attempt to call non-function") }
233 return fn.Fn(el.(List).Val[1:])
234 }
235 }
236
237 } // TCO loop
238 }
239
240 // print
241 func PRINT(exp MalType) (string, error) {
242 return printer.Pr_str(exp, true), nil
243 }
244
245
246 var repl_env, _ = NewEnv(nil, nil, nil)
247
248 // repl
249 func rep(str string) (MalType, error) {
250 var exp MalType
251 var res string
252 var e error
253 if exp, e = READ(str); e != nil { return nil, e }
254 if exp, e = EVAL(exp, repl_env); e != nil { return nil, e }
255 if res, e = PRINT(exp); e != nil { return nil, e }
256 return res, nil
257 }
258
259 func main() {
260 // core.go: defined using go
261 for k, v := range core.NS {
262 repl_env.Set(Symbol{k}, Func{v.(func([]MalType)(MalType,error)),nil})
263 }
264 repl_env.Set(Symbol{"eval"}, Func{func(a []MalType) (MalType, error) {
265 return EVAL(a[0], repl_env) },nil})
266 repl_env.Set(Symbol{"*ARGV*"}, List{})
267
268 // core.mal: defined using the language itself
269 rep("(def! *host-language* \"go\")")
270 rep("(def! not (fn* (a) (if a false true)))")
271 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \")\")))))")
272 rep("(defmacro! cond (fn* (& xs) (if (> (count xs) 0) (list 'if (first xs) (if (> (count xs) 1) (nth xs 1) (throw \"odd number of forms to cond\")) (cons 'cond (rest (rest xs)))))))")
273 rep("(defmacro! or (fn* (& xs) (if (empty? xs) nil (if (= 1 (count xs)) (first xs) `(let* (or_FIXME ~(first xs)) (if or_FIXME or_FIXME (or ~@(rest xs))))))))")
274
275 // called with mal script to load and eval
276 if len(os.Args) > 1 {
277 args := make([]MalType, 0, len(os.Args)-2)
278 for _,a := range os.Args[2:] {
279 args = append(args, a)
280 }
281 repl_env.Set(Symbol{"*ARGV*"}, List{args,nil})
282 if _,e := rep("(load-file \"" + os.Args[1] + "\")"); e != nil {
283 fmt.Printf("Error: %v\n", e)
284 os.Exit(1)
285 }
286 os.Exit(0)
287 }
288
289 // repl loop
290 rep("(println (str \"Mal [\" *host-language* \"]\"))")
291 for {
292 text, err := readline.Readline("user> ")
293 text = strings.TrimRight(text, "\n");
294 if (err != nil) {
295 return
296 }
297 var out MalType
298 var e error
299 if out, e = rep(text); e != nil {
300 if e.Error() == "<empty line>" { continue }
301 fmt.Printf("Error: %v\n", e)
302 continue
303 }
304 fmt.Printf("%v\n", out)
305 }
306 }