rebase to kanaka/mal
[jackhill/mal.git] / impls / wren / step6_file.wren
1 import "os" for Process
2 import "./env" for Env
3 import "./readline" for Readline
4 import "./reader" for MalReader
5 import "./printer" for Printer
6 import "./types" for MalSymbol, MalList, MalVector, MalMap, MalNativeFn, MalFn
7 import "./core" for Core
8
9 class Mal {
10 static read(str) {
11 return MalReader.read_str(str)
12 }
13
14 static eval_ast(ast, env) {
15 if (ast is MalSymbol) {
16 return env.get(ast.value)
17 } else if (ast is MalList) {
18 return MalList.new(ast.elements.map { |e| eval(e, env) }.toList)
19 } else if (ast is MalVector) {
20 return MalVector.new(ast.elements.map { |e| eval(e, env) }.toList)
21 } else if (ast is MalMap) {
22 var m = {}
23 for (e in ast.data) {
24 m[e.key] = eval(e.value, env)
25 }
26 return MalMap.new(m)
27 } else {
28 return ast
29 }
30 }
31
32 static eval(ast, env) {
33 while (true) {
34 var tco = false
35 if (!(ast is MalList)) return eval_ast(ast, env)
36 if (ast.isEmpty) return ast
37 if (ast[0] is MalSymbol) {
38 if (ast[0].value == "def!") {
39 return env.set(ast[1].value, eval(ast[2], env))
40 } else if (ast[0].value == "let*") {
41 var letEnv = Env.new(env)
42 var i = 0
43 while (i < ast[1].count) {
44 letEnv.set(ast[1][i].value, eval(ast[1][i + 1], letEnv))
45 i = i + 2
46 }
47 ast = ast[2]
48 env = letEnv
49 tco = true
50 } else if (ast[0].value == "do") {
51 for (i in 1...(ast.count - 1)) {
52 eval(ast[i], env)
53 }
54 ast = ast[-1]
55 tco = true
56 } else if (ast[0].value == "if") {
57 var condval = eval(ast[1], env)
58 if (condval) {
59 ast = ast[2]
60 } else {
61 if (ast.count <= 3) return null
62 ast = ast[3]
63 }
64 tco = true
65 } else if (ast[0].value == "fn*") {
66 return MalFn.new(ast[2], ast[1].elements, env,
67 Fn.new { |a| eval(ast[2], Env.new(env, ast[1].elements, a)) })
68 }
69 }
70 if (!tco) {
71 var evaled_ast = eval_ast(ast, env)
72 var f = evaled_ast[0]
73 if (f is MalNativeFn) {
74 return f.call(evaled_ast[1..-1])
75 } else if (f is MalFn) {
76 ast = f.ast
77 env = Env.new(f.env, f.params, evaled_ast[1..-1])
78 tco = true
79 } else {
80 Fiber.abort("unknown function type")
81 }
82 }
83 }
84 }
85
86 static print(ast) {
87 return Printer.pr_str(ast)
88 }
89
90 static rep(str) {
91 return print(eval(read(str), __repl_env))
92 }
93
94 static main() {
95 __repl_env = Env.new()
96 // core.wren: defined in wren
97 for (e in Core.ns) { __repl_env.set(e.key, e.value) }
98 __repl_env.set("eval", MalNativeFn.new { |a| eval(a[0], __repl_env) })
99 __repl_env.set("*ARGV*", MalList.new(Process.arguments.count > 0 ? Process.arguments[1..-1] : []))
100 // core.mal: defined using the language itself
101 rep("(def! not (fn* (a) (if a false true)))")
102 rep("(def! load-file (fn* (f) (eval (read-string (str \"(do \" (slurp f) \"\nnil)\")))))")
103
104 if (Process.arguments.count > 0) {
105 rep("(load-file \"%(Process.arguments[0])\")")
106 return
107 }
108
109 while (true) {
110 var line = Readline.readLine("user> ")
111 if (line == null) break
112 if (line != "") {
113 var fiber = Fiber.new { System.print(rep(line)) }
114 fiber.try()
115 if (fiber.error) System.print("Error: %(fiber.error)")
116 }
117 }
118 System.print()
119 }
120 }
121
122 Mal.main()