1 import { readline } from "./node_readline";
3 import { MalType, MalNumber, MalList, MalVector, MalHashMap, MalFunction } from "./types";
4 import { readStr } from "./reader";
5 import { prStr } from "./printer";
7 function read(str: string): MalType {
11 interface MalEnvironment {
12 [key: string]: MalFunction;
15 function evalAST(ast: MalType, env: MalEnvironment): MalType {
20 throw new Error(`unknown symbol: ${ast.v}`);
24 return new MalList(ast.list.map(ast => evalSexp(ast, env)));
26 return new MalVector(ast.list.map(ast => evalSexp(ast, env)));
28 const list: MalType[] = [];
29 for (const [key, value] of ast.map) {
31 list.push(evalSexp(value, env));
33 return new MalHashMap(list);
39 function evalSexp(ast: MalType, env: MalEnvironment): MalType {
40 if (ast.type !== "list") {
41 return evalAST(ast, env);
43 if (ast.list.length === 0) {
46 const result = evalAST(ast, env) as MalList;
47 const [f, ...rest] = result.list;
48 if (!MalFunction.is(f)) {
49 throw new Error(`unexpected token: ${f.type}, expected: function`);
51 return f.func(...rest);
54 function print(exp: MalType): string {
58 const replEnv: MalEnvironment = {
59 "+": new MalFunction((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v + b!.v)),
60 "-": new MalFunction((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v - b!.v)),
61 "*": new MalFunction((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v * b!.v)),
62 "/": new MalFunction((a?: MalNumber, b?: MalNumber) => new MalNumber(a!.v / b!.v)),
64 function rep(str: string): string {
65 return print(evalSexp(read(str), replEnv));
69 const line = readline("user> ");
77 console.log(rep(line));
80 console.error(err.message);