import {assertEquals} from "https://deno.land/std@0.224.0/assert/assert_equals.ts"; import {AST, FunctionToken, printAST, readAST} from "./reader-printer.ts"; type MalEnv = Map; function functionToken(fn: (...args: any[]) => any): FunctionToken { return { type: "function", value: fn }; } function isBoolean(v: unknown): v is boolean { return v === true || v === false; } function isNumber(v: unknown): v is number { return typeof v === "number" && isFinite(v); } function isString(v: unknown): v is string { return typeof v === "string" || v instanceof String; } class REPL { private readonly env: MalEnv; constructor(env: MalEnv) { this.env = env; } static mathRepl(): REPL { const mathFunctions = new Map(); mathFunctions.set("+", functionToken((a, b) => a + b)); mathFunctions.set("-", functionToken((a, b) => a - b)); mathFunctions.set("*", functionToken((a, b) => a * b)); mathFunctions.set("/", functionToken((a, b) => a / b)); return new REPL(mathFunctions); } read(source: string): AST { return readAST(source); } cast(value: unknown): AST { if (value === true || value === false) { return { type: "boolean", value: value }; } if (isBoolean(value)) { return { type: "boolean", value: value }; } if (isString(value)) { return { type: "string", value: value }; } if (isNumber(value)) { return { type: "number", value: value }; } throw new Error(`Unknown return type from functon: ${value}`); } eval_(ast: AST): AST { if (ast.type === "list") { const fn = this.eval_(ast.value[0]) as FunctionToken; const ret = fn.value( ...ast.value.slice(1).map((n) => this.eval_(n).value), ); return this.cast(ret); } if (ast.type === "sym") { const ret = this.env.get(ast.value); if (ret === undefined) { throw new Error(`Unknown reference ${ast.value}`); } return ret; } return ast; } print(form: AST): string { return printAST(form); } rep(source: string): string { return this.print(this.eval_(this.read(source))); } } Deno.test("basic", function (): void { const repl = REPL.mathRepl(); assertEquals(repl.rep("(+ 1 1)"), "2"); assertEquals(repl.rep("(+ 2 (* 3 4))"), "14"); });