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:
parent
92bf05308e
commit
5bb7479da5
114
ts/core.ts
114
ts/core.ts
@ -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));
|
||||
|
@ -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)})`;
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
112
ts/types.ts
112
ts/types.ts
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user