1
1
mirror of https://github.com/kanaka/mal.git synced 2024-09-20 01:57:09 +03:00

refactor to using const enum

This commit is contained in:
vvakame 2017-02-25 14:12:15 +09:00
parent 92bf05308e
commit 5bb7479da5
12 changed files with 252 additions and 282 deletions

View File

@ -2,7 +2,7 @@ import * as fs from "fs";
import { readline } from "./node_readline";
import { MalType, MalSymbol, MalFunction, MalNull, MalList, MalVector, MalBoolean, MalNumber, MalString, MalKeyword, MalHashMap, MalAtom, equals, isSeq } from "./types";
import { Node, MalType, MalSymbol, MalFunction, MalNull, MalList, MalVector, MalBoolean, MalNumber, MalString, MalKeyword, MalHashMap, MalAtom, equals, isSeq } from "./types";
import { readStr } from "./reader";
import { prStr } from "./printer";
@ -16,34 +16,34 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
},
"nil?"(v: MalType) {
return new MalBoolean(MalNull.is(v));
return new MalBoolean(v.type === Node.Null);
},
"true?"(v: MalType) {
return new MalBoolean(MalBoolean.is(v) && v.v);
return new MalBoolean(v.type === Node.Boolean && v.v);
},
"false?"(v: MalType) {
return new MalBoolean(MalBoolean.is(v) && !v.v);
return new MalBoolean(v.type === Node.Boolean && !v.v);
},
"string?"(v: MalType) {
return new MalBoolean(MalString.is(v));
return new MalBoolean(v.type === Node.String);
},
symbol(v: MalType) {
if (!MalString.is(v)) {
if (v.type !== Node.String) {
throw new Error(`unexpected symbol: ${v.type}, expected: string`);
}
return MalSymbol.get(v.v);
},
"symbol?"(v: MalType) {
return new MalBoolean(MalSymbol.is(v));
return new MalBoolean(v.type === Node.Symbol);
},
keyword(v: MalType) {
if (!MalString.is(v)) {
if (v.type !== Node.String) {
throw new Error(`unexpected symbol: ${v.type}, expected: string`);
}
return MalKeyword.get(v.v);
},
"keyword?"(v: MalType) {
return new MalBoolean(MalKeyword.is(v));
return new MalBoolean(v.type === Node.Keyword);
},
"pr-str"(...args: MalType[]): MalString {
@ -63,13 +63,13 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
return MalNull.instance;
},
"read-string"(v: MalType) {
if (!MalString.is(v)) {
if (v.type !== Node.String) {
throw new Error(`unexpected symbol: ${v.type}, expected: string`);
}
return readStr(v.v);
},
readline(v: MalType) {
if (!MalString.is(v)) {
if (v.type !== Node.String) {
throw new Error(`unexpected symbol: ${v.type}, expected: string`);
}
@ -81,7 +81,7 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
return new MalString(ret);
},
slurp(v: MalType) {
if (!MalString.is(v)) {
if (v.type !== Node.String) {
throw new Error(`unexpected symbol: ${v.type}, expected: string`);
}
const content = fs.readFileSync(v.v, "UTF-8");
@ -89,80 +89,80 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
},
"<"(a: MalType, b: MalType): MalBoolean {
if (!MalNumber.is(a)) {
if (a.type !== Node.Number) {
throw new Error(`unexpected symbol: ${a.type}, expected: number`);
}
if (!MalNumber.is(b)) {
if (b.type !== Node.Number) {
throw new Error(`unexpected symbol: ${b.type}, expected: number`);
}
return new MalBoolean(a.v < b.v);
},
"<="(a: MalType, b: MalType): MalBoolean {
if (!MalNumber.is(a)) {
if (a.type !== Node.Number) {
throw new Error(`unexpected symbol: ${a.type}, expected: number`);
}
if (!MalNumber.is(b)) {
if (b.type !== Node.Number) {
throw new Error(`unexpected symbol: ${b.type}, expected: number`);
}
return new MalBoolean(a.v <= b.v);
},
">"(a: MalType, b: MalType): MalBoolean {
if (!MalNumber.is(a)) {
if (a.type !== Node.Number) {
throw new Error(`unexpected symbol: ${a.type}, expected: number`);
}
if (!MalNumber.is(b)) {
if (b.type !== Node.Number) {
throw new Error(`unexpected symbol: ${b.type}, expected: number`);
}
return new MalBoolean(a.v > b.v);
},
">="(a: MalType, b: MalType): MalBoolean {
if (!MalNumber.is(a)) {
if (a.type !== Node.Number) {
throw new Error(`unexpected symbol: ${a.type}, expected: number`);
}
if (!MalNumber.is(b)) {
if (b.type !== Node.Number) {
throw new Error(`unexpected symbol: ${b.type}, expected: number`);
}
return new MalBoolean(a.v >= b.v);
},
"+"(a: MalType, b: MalType): MalNumber {
if (!MalNumber.is(a)) {
if (a.type !== Node.Number) {
throw new Error(`unexpected symbol: ${a.type}, expected: number`);
}
if (!MalNumber.is(b)) {
if (b.type !== Node.Number) {
throw new Error(`unexpected symbol: ${b.type}, expected: number`);
}
return new MalNumber(a.v + b.v);
},
"-"(a: MalType, b: MalType): MalNumber {
if (!MalNumber.is(a)) {
if (a.type !== Node.Number) {
throw new Error(`unexpected symbol: ${a.type}, expected: number`);
}
if (!MalNumber.is(b)) {
if (b.type !== Node.Number) {
throw new Error(`unexpected symbol: ${b.type}, expected: number`);
}
return new MalNumber(a.v - b.v);
},
"*"(a: MalType, b: MalType): MalNumber {
if (!MalNumber.is(a)) {
if (a.type !== Node.Number) {
throw new Error(`unexpected symbol: ${a.type}, expected: number`);
}
if (!MalNumber.is(b)) {
if (b.type !== Node.Number) {
throw new Error(`unexpected symbol: ${b.type}, expected: number`);
}
return new MalNumber(a.v * b.v);
},
"/"(a: MalType, b: MalType): MalNumber {
if (!MalNumber.is(a)) {
if (a.type !== Node.Number) {
throw new Error(`unexpected symbol: ${a.type}, expected: number`);
}
if (!MalNumber.is(b)) {
if (b.type !== Node.Number) {
throw new Error(`unexpected symbol: ${b.type}, expected: number`);
}
@ -182,61 +182,61 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
return new MalVector(args);
},
"vector?"(v: MalType): MalBoolean {
return new MalBoolean(MalVector.is(v));
return new MalBoolean(v.type === Node.Vector);
},
"hash-map"(...args: MalType[]) {
return new MalHashMap(args);
},
"map?"(v: MalType): MalBoolean {
return new MalBoolean(MalHashMap.is(v));
return new MalBoolean(v.type === Node.HashMap);
},
assoc(v: MalType, ...args: MalType[]) {
if (!MalHashMap.is(v)) {
if (v.type !== Node.HashMap) {
throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
}
return v.assoc(args);
},
dissoc(v: MalType, ...args: MalType[]) {
if (!MalHashMap.is(v)) {
if (v.type !== Node.HashMap) {
throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
}
return v.dissoc(args);
},
get(v: MalType, key: MalType) {
if (MalNull.is(v)) {
if (v.type === Node.Null) {
return MalNull.instance;
}
if (!MalHashMap.is(v)) {
if (v.type !== Node.HashMap) {
throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
}
if (!MalString.is(key) && !MalKeyword.is(key)) {
if (key.type !== Node.String && key.type !== Node.Keyword) {
throw new Error(`unexpected symbol: ${key.type}, expected: string or keyword`);
}
return v.get(key) || MalNull.instance;
},
"contains?"(v: MalType, key: MalType) {
if (MalNull.is(v)) {
if (v.type === Node.Null) {
return MalNull.instance;
}
if (!MalHashMap.is(v)) {
if (v.type !== Node.HashMap) {
throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
}
if (!MalString.is(key) && !MalKeyword.is(key)) {
if (key.type !== Node.String && key.type !== Node.Keyword) {
throw new Error(`unexpected symbol: ${key.type}, expected: string or keyword`);
}
return new MalBoolean(v.has(key));
},
keys(v: MalType) {
if (!MalHashMap.is(v)) {
if (v.type !== Node.HashMap) {
throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
}
return new MalList([...v.keys()]);
},
vals(v: MalType) {
if (!MalHashMap.is(v)) {
if (v.type !== Node.HashMap) {
throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
}
@ -269,7 +269,7 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
if (!isSeq(list)) {
throw new Error(`unexpected symbol: ${list.type}, expected: list or vector`);
}
if (!MalNumber.is(idx)) {
if (idx.type !== Node.Number) {
throw new Error(`unexpected symbol: ${idx.type}, expected: number`);
}
@ -281,7 +281,7 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
return v;
},
first(v: MalType) {
if (MalNull.is(v)) {
if (v.type === Node.Null) {
return MalNull.instance;
}
if (!isSeq(v)) {
@ -291,7 +291,7 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
return v.list[0] || MalNull.instance;
},
rest(v: MalType) {
if (MalNull.is(v)) {
if (v.type === Node.Null) {
return new MalList([]);
}
if (!isSeq(v)) {
@ -310,13 +310,13 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
if (isSeq(v)) {
return new MalNumber(v.list.length);
}
if (MalNull.is(v)) {
if (v.type === Node.Null) {
return new MalNumber(0);
}
throw new Error(`unexpected symbol: ${v.type}`);
},
apply(f: MalType, ...list: MalType[]) {
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
throw new Error(`unexpected symbol: ${f.type}, expected: function`);
}
@ -328,7 +328,7 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
return f.func(...args);
},
map(f: MalType, list: MalType) {
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
throw new Error(`unexpected symbol: ${f.type}, expected: function`);
}
if (!isSeq(list)) {
@ -340,36 +340,36 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
conj(list: MalType, ...args: MalType[]) {
switch (list.type) {
case "list":
case Node.List:
const newList = new MalList(list.list);
args.forEach(arg => newList.list.unshift(arg));
return newList;
case "vector":
case Node.Vector:
return new MalVector([...list.list, ...args]);
}
throw new Error(`unexpected symbol: ${list.type}, expected: list or vector`);
},
seq(v: MalType) {
if (MalList.is(v)) {
if (v.type === Node.List) {
if (v.list.length === 0) {
return MalNull.instance;
}
return v;
}
if (MalVector.is(v)) {
if (v.type === Node.Vector) {
if (v.list.length === 0) {
return MalNull.instance;
}
return new MalList(v.list);
}
if (MalString.is(v)) {
if (v.type === Node.String) {
if (v.v.length === 0) {
return MalNull.instance;
}
return new MalList(v.v.split("").map(s => new MalString(s)));
}
if (MalNull.is(v)) {
if (v.type === Node.Null) {
return MalNull.instance;
}
@ -386,26 +386,26 @@ export const ns: Map<MalSymbol, MalFunction> = (() => {
return new MalAtom(v);
},
"atom?"(v: MalType): MalBoolean {
return new MalBoolean(MalAtom.is(v));
return new MalBoolean(v.type === Node.Atom);
},
deref(v: MalType): MalType {
if (!MalAtom.is(v)) {
if (v.type !== Node.Atom) {
throw new Error(`unexpected symbol: ${v.type}, expected: atom`);
}
return v.v;
},
"reset!"(atom: MalType, v: MalType): MalType {
if (!MalAtom.is(atom)) {
if (atom.type !== Node.Atom) {
throw new Error(`unexpected symbol: ${atom.type}, expected: atom`);
}
atom.v = v;
return v;
},
"swap!"(atom: MalType, f: MalType, ...args: MalType[]): MalType {
if (!MalAtom.is(atom)) {
if (atom.type !== Node.Atom) {
throw new Error(`unexpected symbol: ${atom.type}, expected: atom`);
}
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
throw new Error(`unexpected symbol: ${f.type}, expected: function`);
}
atom.v = f.func(...[atom.v].concat(args));

View File

@ -1,12 +1,12 @@
import { MalType } from "./types";
import { Node, MalType } from "./types";
export function prStr(v: MalType, printReadably = true): string {
switch (v.type) {
case "list":
case Node.List:
return `(${v.list.map(v => prStr(v, printReadably)).join(" ")})`;
case "vector":
case Node.Vector:
return `[${v.list.map(v => prStr(v, printReadably)).join(" ")}]`;
case "hash-map":
case Node.HashMap:
let result = "{";
for (const [key, value] of v.entries()) {
if (result !== "{") {
@ -16,11 +16,11 @@ export function prStr(v: MalType, printReadably = true): string {
}
result += "}";
return result;
case "number":
case "symbol":
case "boolean":
case Node.Number:
case Node.Symbol:
case Node.Boolean:
return `${v.v}`;
case "string":
case Node.String:
if (printReadably) {
const str = v.v
.replace(/\\/g, "\\\\")
@ -30,13 +30,13 @@ export function prStr(v: MalType, printReadably = true): string {
} else {
return v.v;
}
case "null":
case Node.Null:
return "nil";
case "keyword":
case Node.Keyword:
return `:${v.v}`;
case "function":
case Node.Function:
return "#<function>";
case "atom":
case Node.Atom:
return `(atom ${prStr(v.v, printReadably)})`;
}
}

View File

@ -1,6 +1,6 @@
import { readline } from "./node_readline";
import { MalType, MalNumber, MalList, MalVector, MalHashMap, MalFunction } from "./types";
import { Node, MalType, MalNumber, MalList, MalVector, MalHashMap, MalFunction } from "./types";
import { readStr } from "./reader";
import { prStr } from "./printer";
@ -15,17 +15,17 @@ interface MalEnvironment {
function evalAST(ast: MalType, env: MalEnvironment): MalType {
switch (ast.type) {
case "symbol":
case Node.Symbol:
const f = env[ast.v];
if (!f) {
throw new Error(`unknown symbol: ${ast.v}`);
}
return f;
case "list":
case Node.List:
return new MalList(ast.list.map(ast => evalMal(ast, env)));
case "vector":
case Node.Vector:
return new MalVector(ast.list.map(ast => evalMal(ast, env)));
case "hash-map":
case Node.HashMap:
const list: MalType[] = [];
for (const [key, value] of ast.entries()) {
list.push(key);
@ -39,7 +39,7 @@ function evalAST(ast: MalType, env: MalEnvironment): MalType {
// EVAL
function evalMal(ast: MalType, env: MalEnvironment): MalType {
if (ast.type !== "list") {
if (ast.type !== Node.List) {
return evalAST(ast, env);
}
if (ast.list.length === 0) {
@ -47,7 +47,7 @@ function evalMal(ast: MalType, env: MalEnvironment): MalType {
}
const result = evalAST(ast, env) as MalList;
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);

View File

@ -1,6 +1,6 @@
import { readline } from "./node_readline";
import { MalType, MalNumber, MalList, MalVector, MalHashMap, MalSymbol, MalFunction } from "./types";
import { Node, MalType, MalNumber, MalList, MalVector, MalHashMap, MalSymbol, MalFunction } from "./types";
import { Env } from "./env";
import { readStr } from "./reader";
import { prStr } from "./printer";
@ -12,17 +12,17 @@ function read(str: string): MalType {
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":
case Node.List:
return new MalList(ast.list.map(ast => evalMal(ast, env)));
case "vector":
case Node.Vector:
return new MalVector(ast.list.map(ast => evalMal(ast, env)));
case "hash-map":
case Node.HashMap:
const list: MalType[] = [];
for (const [key, value] of ast.entries()) {
list.push(key);
@ -36,7 +36,7 @@ function evalAST(ast: MalType, env: Env): MalType {
// EVAL
function evalMal(ast: MalType, env: Env): MalType {
if (ast.type !== "list") {
if (ast.type !== Node.List) {
return evalAST(ast, env);
}
if (ast.list.length === 0) {
@ -44,7 +44,7 @@ function evalMal(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;
@ -78,7 +78,7 @@ function evalMal(ast: MalType, env: Env): MalType {
}
const result = evalAST(ast, env) as MalList;
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);

View File

@ -1,6 +1,6 @@
import { readline } from "./node_readline";
import { MalType, MalBoolean, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isSeq } from "./types";
import { Node, MalType, MalNull, MalList, MalVector, MalHashMap, MalFunction, isSeq } from "./types";
import { Env } from "./env";
import * as core from "./core";
import { readStr } from "./reader";
@ -13,17 +13,17 @@ function read(str: string): MalType {
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":
case Node.List:
return new MalList(ast.list.map(ast => evalMal(ast, env)));
case "vector":
case Node.Vector:
return new MalVector(ast.list.map(ast => evalMal(ast, env)));
case "hash-map":
case Node.HashMap:
const list: MalType[] = [];
for (const [key, value] of ast.entries()) {
list.push(key);
@ -37,7 +37,7 @@ function evalAST(ast: MalType, env: Env): MalType {
// EVAL
function evalMal(ast: MalType, env: Env): MalType {
if (ast.type !== "list") {
if (ast.type !== Node.List) {
return evalAST(ast, env);
}
if (ast.list.length === 0) {
@ -45,11 +45,11 @@ function evalMal(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) {
@ -66,7 +66,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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) {
@ -89,9 +89,9 @@ function evalMal(ast: MalType, env: Env): MalType {
const [, cond, thenExpr, elseExrp] = ast.list;
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.Null) {
b = false;
}
if (b) {
@ -107,11 +107,11 @@ function evalMal(ast: MalType, env: Env): MalType {
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 evalMal(binds, new Env(env, symbols, fnArgs));
@ -124,7 +124,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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);

View File

@ -1,6 +1,6 @@
import { readline } from "./node_readline";
import { MalType, MalBoolean, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isSeq } from "./types";
import { Node, MalType, MalNull, MalList, MalVector, MalHashMap, MalFunction, isSeq } from "./types";
import { Env } from "./env";
import * as core from "./core";
import { readStr } from "./reader";
@ -13,17 +13,17 @@ function read(str: string): MalType {
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":
case Node.List:
return new MalList(ast.list.map(ast => evalMal(ast, env)));
case "vector":
case Node.Vector:
return new MalVector(ast.list.map(ast => evalMal(ast, env)));
case "hash-map":
case Node.HashMap:
const list: MalType[] = [];
for (const [key, value] of ast.entries()) {
list.push(key);
@ -38,7 +38,7 @@ function evalAST(ast: MalType, env: Env): MalType {
// EVAL
function evalMal(ast: MalType, env: Env): MalType {
loop: while (true) {
if (ast.type !== "list") {
if (ast.type !== Node.List) {
return evalAST(ast, env);
}
if (ast.list.length === 0) {
@ -46,11 +46,11 @@ function evalMal(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) {
@ -67,7 +67,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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) {
@ -89,9 +89,9 @@ function evalMal(ast: MalType, env: Env): MalType {
const [, cond, thenExpr, elseExrp] = ast.list;
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.Null) {
b = false;
}
if (b) {
@ -109,7 +109,7 @@ function evalMal(ast: MalType, env: Env): MalType {
throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);
}
const symbols = params.list.map(param => {
if (!MalSymbol.is(param)) {
if (param.type !== Node.Symbol) {
throw new Error(`unexpected return type: ${param.type}, expected: symbol`);
}
return param;
@ -123,7 +123,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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`);
}
if (f.ast) {

View File

@ -1,6 +1,6 @@
import { readline } from "./node_readline";
import { MalType, MalString, MalBoolean, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isSeq } from "./types";
import { Node, MalType, MalString, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isSeq } from "./types";
import { Env } from "./env";
import * as core from "./core";
import { readStr } from "./reader";
@ -13,17 +13,17 @@ function read(str: string): MalType {
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":
case Node.List:
return new MalList(ast.list.map(ast => evalMal(ast, env)));
case "vector":
case Node.Vector:
return new MalVector(ast.list.map(ast => evalMal(ast, env)));
case "hash-map":
case Node.HashMap:
const list: MalType[] = [];
for (const [key, value] of ast.entries()) {
list.push(key);
@ -38,7 +38,7 @@ function evalAST(ast: MalType, env: Env): MalType {
// EVAL
function evalMal(ast: MalType, env: Env): MalType {
loop: while (true) {
if (ast.type !== "list") {
if (ast.type !== Node.List) {
return evalAST(ast, env);
}
if (ast.list.length === 0) {
@ -46,11 +46,11 @@ function evalMal(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) {
@ -67,7 +67,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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) {
@ -89,9 +89,9 @@ function evalMal(ast: MalType, env: Env): MalType {
const [, cond, thenExpr, elseExrp] = ast.list;
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.Null) {
b = false;
}
if (b) {
@ -109,7 +109,7 @@ function evalMal(ast: MalType, env: Env): MalType {
throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);
}
const symbols = params.list.map(param => {
if (!MalSymbol.is(param)) {
if (param.type !== Node.Symbol) {
throw new Error(`unexpected return type: ${param.type}, expected: symbol`);
}
return param;
@ -123,7 +123,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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`);
}
if (f.ast) {

View File

@ -1,6 +1,6 @@
import { readline } from "./node_readline";
import { MalType, MalString, MalBoolean, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isSeq } from "./types";
import { Node, MalType, MalString, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isSeq } from "./types";
import { Env } from "./env";
import * as core from "./core";
import { readStr } from "./reader";
@ -19,7 +19,7 @@ function quasiquote(ast: MalType): MalType {
throw new Error(`unexpected token type: ${ast.type}, expected: list or vector`);
}
const [arg1, arg2] = ast.list;
if (MalSymbol.is(arg1) && arg1.v === "unquote") {
if (arg1.type === Node.Symbol && arg1.v === "unquote") {
return arg2;
}
if (isPair(arg1)) {
@ -27,7 +27,7 @@ function quasiquote(ast: MalType): MalType {
throw new Error(`unexpected token type: ${arg1.type}, expected: list or vector`);
}
const [arg11, arg12] = arg1.list;
if (MalSymbol.is(arg11) && arg11.v === "splice-unquote") {
if (arg11.type === Node.Symbol && arg11.v === "splice-unquote") {
return new MalList([
MalSymbol.get("concat"),
arg12,
@ -53,17 +53,17 @@ function quasiquote(ast: MalType): MalType {
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":
case Node.List:
return new MalList(ast.list.map(ast => evalMal(ast, env)));
case "vector":
case Node.Vector:
return new MalVector(ast.list.map(ast => evalMal(ast, env)));
case "hash-map":
case Node.HashMap:
const list: MalType[] = [];
for (const [key, value] of ast.entries()) {
list.push(key);
@ -78,7 +78,7 @@ function evalAST(ast: MalType, env: Env): MalType {
// EVAL
function evalMal(ast: MalType, env: Env): MalType {
loop: while (true) {
if (ast.type !== "list") {
if (ast.type !== Node.List) {
return evalAST(ast, env);
}
if (ast.list.length === 0) {
@ -86,11 +86,11 @@ function evalMal(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) {
@ -107,7 +107,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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) {
@ -136,9 +136,9 @@ function evalMal(ast: MalType, env: Env): MalType {
const [, cond, thenExpr, elseExrp] = ast.list;
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.Null) {
b = false;
}
if (b) {
@ -156,7 +156,7 @@ function evalMal(ast: MalType, env: Env): MalType {
throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);
}
const symbols = params.list.map(param => {
if (!MalSymbol.is(param)) {
if (param.type !== Node.Symbol) {
throw new Error(`unexpected return type: ${param.type}, expected: symbol`);
}
return param;
@ -170,7 +170,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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`);
}
if (f.ast) {

View File

@ -1,6 +1,6 @@
import { readline } from "./node_readline";
import { MalType, MalString, MalBoolean, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isSeq } from "./types";
import { Node, MalType, MalString, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isSeq } from "./types";
import { Env } from "./env";
import * as core from "./core";
import { readStr } from "./reader";
@ -19,7 +19,7 @@ function quasiquote(ast: MalType): MalType {
throw new Error(`unexpected token type: ${ast.type}, expected: list or vector`);
}
const [arg1, arg2] = ast.list;
if (MalSymbol.is(arg1) && arg1.v === "unquote") {
if (arg1.type === Node.Symbol && arg1.v === "unquote") {
return arg2;
}
if (isPair(arg1)) {
@ -27,7 +27,7 @@ function quasiquote(ast: MalType): MalType {
throw new Error(`unexpected token type: ${arg1.type}, expected: list or vector`);
}
const [arg11, arg12] = arg1.list;
if (MalSymbol.is(arg11) && arg11.v === "splice-unquote") {
if (arg11.type === Node.Symbol && arg11.v === "splice-unquote") {
return new MalList([
MalSymbol.get("concat"),
arg12,
@ -56,7 +56,7 @@ function isMacro(ast: MalType, env: Env): boolean {
return false;
}
const s = ast.list[0];
if (!MalSymbol.is(s)) {
if (s.type !== Node.Symbol) {
return false;
}
const foundEnv = env.find(s);
@ -65,7 +65,7 @@ function isMacro(ast: MalType, env: Env): boolean {
}
const f = foundEnv.get(s);
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
return false;
}
@ -78,11 +78,11 @@ function macroexpand(ast: MalType, env: Env): MalType {
throw new Error(`unexpected token type: ${ast.type}, expected: list or vector`);
}
const s = ast.list[0];
if (!MalSymbol.is(s)) {
if (s.type !== Node.Symbol) {
throw new Error(`unexpected token type: ${s.type}, expected: symbol`);
}
const f = env.get(s);
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
throw new Error(`unexpected token type: ${f.type}, expected: function`);
}
ast = f.func(...ast.list.slice(1));
@ -93,17 +93,17 @@ function macroexpand(ast: MalType, env: Env): MalType {
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":
case Node.List:
return new MalList(ast.list.map(ast => evalMal(ast, env)));
case "vector":
case Node.Vector:
return new MalVector(ast.list.map(ast => evalMal(ast, env)));
case "hash-map":
case Node.HashMap:
const list: MalType[] = [];
for (const [key, value] of ast.entries()) {
list.push(key);
@ -118,12 +118,12 @@ function evalAST(ast: MalType, env: Env): MalType {
// EVAL
function evalMal(ast: MalType, env: Env): MalType {
loop: while (true) {
if (ast.type !== "list") {
if (ast.type !== Node.List) {
return evalAST(ast, env);
}
ast = macroexpand(ast, env);
if (ast.type !== "list" && ast.type !== "vector") {
if (!isSeq(ast)) {
return evalAST(ast, env);
}
@ -132,11 +132,11 @@ function evalMal(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) {
@ -153,7 +153,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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) {
@ -174,14 +174,14 @@ function evalMal(ast: MalType, env: Env): MalType {
}
case "defmacro!": {
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`);
}
const f = evalMal(value, env);
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
throw new Error(`unexpected token type: ${f.type}, expected: function`);
}
f.isMacro = true;
@ -200,9 +200,9 @@ function evalMal(ast: MalType, env: Env): MalType {
const [, cond, thenExpr, elseExrp] = ast.list;
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.Null) {
b = false;
}
if (b) {
@ -220,7 +220,7 @@ function evalMal(ast: MalType, env: Env): MalType {
throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);
}
const symbols = params.list.map(param => {
if (!MalSymbol.is(param)) {
if (param.type !== Node.Symbol) {
throw new Error(`unexpected return type: ${param.type}, expected: symbol`);
}
return param;
@ -234,7 +234,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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`);
}
if (f.ast) {

View File

@ -1,6 +1,6 @@
import { readline } from "./node_readline";
import { MalType, MalString, MalBoolean, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isAST, isSeq } from "./types";
import { Node, MalType, MalString, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isAST, isSeq } from "./types";
import { Env } from "./env";
import * as core from "./core";
import { readStr } from "./reader";
@ -19,7 +19,7 @@ function quasiquote(ast: MalType): MalType {
throw new Error(`unexpected token type: ${ast.type}, expected: list or vector`);
}
const [arg1, arg2] = ast.list;
if (MalSymbol.is(arg1) && arg1.v === "unquote") {
if (arg1.type === Node.Symbol && arg1.v === "unquote") {
return arg2;
}
if (isPair(arg1)) {
@ -27,7 +27,7 @@ function quasiquote(ast: MalType): MalType {
throw new Error(`unexpected token type: ${arg1.type}, expected: list or vector`);
}
const [arg11, arg12] = arg1.list;
if (MalSymbol.is(arg11) && arg11.v === "splice-unquote") {
if (arg11.type === Node.Symbol && arg11.v === "splice-unquote") {
return new MalList([
MalSymbol.get("concat"),
arg12,
@ -56,7 +56,7 @@ function isMacro(ast: MalType, env: Env): boolean {
return false;
}
const s = ast.list[0];
if (!MalSymbol.is(s)) {
if (s.type !== Node.Symbol) {
return false;
}
const foundEnv = env.find(s);
@ -65,7 +65,7 @@ function isMacro(ast: MalType, env: Env): boolean {
}
const f = foundEnv.get(s);
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
return false;
}
@ -78,11 +78,11 @@ function macroexpand(ast: MalType, env: Env): MalType {
throw new Error(`unexpected token type: ${ast.type}, expected: list or vector`);
}
const s = ast.list[0];
if (!MalSymbol.is(s)) {
if (s.type !== Node.Symbol) {
throw new Error(`unexpected token type: ${s.type}, expected: symbol`);
}
const f = env.get(s);
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
throw new Error(`unexpected token type: ${f.type}, expected: function`);
}
ast = f.func(...ast.list.slice(1));
@ -93,17 +93,17 @@ function macroexpand(ast: MalType, env: Env): MalType {
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":
case Node.List:
return new MalList(ast.list.map(ast => evalMal(ast, env)));
case "vector":
case Node.Vector:
return new MalVector(ast.list.map(ast => evalMal(ast, env)));
case "hash-map":
case Node.HashMap:
const list: MalType[] = [];
for (const [key, value] of ast.entries()) {
list.push(key);
@ -118,12 +118,12 @@ function evalAST(ast: MalType, env: Env): MalType {
// EVAL
function evalMal(ast: MalType, env: Env): MalType {
loop: while (true) {
if (ast.type !== "list") {
if (ast.type !== Node.List) {
return evalAST(ast, env);
}
ast = macroexpand(ast, env);
if (ast.type !== "list" && ast.type !== "vector") {
if (!isSeq(ast)) {
return evalAST(ast, env);
}
@ -132,11 +132,11 @@ function evalMal(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) {
@ -153,7 +153,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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) {
@ -174,14 +174,14 @@ function evalMal(ast: MalType, env: Env): MalType {
}
case "defmacro!": {
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`);
}
const f = evalMal(value, env);
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
throw new Error(`unexpected token type: ${f.type}, expected: function`);
}
f.isMacro = true;
@ -199,9 +199,9 @@ function evalMal(ast: MalType, env: Env): MalType {
throw new Error(`unexpected return type: ${catchBody.type}, expected: list or vector`);
}
const catchSymbol = catchBody.list[0];
if (MalSymbol.is(catchSymbol) && catchSymbol.v === "catch*") {
if (catchSymbol.type === Node.Symbol && catchSymbol.v === "catch*") {
const errorSymbol = catchBody.list[1];
if (!MalSymbol.is(errorSymbol)) {
if (errorSymbol.type !== Node.Symbol) {
throw new Error(`unexpected return type: ${errorSymbol.type}, expected: symbol`);
}
if (!isAST(e)) {
@ -222,9 +222,9 @@ function evalMal(ast: MalType, env: Env): MalType {
const [, cond, thenExpr, elseExrp] = ast.list;
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.Null) {
b = false;
}
if (b) {
@ -242,7 +242,7 @@ function evalMal(ast: MalType, env: Env): MalType {
throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);
}
const symbols = params.list.map(param => {
if (!MalSymbol.is(param)) {
if (param.type !== Node.Symbol) {
throw new Error(`unexpected return type: ${param.type}, expected: symbol`);
}
return param;
@ -256,7 +256,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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`);
}
if (f.ast) {

View File

@ -1,6 +1,6 @@
import { readline } from "./node_readline";
import { MalType, MalString, MalBoolean, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isAST, isSeq } from "./types";
import { Node, MalType, MalString, MalNull, MalList, MalVector, MalHashMap, MalSymbol, MalFunction, isAST, isSeq } from "./types";
import { Env } from "./env";
import * as core from "./core";
import { readStr } from "./reader";
@ -19,7 +19,7 @@ function quasiquote(ast: MalType): MalType {
throw new Error(`unexpected token type: ${ast.type}, expected: list or vector`);
}
const [arg1, arg2] = ast.list;
if (MalSymbol.is(arg1) && arg1.v === "unquote") {
if (arg1.type === Node.Symbol && arg1.v === "unquote") {
return arg2;
}
if (isPair(arg1)) {
@ -27,7 +27,7 @@ function quasiquote(ast: MalType): MalType {
throw new Error(`unexpected token type: ${arg1.type}, expected: list or vector`);
}
const [arg11, arg12] = arg1.list;
if (MalSymbol.is(arg11) && arg11.v === "splice-unquote") {
if (arg11.type === Node.Symbol && arg11.v === "splice-unquote") {
return new MalList([
MalSymbol.get("concat"),
arg12,
@ -56,7 +56,7 @@ function isMacro(ast: MalType, env: Env): boolean {
return false;
}
const s = ast.list[0];
if (!MalSymbol.is(s)) {
if (s.type !== Node.Symbol) {
return false;
}
const foundEnv = env.find(s);
@ -65,7 +65,7 @@ function isMacro(ast: MalType, env: Env): boolean {
}
const f = foundEnv.get(s);
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
return false;
}
@ -78,11 +78,11 @@ function macroexpand(ast: MalType, env: Env): MalType {
throw new Error(`unexpected token type: ${ast.type}, expected: list or vector`);
}
const s = ast.list[0];
if (!MalSymbol.is(s)) {
if (s.type !== Node.Symbol) {
throw new Error(`unexpected token type: ${s.type}, expected: symbol`);
}
const f = env.get(s);
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
throw new Error(`unexpected token type: ${f.type}, expected: function`);
}
ast = f.func(...ast.list.slice(1));
@ -93,17 +93,17 @@ function macroexpand(ast: MalType, env: Env): MalType {
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":
case Node.List:
return new MalList(ast.list.map(ast => evalMal(ast, env)));
case "vector":
case Node.Vector:
return new MalVector(ast.list.map(ast => evalMal(ast, env)));
case "hash-map":
case Node.HashMap:
const list: MalType[] = [];
for (const [key, value] of ast.entries()) {
list.push(key);
@ -118,12 +118,12 @@ function evalAST(ast: MalType, env: Env): MalType {
// EVAL
function evalMal(ast: MalType, env: Env): MalType {
loop: while (true) {
if (ast.type !== "list") {
if (ast.type !== Node.List) {
return evalAST(ast, env);
}
ast = macroexpand(ast, env);
if (ast.type !== "list" && ast.type !== "vector") {
if (!isSeq(ast)) {
return evalAST(ast, env);
}
@ -132,11 +132,11 @@ function evalMal(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) {
@ -153,7 +153,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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) {
@ -174,14 +174,14 @@ function evalMal(ast: MalType, env: Env): MalType {
}
case "defmacro!": {
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`);
}
const f = evalMal(value, env);
if (!MalFunction.is(f)) {
if (f.type !== Node.Function) {
throw new Error(`unexpected token type: ${f.type}, expected: function`);
}
f.isMacro = true;
@ -199,9 +199,9 @@ function evalMal(ast: MalType, env: Env): MalType {
throw new Error(`unexpected return type: ${catchBody.type}, expected: list or vector`);
}
const catchSymbol = catchBody.list[0];
if (MalSymbol.is(catchSymbol) && catchSymbol.v === "catch*") {
if (catchSymbol.type === Node.Symbol && catchSymbol.v === "catch*") {
const errorSymbol = catchBody.list[1];
if (!MalSymbol.is(errorSymbol)) {
if (errorSymbol.type !== Node.Symbol) {
throw new Error(`unexpected return type: ${errorSymbol.type}, expected: symbol`);
}
if (!isAST(e)) {
@ -222,9 +222,9 @@ function evalMal(ast: MalType, env: Env): MalType {
const [, cond, thenExpr, elseExrp] = ast.list;
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.Null) {
b = false;
}
if (b) {
@ -242,7 +242,7 @@ function evalMal(ast: MalType, env: Env): MalType {
throw new Error(`unexpected return type: ${params.type}, expected: list or vector`);
}
const symbols = params.list.map(param => {
if (!MalSymbol.is(param)) {
if (param.type !== Node.Symbol) {
throw new Error(`unexpected return type: ${param.type}, expected: symbol`);
}
return param;
@ -256,7 +256,7 @@ function evalMal(ast: MalType, env: Env): MalType {
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`);
}
if (f.ast) {

View File

@ -2,18 +2,32 @@ import { Env } from "./env";
export type MalType = MalList | MalNumber | MalString | MalNull | MalBoolean | MalSymbol | MalKeyword | MalVector | MalHashMap | MalFunction | MalAtom;
export const enum Node {
List = 1,
Number,
String,
Null,
Boolean,
Symbol,
Keyword,
Vector,
HashMap,
Function,
Atom,
}
export function equals(a: MalType, b: MalType, strict?: boolean): boolean {
if (strict && a.type !== b.type) {
return false;
}
if (MalNull.is(a) && MalNull.is(b)) {
if (a.type === Node.Null && b.type === Node.Null) {
return true;
}
if (isSeq(a) && isSeq(b)) {
return listEquals(a.list, b.list);
}
if (MalHashMap.is(a) && MalHashMap.is(b)) {
if (a.type === Node.HashMap && b.type === Node.HashMap) {
if (a.keywordMap.size !== b.keywordMap.size) {
return false;
}
@ -21,11 +35,11 @@ export function equals(a: MalType, b: MalType, strict?: boolean): boolean {
return false;
}
for (const [aK, aV] of a.entries()) {
if (!MalString.is(aK) && !MalKeyword.is(aK)) {
if (aK.type !== Node.String && aK.type !== Node.Keyword) {
throw new Error(`unexpected symbol: ${aK.type}, expected: string or keyword`);
}
const bV = b.get(aK);
if (MalNull.is(aV) && MalNull.is(bV)) {
if (aV.type === Node.Null && bV.type === Node.Null) {
continue;
}
if (!equals(aV, bV)) {
@ -36,11 +50,11 @@ export function equals(a: MalType, b: MalType, strict?: boolean): boolean {
return true;
}
if (
(MalNumber.is(a) && MalNumber.is(b))
|| (MalString.is(a) && MalString.is(b))
|| (MalBoolean.is(a) && MalBoolean.is(b))
|| (MalSymbol.is(a) && MalSymbol.is(b))
|| (MalKeyword.is(a) && MalKeyword.is(b))
(a.type === Node.Number && b.type === Node.Number)
|| (a.type === Node.String && b.type === Node.String)
|| (a.type === Node.Boolean && b.type === Node.Boolean)
|| (a.type === Node.Symbol && b.type === Node.Symbol)
|| (a.type === Node.Keyword && b.type === Node.Keyword)
) {
return a.v === b.v;
}
@ -61,7 +75,7 @@ export function equals(a: MalType, b: MalType, strict?: boolean): boolean {
}
export function isSeq(ast: MalType): ast is MalList | MalVector {
return MalList.is(ast) || MalVector.is(ast);
return ast.type === Node.List || ast.type === Node.Vector;
}
export function isAST(v: MalType): v is MalType {
@ -69,11 +83,7 @@ export function isAST(v: MalType): v is MalType {
}
export class MalList {
static is(f: MalType): f is MalList {
return f instanceof MalList;
}
type: "list" = "list";
type: Node.List = Node.List;
meta?: MalType;
constructor(public list: MalType[]) {
@ -87,11 +97,7 @@ export class MalList {
}
export class MalNumber {
static is(f: MalType): f is MalNumber {
return f instanceof MalNumber;
}
type: "number" = "number";
type: Node.Number = Node.Number;
meta?: MalType;
constructor(public v: number) {
@ -105,11 +111,7 @@ export class MalNumber {
}
export class MalString {
static is(f: MalType): f is MalString {
return f instanceof MalString;
}
type: "string" = "string";
type: Node.String = Node.String;
meta?: MalType;
constructor(public v: string) {
@ -123,13 +125,10 @@ export class MalString {
}
export class MalNull {
static is(f: MalType): f is MalNull {
return f instanceof MalNull;
}
static instance = new MalNull();
type: "null" = "null";
type: Node.Null = Node.Null;
meta?: MalType;
private constructor() { }
@ -140,11 +139,7 @@ export class MalNull {
}
export class MalBoolean {
static is(f: MalType): f is MalBoolean {
return f instanceof MalBoolean;
}
type: "boolean" = "boolean";
type: Node.Boolean = Node.Boolean;
meta?: MalType;
constructor(public v: boolean) {
@ -158,10 +153,6 @@ export class MalBoolean {
}
export class MalSymbol {
static is(f: MalType): f is MalSymbol {
return f instanceof MalSymbol;
}
static map = new Map<symbol, MalSymbol>();
static get(name: string): MalSymbol {
@ -175,7 +166,7 @@ export class MalSymbol {
return token;
}
type: "symbol" = "symbol";
type: Node.Symbol = Node.Symbol;
meta?: MalType;
private constructor(public v: string) {
@ -187,10 +178,6 @@ export class MalSymbol {
}
export class MalKeyword {
static is(f: MalType): f is MalKeyword {
return f instanceof MalKeyword;
}
static map = new Map<symbol, MalKeyword>();
static get(name: string): MalKeyword {
@ -204,7 +191,7 @@ export class MalKeyword {
return token;
}
type: "keyword" = "keyword";
type: Node.Keyword = Node.Keyword;
meta?: MalType;
private constructor(public v: string) {
@ -216,11 +203,7 @@ export class MalKeyword {
}
export class MalVector {
static is(f: MalType): f is MalVector {
return f instanceof MalVector;
}
type: "vector" = "vector";
type: Node.Vector = Node.Vector;
meta?: MalType;
constructor(public list: MalType[]) {
@ -234,11 +217,7 @@ export class MalVector {
}
export class MalHashMap {
static is(f: MalType): f is MalHashMap {
return f instanceof MalHashMap;
}
type: "hash-map" = "hash-map";
type: Node.HashMap = Node.HashMap;
stringMap: { [key: string]: MalType } = {};
keywordMap = new Map<MalType, MalType>();
meta?: MalType;
@ -250,9 +229,9 @@ export class MalHashMap {
if (value == null) {
throw new Error("unexpected hash length");
}
if (MalKeyword.is(key)) {
if (key.type === Node.Keyword) {
this.keywordMap.set(key, value);
} else if (MalString.is(key)) {
} else if (key.type === Node.String) {
this.stringMap[key.v] = value;
} else {
throw new Error(`unexpected key symbol: ${key.type}, expected: keyword or string`);
@ -267,14 +246,14 @@ export class MalHashMap {
}
has(key: MalKeyword | MalString) {
if (MalKeyword.is(key)) {
if (key.type === Node.Keyword) {
return !!this.keywordMap.get(key);
}
return !!this.stringMap[key.v];
}
get(key: MalKeyword | MalString) {
if (MalKeyword.is(key)) {
if (key.type === Node.Keyword) {
return this.keywordMap.get(key) || MalNull.instance;
}
return this.stringMap[key.v] || MalNull.instance;
@ -327,9 +306,9 @@ export class MalHashMap {
const newHashMap = this.assoc([]);
args.forEach(arg => {
if (MalString.is(arg)) {
if (arg.type === Node.String) {
delete newHashMap.stringMap[arg.v];
} else if (MalKeyword.is(arg)) {
} else if (arg.type === Node.Keyword) {
newHashMap.keywordMap.delete(arg);
} else {
throw new Error(`unexpected symbol: ${arg.type}, expected: keyword or string`);
@ -342,10 +321,6 @@ export class MalHashMap {
type MalF = (...args: (MalType | undefined)[]) => MalType;
export class MalFunction {
static is(f: MalType): f is MalFunction {
return f instanceof MalFunction;
}
static fromLisp(evalMal: (ast: MalType, env: Env) => MalType, env: Env, params: MalSymbol[], bodyAst: MalType): MalFunction {
const f = new MalFunction();
f.func = (...args) => evalMal(bodyAst, new Env(env, params, checkUndefined(args)));
@ -374,8 +349,7 @@ export class MalFunction {
return f;
}
type: "function" = "function";
type: Node.Function = Node.Function;
func: MalF;
ast: MalType;
env: Env;
@ -403,11 +377,7 @@ export class MalFunction {
}
export class MalAtom {
static is(f: MalType): f is MalAtom {
return f instanceof MalAtom;
}
type: "atom" = "atom";
type: Node.Atom = Node.Atom;
meta?: MalType;
constructor(public v: MalType) {