Commit | Line | Data |
---|---|---|
dfe70453 | 1 | import { readline } from "./node_readline"; |
2 | ||
dd7a4f55 | 3 | import { Node, MalType, MalNil, MalList, MalVector, MalHashMap, MalFunction, isAST, isSeq } from "./types"; |
dfe70453 | 4 | import { Env } from "./env"; |
5 | import * as core from "./core"; | |
6 | import { readStr } from "./reader"; | |
7 | import { prStr } from "./printer"; | |
8 | ||
9c92462f | 9 | // READ |
dfe70453 | 10 | function read(str: string): MalType { |
11 | return readStr(str); | |
12 | } | |
13 | ||
14 | function evalAST(ast: MalType, env: Env): MalType { | |
15 | switch (ast.type) { | |
5bb7479d | 16 | case Node.Symbol: |
dfe70453 | 17 | const f = env.get(ast); |
18 | if (!f) { | |
19 | throw new Error(`unknown symbol: ${ast.v}`); | |
20 | } | |
21 | return f; | |
5bb7479d | 22 | case Node.List: |
9c92462f | 23 | return new MalList(ast.list.map(ast => evalMal(ast, env))); |
5bb7479d | 24 | case Node.Vector: |
9c92462f | 25 | return new MalVector(ast.list.map(ast => evalMal(ast, env))); |
5bb7479d | 26 | case Node.HashMap: |
dfe70453 | 27 | const list: MalType[] = []; |
10f8aa84 | 28 | for (const [key, value] of ast.entries()) { |
dfe70453 | 29 | list.push(key); |
9c92462f | 30 | list.push(evalMal(value, env)); |
dfe70453 | 31 | } |
32 | return new MalHashMap(list); | |
33 | default: | |
34 | return ast; | |
35 | } | |
36 | } | |
37 | ||
9c92462f | 38 | // EVAL |
39 | function evalMal(ast: MalType, env: Env): MalType { | |
5bb7479d | 40 | if (ast.type !== Node.List) { |
dfe70453 | 41 | return evalAST(ast, env); |
42 | } | |
43 | if (ast.list.length === 0) { | |
44 | return ast; | |
45 | } | |
46 | const first = ast.list[0]; | |
47 | switch (first.type) { | |
5bb7479d | 48 | case Node.Symbol: |
dfe70453 | 49 | switch (first.v) { |
50 | case "def!": { | |
51 | const [, key, value] = ast.list; | |
5bb7479d | 52 | if (key.type !== Node.Symbol) { |
dfe70453 | 53 | throw new Error(`unexpected token type: ${key.type}, expected: symbol`); |
54 | } | |
55 | if (!value) { | |
56 | throw new Error(`unexpected syntax`); | |
57 | } | |
677a1c9d | 58 | return env.set(key, evalMal(value, env)); |
dfe70453 | 59 | } |
60 | case "let*": { | |
61 | let letEnv = new Env(env); | |
62 | const pairs = ast.list[1]; | |
92bf0530 | 63 | if (!isSeq(pairs)) { |
dfe70453 | 64 | throw new Error(`unexpected token type: ${pairs.type}, expected: list or vector`); |
65 | } | |
66 | for (let i = 0; i < pairs.list.length; i += 2) { | |
67 | const key = pairs.list[i]; | |
68 | const value = pairs.list[i + 1]; | |
5bb7479d | 69 | if (key.type !== Node.Symbol) { |
dfe70453 | 70 | throw new Error(`unexpected token type: ${key.type}, expected: symbol`); |
71 | } | |
72 | if (!key || !value) { | |
73 | throw new Error(`unexpected syntax`); | |
74 | } | |
75 | ||
9c92462f | 76 | letEnv.set(key, evalMal(value, letEnv)); |
dfe70453 | 77 | } |
9c92462f | 78 | return evalMal(ast.list[2], letEnv); |
dfe70453 | 79 | } |
80 | case "do": { | |
81 | const [, ...list] = ast.list; | |
82 | const ret = evalAST(new MalList(list), env); | |
92bf0530 | 83 | if (!isSeq(ret)) { |
dfe70453 | 84 | throw new Error(`unexpected return type: ${ret.type}, expected: list or vector`); |
85 | } | |
86 | return ret.list[ret.list.length - 1]; | |
87 | } | |
88 | case "if": { | |
89 | const [, cond, thenExpr, elseExrp] = ast.list; | |
9c92462f | 90 | const ret = evalMal(cond, env); |
dfe70453 | 91 | let b = true; |
5bb7479d | 92 | if (ret.type === Node.Boolean && !ret.v) { |
dfe70453 | 93 | b = false; |
6071876f | 94 | } else if (ret.type === Node.Nil) { |
dfe70453 | 95 | b = false; |
96 | } | |
97 | if (b) { | |
9c92462f | 98 | return evalMal(thenExpr, env); |
dfe70453 | 99 | } else if (elseExrp) { |
9c92462f | 100 | return evalMal(elseExrp, env); |
dfe70453 | 101 | } else { |
6071876f | 102 | return MalNil.instance; |
dfe70453 | 103 | } |
104 | } | |
105 | case "fn*": { | |
106 | const [, args, binds] = ast.list; | |
92bf0530 | 107 | if (!isSeq(args)) { |
dfe70453 | 108 | throw new Error(`unexpected return type: ${args.type}, expected: list or vector`); |
109 | } | |
5bb7479d | 110 | const symbols = args.list.map(param => { |
111 | if (param.type !== Node.Symbol) { | |
112 | throw new Error(`unexpected return type: ${param.type}, expected: symbol`); | |
dfe70453 | 113 | } |
5bb7479d | 114 | return param; |
dfe70453 | 115 | }); |
79a10a6e | 116 | return MalFunction.fromBootstrap((...fnArgs: MalType[]) => { |
9c92462f | 117 | return evalMal(binds, new Env(env, symbols, fnArgs)); |
dfe70453 | 118 | }); |
119 | } | |
120 | } | |
121 | } | |
122 | const result = evalAST(ast, env); | |
92bf0530 | 123 | if (!isSeq(result)) { |
dfe70453 | 124 | throw new Error(`unexpected return type: ${result.type}, expected: list or vector`); |
125 | } | |
79a10a6e | 126 | const [f, ...args] = result.list; |
5bb7479d | 127 | if (f.type !== Node.Function) { |
dfe70453 | 128 | throw new Error(`unexpected token: ${f.type}, expected: function`); |
129 | } | |
79a10a6e | 130 | return f.func(...args); |
dfe70453 | 131 | } |
132 | ||
9c92462f | 133 | |
dfe70453 | 134 | function print(exp: MalType): string { |
135 | return prStr(exp); | |
136 | } | |
137 | ||
138 | const replEnv = new Env(); | |
9c92462f | 139 | function rep(str: string): string { |
140 | return print(evalMal(read(str), replEnv)); | |
141 | } | |
142 | ||
143 | // core.EXT: defined using Racket | |
eb7a2bbd | 144 | core.ns.forEach((value, key) => { |
dfe70453 | 145 | replEnv.set(key, value); |
eb7a2bbd | 146 | }); |
dfe70453 | 147 | |
148 | // core.mal: defined using the language itself | |
149 | rep("(def! not (fn* (a) (if a false true)))"); | |
150 | ||
dfe70453 | 151 | while (true) { |
152 | const line = readline("user> "); | |
153 | if (line == null) { | |
154 | break; | |
155 | } | |
156 | if (line === "") { | |
157 | continue; | |
158 | } | |
159 | try { | |
160 | console.log(rep(line)); | |
161 | } catch (e) { | |
dd7a4f55 JM |
162 | if (isAST(e)) { |
163 | console.error("Error:", prStr(e)); | |
164 | } else { | |
165 | const err: Error = e; | |
166 | console.error("Error:", err.message); | |
167 | } | |
dfe70453 | 168 | } |
169 | } |