1 import { readline } from "./node_readline";
3 import { MalType, MalNumber, MalList, MalVector, MalHashMap, MalSymbol, MalFunction } from "./types";
4 import { Env } from "./env";
5 import { readStr } from "./reader";
6 import { prStr } from "./printer";
8 function read(str: string): MalType {
12 function evalAST(ast: MalType, env: Env): MalType {
15 const f = env.get(ast);
17 throw new Error(`unknown symbol: ${ast.v}`);
21 return new MalList(ast.list.map(ast => evalSexp(ast, env)));
23 return new MalVector(ast.list.map(ast => evalSexp(ast, env)));
25 const list: MalType[] = [];
26 for (const [key, value] of ast.map) {
28 list.push(evalSexp(value, env));
30 return new MalHashMap(list);
36 function evalSexp(ast: MalType, env: Env): MalType {
37 if (ast.type !== "list") {
38 return evalAST(ast, env);
40 if (ast.list.length === 0) {
43 const first = ast.list[0];
48 const [, key, value] = ast.list;
49 if (key instanceof MalSymbol === false) {
50 throw new Error(`unexpected toke type: ${key.type}, expected: symbol`);
53 throw new Error(`unexpected syntax`);
55 return env.set(key as MalSymbol, evalSexp(value, env))
58 let letEnv = new Env(env);
59 const pairs = ast.list[1];
60 if (pairs instanceof MalList === false && pairs instanceof MalVector === false) {
61 throw new Error(`unexpected toke type: ${pairs.type}, expected: list or vector`);
63 const list = (pairs as (MalList | MalVector)).list;
64 for (let i = 0; i < list.length; i += 2) {
66 const value = list[i + 1];
68 throw new Error(`unexpected syntax`);
71 letEnv.set(key as MalSymbol, evalSexp(value, letEnv));
73 return evalSexp(ast.list[2], letEnv);
77 const result = evalAST(ast, env) as MalList;
78 const [f, ...args] = result.list;
79 if (!MalFunction.is(f)) {
80 throw new Error(`unexpected token: ${f.type}, expected: function`);
82 return f.func(...args);
85 function print(exp: MalType): string {
89 const replEnv = new Env();
90 replEnv.set(MalSymbol.get("+"), MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v + b!.v)));
91 replEnv.set(MalSymbol.get("-"), MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v - b!.v)));
92 replEnv.set(MalSymbol.get("*"), MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v * b!.v)));
93 replEnv.set(MalSymbol.get("/"), MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v / b!.v)));
95 function rep(str: string): string {
96 return print(evalSexp(read(str), replEnv));
100 const line = readline("user> ");
108 console.log(rep(line));
110 const err: Error = e;
111 console.error(err.message);