Merge pull request #323 from jig/master
[jackhill/mal.git] / ts / step3_env.ts
CommitLineData
86fa8806 1import { readline } from "./node_readline";
2
677a1c9d 3import { Node, MalType, MalNumber, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isSeq } from "./types";
86fa8806 4import { Env } from "./env";
5import { readStr } from "./reader";
6import { prStr } from "./printer";
7
9c92462f 8// READ
86fa8806 9function read(str: string): MalType {
10 return readStr(str);
11}
12
13function evalAST(ast: MalType, env: Env): MalType {
14 switch (ast.type) {
5bb7479d 15 case Node.Symbol:
86fa8806 16 const f = env.get(ast);
17 if (!f) {
18 throw new Error(`unknown symbol: ${ast.v}`);
19 }
20 return f;
5bb7479d 21 case Node.List:
9c92462f 22 return new MalList(ast.list.map(ast => evalMal(ast, env)));
5bb7479d 23 case Node.Vector:
9c92462f 24 return new MalVector(ast.list.map(ast => evalMal(ast, env)));
5bb7479d 25 case Node.HashMap:
86fa8806 26 const list: MalType[] = [];
10f8aa84 27 for (const [key, value] of ast.entries()) {
86fa8806 28 list.push(key);
9c92462f 29 list.push(evalMal(value, env));
86fa8806 30 }
31 return new MalHashMap(list);
32 default:
33 return ast;
34 }
35}
36
9c92462f 37// EVAL
38function evalMal(ast: MalType, env: Env): MalType {
5bb7479d 39 if (ast.type !== Node.List) {
86fa8806 40 return evalAST(ast, env);
41 }
42 if (ast.list.length === 0) {
43 return ast;
44 }
45 const first = ast.list[0];
46 switch (first.type) {
5bb7479d 47 case Node.Symbol:
86fa8806 48 switch (first.v) {
49 case "def!": {
50 const [, key, value] = ast.list;
677a1c9d 51 if (key.type !== Node.Symbol) {
86fa8806 52 throw new Error(`unexpected toke type: ${key.type}, expected: symbol`);
53 }
54 if (!value) {
55 throw new Error(`unexpected syntax`);
56 }
677a1c9d 57 return env.set(key, evalMal(value, env));
86fa8806 58 }
59 case "let*": {
60 let letEnv = new Env(env);
61 const pairs = ast.list[1];
677a1c9d 62 if (!isSeq(pairs)) {
86fa8806 63 throw new Error(`unexpected toke type: ${pairs.type}, expected: list or vector`);
64 }
677a1c9d 65 const list = pairs.list;
86fa8806 66 for (let i = 0; i < list.length; i += 2) {
67 const key = list[i];
68 const value = list[i + 1];
677a1c9d 69 if (key.type !== Node.Symbol) {
70 throw new Error(`unexpected token type: ${key.type}, expected: symbol`);
71 }
86fa8806 72 if (!key || !value) {
73 throw new Error(`unexpected syntax`);
74 }
75
677a1c9d 76 letEnv.set(key, evalMal(value, letEnv));
86fa8806 77 }
9c92462f 78 return evalMal(ast.list[2], letEnv);
86fa8806 79 }
80 }
81 }
677a1c9d 82 const result = evalAST(ast, env);
83 if (!isSeq(result)) {
84 throw new Error(`unexpected return type: ${result.type}, expected: list or vector`);
85 }
79a10a6e 86 const [f, ...args] = result.list;
5bb7479d 87 if (f.type !== Node.Function) {
86fa8806 88 throw new Error(`unexpected token: ${f.type}, expected: function`);
89 }
79a10a6e 90 return f.func(...args);
86fa8806 91}
92
9c92462f 93// PRINT
86fa8806 94function print(exp: MalType): string {
95 return prStr(exp);
96}
97
98const replEnv = new Env();
9c92462f 99function rep(str: string): string {
100 return print(evalMal(read(str), replEnv));
101}
102
79a10a6e 103replEnv.set(MalSymbol.get("+"), MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v + b!.v)));
104replEnv.set(MalSymbol.get("-"), MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v - b!.v)));
105replEnv.set(MalSymbol.get("*"), MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v * b!.v)));
106replEnv.set(MalSymbol.get("/"), MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v / b!.v)));
86fa8806 107
86fa8806 108while (true) {
109 const line = readline("user> ");
110 if (line == null) {
111 break;
112 }
113 if (line === "") {
114 continue;
115 }
116 try {
117 console.log(rep(line));
118 } catch (e) {
119 const err: Error = e;
120 console.error(err.message);
121 }
122}