1 import { readline } from "./node_readline";
3 import { Node, MalType, MalNumber, MalList, MalVector, MalHashMap, MalFunction, isSeq } from "./types";
4 import { readStr } from "./reader";
5 import { prStr } from "./printer";
8 function read(str: string): MalType {
12 interface MalEnvironment {
13 [key: string]: MalFunction;
16 function evalAST(ast: MalType, env: MalEnvironment): MalType {
21 throw new Error(`unknown symbol: ${ast.v}`);
25 return new MalList(ast.list.map(ast => evalMal(ast, env)));
27 return new MalVector(ast.list.map(ast => evalMal(ast, env)));
29 const list: MalType[] = [];
30 for (const [key, value] of ast.entries()) {
32 list.push(evalMal(value, env));
34 return new MalHashMap(list);
41 function evalMal(ast: MalType, env: MalEnvironment): MalType {
42 if (ast.type !== Node.List) {
43 return evalAST(ast, env);
45 if (ast.list.length === 0) {
48 const result = evalAST(ast, env);
50 throw new Error(`unexpected return type: ${result.type}, expected: list or vector`);
52 const [f, ...args] = result.list;
53 if (f.type !== Node.Function) {
54 throw new Error(`unexpected token: ${f.type}, expected: function`);
56 return f.func(...args);
60 function print(exp: MalType): string {
64 const replEnv: MalEnvironment = {
65 "+": MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v + b!.v)),
66 "-": MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v - b!.v)),
67 "*": MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v * b!.v)),
68 "/": MalFunction.fromBootstrap((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v / b!.v)),
70 function rep(str: string): string {
71 return print(evalMal(read(str), replEnv));
75 const line = readline("user> ");
83 console.log(rep(line));
86 console.error(err.message);