X-Git-Url: http://git.hcoop.net/jackhill/mal.git/blobdiff_plain/10f8aa846cbcacf6b23c2499bff4cbfa8caa64cf..976e03b912cf83bfedf99c571023498a9b5a8e40:/ts/step4_if_fn_do.ts diff --git a/ts/step4_if_fn_do.ts b/ts/step4_if_fn_do.ts index 5863e717..fd42ed73 100644 --- a/ts/step4_if_fn_do.ts +++ b/ts/step4_if_fn_do.ts @@ -1,32 +1,33 @@ import { readline } from "./node_readline"; -import { MalType, MalBoolean, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction } from "./types"; +import { Node, MalType, MalNil, MalList, MalVector, MalHashMap, MalFunction, isAST, isSeq } from "./types"; import { Env } from "./env"; import * as core from "./core"; import { readStr } from "./reader"; import { prStr } from "./printer"; +// READ function read(str: string): MalType { return readStr(str); } function evalAST(ast: MalType, env: Env): MalType { switch (ast.type) { - case "symbol": + case Node.Symbol: const f = env.get(ast); if (!f) { throw new Error(`unknown symbol: ${ast.v}`); } return f; - case "list": - return new MalList(ast.list.map(ast => evalSexp(ast, env))); - case "vector": - return new MalVector(ast.list.map(ast => evalSexp(ast, env))); - case "hash-map": + case Node.List: + return new MalList(ast.list.map(ast => evalMal(ast, env))); + case Node.Vector: + return new MalVector(ast.list.map(ast => evalMal(ast, env))); + case Node.HashMap: const list: MalType[] = []; for (const [key, value] of ast.entries()) { list.push(key); - list.push(evalSexp(value, env)); + list.push(evalMal(value, env)); } return new MalHashMap(list); default: @@ -34,8 +35,9 @@ function evalAST(ast: MalType, env: Env): MalType { } } -function evalSexp(ast: MalType, env: Env): MalType { - if (ast.type !== "list") { +// EVAL +function evalMal(ast: MalType, env: Env): MalType { + if (ast.type !== Node.List) { return evalAST(ast, env); } if (ast.list.length === 0) { @@ -43,107 +45,109 @@ function evalSexp(ast: MalType, env: Env): MalType { } const first = ast.list[0]; switch (first.type) { - case "symbol": + case Node.Symbol: switch (first.v) { case "def!": { const [, key, value] = ast.list; - if (!MalSymbol.is(key)) { + if (key.type !== Node.Symbol) { throw new Error(`unexpected token type: ${key.type}, expected: symbol`); } if (!value) { throw new Error(`unexpected syntax`); } - return env.set(key, evalSexp(value, env)) + return env.set(key, evalMal(value, env)); } case "let*": { let letEnv = new Env(env); const pairs = ast.list[1]; - if (!MalList.is(pairs) && !MalVector.is(pairs)) { + if (!isSeq(pairs)) { throw new Error(`unexpected token type: ${pairs.type}, expected: list or vector`); } for (let i = 0; i < pairs.list.length; i += 2) { const key = pairs.list[i]; const value = pairs.list[i + 1]; - if (!MalSymbol.is(key)) { + if (key.type !== Node.Symbol) { throw new Error(`unexpected token type: ${key.type}, expected: symbol`); } if (!key || !value) { throw new Error(`unexpected syntax`); } - letEnv.set(key, evalSexp(value, letEnv)); + letEnv.set(key, evalMal(value, letEnv)); } - return evalSexp(ast.list[2], letEnv); + return evalMal(ast.list[2], letEnv); } case "do": { const [, ...list] = ast.list; const ret = evalAST(new MalList(list), env); - if (!MalList.is(ret) && !MalVector.is(ret)) { + if (!isSeq(ret)) { throw new Error(`unexpected return type: ${ret.type}, expected: list or vector`); } return ret.list[ret.list.length - 1]; } case "if": { const [, cond, thenExpr, elseExrp] = ast.list; - const ret = evalSexp(cond, env); + const ret = evalMal(cond, env); let b = true; - if (MalBoolean.is(ret) && !ret.v) { + if (ret.type === Node.Boolean && !ret.v) { b = false; - } else if (MalNull.is(ret)) { + } else if (ret.type === Node.Nil) { b = false; } if (b) { - return evalSexp(thenExpr, env); + return evalMal(thenExpr, env); } else if (elseExrp) { - return evalSexp(elseExrp, env); + return evalMal(elseExrp, env); } else { - return MalNull.instance; + return MalNil.instance; } } case "fn*": { const [, args, binds] = ast.list; - if (!MalList.is(args) && !MalVector.is(args)) { + if (!isSeq(args)) { throw new Error(`unexpected return type: ${args.type}, expected: list or vector`); } - const symbols = args.list.map(arg => { - if (!MalSymbol.is(arg)) { - throw new Error(`unexpected return type: ${arg.type}, expected: symbol`); + const symbols = args.list.map(param => { + if (param.type !== Node.Symbol) { + throw new Error(`unexpected return type: ${param.type}, expected: symbol`); } - return arg; + return param; }); return MalFunction.fromBootstrap((...fnArgs: MalType[]) => { - return evalSexp(binds, new Env(env, symbols, fnArgs)); + return evalMal(binds, new Env(env, symbols, fnArgs)); }); } } } const result = evalAST(ast, env); - if (!MalList.is(result) && !MalVector.is(result)) { + if (!isSeq(result)) { throw new Error(`unexpected return type: ${result.type}, expected: list or vector`); } const [f, ...args] = result.list; - if (!MalFunction.is(f)) { + if (f.type !== Node.Function) { throw new Error(`unexpected token: ${f.type}, expected: function`); } return f.func(...args); } +// PRINT function print(exp: MalType): string { return prStr(exp); } const replEnv = new Env(); -for (const [key, value] of core.ns) { - replEnv.set(key, value); +function rep(str: string): string { + return print(evalMal(read(str), replEnv)); } +// core.EXT: defined using Racket +core.ns.forEach((value, key) => { + replEnv.set(key, value); +}); + // core.mal: defined using the language itself rep("(def! not (fn* (a) (if a false true)))"); -function rep(str: string): string { - return print(evalSexp(read(str), replEnv)); -} - while (true) { const line = readline("user> "); if (line == null) { @@ -155,7 +159,11 @@ while (true) { try { console.log(rep(line)); } catch (e) { - const err: Error = e; - console.error(err.message); + if (isAST(e)) { + console.error("Error:", prStr(e)); + } else { + const err: Error = e; + console.error("Error:", err.message); + } } }