1 import * as fs from "fs";
3 import { readline } from "./node_readline";
5 import { Node, MalType, MalSymbol, MalFunction, MalNull, MalList, MalVector, MalBoolean, MalNumber, MalString, MalKeyword, MalHashMap, MalAtom, equals, isSeq } from "./types";
6 import { readStr } from "./reader";
7 import { prStr } from "./printer";
9 export const ns: Map<MalSymbol, MalFunction> = (() => {
10 const ns: { [symbol: string]: typeof MalFunction.prototype.func; } = {
11 "="(a: MalType, b: MalType): MalBoolean {
12 return new MalBoolean(equals(a, b));
14 throw(v: MalType): MalType {
19 return new MalBoolean(v.type === Node.Null);
22 return new MalBoolean(v.type === Node.Boolean && v.v);
24 "false?"(v: MalType) {
25 return new MalBoolean(v.type === Node.Boolean && !v.v);
27 "string?"(v: MalType) {
28 return new MalBoolean(v.type === Node.String);
31 if (v.type !== Node.String) {
32 throw new Error(`unexpected symbol: ${v.type}, expected: string`);
34 return MalSymbol.get(v.v);
36 "symbol?"(v: MalType) {
37 return new MalBoolean(v.type === Node.Symbol);
40 if (v.type !== Node.String) {
41 throw new Error(`unexpected symbol: ${v.type}, expected: string`);
43 return MalKeyword.get(v.v);
45 "keyword?"(v: MalType) {
46 return new MalBoolean(v.type === Node.Keyword);
49 "pr-str"(...args: MalType[]): MalString {
50 return new MalString(args.map(v => prStr(v, true)).join(" "));
52 "str"(...args: MalType[]): MalString {
53 return new MalString(args.map(v => prStr(v, false)).join(""));
55 prn(...args: MalType[]): MalNull {
56 const str = args.map(v => prStr(v, true)).join(" ");
58 return MalNull.instance;
60 println(...args: MalType[]): MalNull {
61 const str = args.map(v => prStr(v, false)).join(" ");
63 return MalNull.instance;
65 "read-string"(v: MalType) {
66 if (v.type !== Node.String) {
67 throw new Error(`unexpected symbol: ${v.type}, expected: string`);
71 readline(v: MalType) {
72 if (v.type !== Node.String) {
73 throw new Error(`unexpected symbol: ${v.type}, expected: string`);
76 const ret = readline(v.v);
78 return MalNull.instance;
81 return new MalString(ret);
84 if (v.type !== Node.String) {
85 throw new Error(`unexpected symbol: ${v.type}, expected: string`);
87 const content = fs.readFileSync(v.v, "UTF-8");
88 return new MalString(content);
91 "<"(a: MalType, b: MalType): MalBoolean {
92 if (a.type !== Node.Number) {
93 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
95 if (b.type !== Node.Number) {
96 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
99 return new MalBoolean(a.v < b.v);
101 "<="(a: MalType, b: MalType): MalBoolean {
102 if (a.type !== Node.Number) {
103 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
105 if (b.type !== Node.Number) {
106 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
109 return new MalBoolean(a.v <= b.v);
111 ">"(a: MalType, b: MalType): MalBoolean {
112 if (a.type !== Node.Number) {
113 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
115 if (b.type !== Node.Number) {
116 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
119 return new MalBoolean(a.v > b.v);
121 ">="(a: MalType, b: MalType): MalBoolean {
122 if (a.type !== Node.Number) {
123 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
125 if (b.type !== Node.Number) {
126 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
129 return new MalBoolean(a.v >= b.v);
131 "+"(a: MalType, b: MalType): MalNumber {
132 if (a.type !== Node.Number) {
133 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
135 if (b.type !== Node.Number) {
136 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
139 return new MalNumber(a.v + b.v);
141 "-"(a: MalType, b: MalType): MalNumber {
142 if (a.type !== Node.Number) {
143 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
145 if (b.type !== Node.Number) {
146 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
149 return new MalNumber(a.v - b.v);
151 "*"(a: MalType, b: MalType): MalNumber {
152 if (a.type !== Node.Number) {
153 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
155 if (b.type !== Node.Number) {
156 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
159 return new MalNumber(a.v * b.v);
161 "/"(a: MalType, b: MalType): MalNumber {
162 if (a.type !== Node.Number) {
163 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
165 if (b.type !== Node.Number) {
166 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
169 return new MalNumber(a.v / b.v);
172 return new MalNumber(Date.now());
175 list(...args: MalType[]): MalList {
176 return new MalList(args);
178 "list?"(v: MalType): MalBoolean {
179 return new MalBoolean(v instanceof MalList);
181 vector(...args: MalType[]): MalVector {
182 return new MalVector(args);
184 "vector?"(v: MalType): MalBoolean {
185 return new MalBoolean(v.type === Node.Vector);
187 "hash-map"(...args: MalType[]) {
188 return new MalHashMap(args);
190 "map?"(v: MalType): MalBoolean {
191 return new MalBoolean(v.type === Node.HashMap);
193 assoc(v: MalType, ...args: MalType[]) {
194 if (v.type !== Node.HashMap) {
195 throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
197 return v.assoc(args);
199 dissoc(v: MalType, ...args: MalType[]) {
200 if (v.type !== Node.HashMap) {
201 throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
203 return v.dissoc(args);
205 get(v: MalType, key: MalType) {
206 if (v.type === Node.Null) {
207 return MalNull.instance;
209 if (v.type !== Node.HashMap) {
210 throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
212 if (key.type !== Node.String && key.type !== Node.Keyword) {
213 throw new Error(`unexpected symbol: ${key.type}, expected: string or keyword`);
216 return v.get(key) || MalNull.instance;
218 "contains?"(v: MalType, key: MalType) {
219 if (v.type === Node.Null) {
220 return MalNull.instance;
222 if (v.type !== Node.HashMap) {
223 throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
225 if (key.type !== Node.String && key.type !== Node.Keyword) {
226 throw new Error(`unexpected symbol: ${key.type}, expected: string or keyword`);
229 return new MalBoolean(v.has(key));
232 if (v.type !== Node.HashMap) {
233 throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
236 return new MalList([...v.keys()]);
239 if (v.type !== Node.HashMap) {
240 throw new Error(`unexpected symbol: ${v.type}, expected: hash-map`);
243 return new MalList([...v.vals()]);
246 "sequential?"(v: MalType) {
247 return new MalBoolean(isSeq(v));
249 cons(a: MalType, b: MalType) {
251 throw new Error(`unexpected symbol: ${b.type}, expected: list or vector`);
254 return new MalList([a].concat(b.list));
256 concat(...args: MalType[]) {
260 throw new Error(`unexpected symbol: ${arg.type}, expected: list or vector`);
264 .reduce((p, c) => p.concat(c.list), [] as MalType[]);
266 return new MalList(list);
268 nth(list: MalType, idx: MalType) {
270 throw new Error(`unexpected symbol: ${list.type}, expected: list or vector`);
272 if (idx.type !== Node.Number) {
273 throw new Error(`unexpected symbol: ${idx.type}, expected: number`);
276 const v = list.list[idx.v];
278 throw new Error("nth: index out of range");
284 if (v.type === Node.Null) {
285 return MalNull.instance;
288 throw new Error(`unexpected symbol: ${v.type}, expected: list or vector`);
291 return v.list[0] || MalNull.instance;
294 if (v.type === Node.Null) {
295 return new MalList([]);
298 throw new Error(`unexpected symbol: ${v.type}, expected: list or vector`);
301 return new MalList(v.list.slice(1));
303 "empty?"(v: MalType): MalBoolean {
305 return new MalBoolean(false);
307 return new MalBoolean(v.list.length === 0);
309 count(v: MalType): MalNumber {
311 return new MalNumber(v.list.length);
313 if (v.type === Node.Null) {
314 return new MalNumber(0);
316 throw new Error(`unexpected symbol: ${v.type}`);
318 apply(f: MalType, ...list: MalType[]) {
319 if (f.type !== Node.Function) {
320 throw new Error(`unexpected symbol: ${f.type}, expected: function`);
323 const tail = list[list.length - 1];
325 throw new Error(`unexpected symbol: ${tail.type}, expected: list or vector`);
327 const args = list.slice(0, -1).concat(tail.list);
328 return f.func(...args);
330 map(f: MalType, list: MalType) {
331 if (f.type !== Node.Function) {
332 throw new Error(`unexpected symbol: ${f.type}, expected: function`);
335 throw new Error(`unexpected symbol: ${list.type}, expected: list or vector`);
338 return new MalList(list.list.map(v => f.func(v)));
341 conj(list: MalType, ...args: MalType[]) {
344 const newList = new MalList(list.list);
345 args.forEach(arg => newList.list.unshift(arg));
348 return new MalVector([...list.list, ...args]);
351 throw new Error(`unexpected symbol: ${list.type}, expected: list or vector`);
354 if (v.type === Node.List) {
355 if (v.list.length === 0) {
356 return MalNull.instance;
360 if (v.type === Node.Vector) {
361 if (v.list.length === 0) {
362 return MalNull.instance;
364 return new MalList(v.list);
366 if (v.type === Node.String) {
367 if (v.v.length === 0) {
368 return MalNull.instance;
370 return new MalList(v.v.split("").map(s => new MalString(s)));
372 if (v.type === Node.Null) {
373 return MalNull.instance;
376 throw new Error(`unexpected symbol: ${v.type}, expected: list or vector or string`);
380 return v.meta || MalNull.instance;
382 "with-meta"(v: MalType, m: MalType) {
383 return v.withMeta(m);
385 atom(v: MalType): MalAtom {
386 return new MalAtom(v);
388 "atom?"(v: MalType): MalBoolean {
389 return new MalBoolean(v.type === Node.Atom);
391 deref(v: MalType): MalType {
392 if (v.type !== Node.Atom) {
393 throw new Error(`unexpected symbol: ${v.type}, expected: atom`);
397 "reset!"(atom: MalType, v: MalType): MalType {
398 if (atom.type !== Node.Atom) {
399 throw new Error(`unexpected symbol: ${atom.type}, expected: atom`);
404 "swap!"(atom: MalType, f: MalType, ...args: MalType[]): MalType {
405 if (atom.type !== Node.Atom) {
406 throw new Error(`unexpected symbol: ${atom.type}, expected: atom`);
408 if (f.type !== Node.Function) {
409 throw new Error(`unexpected symbol: ${f.type}, expected: function`);
411 atom.v = f.func(...[atom.v].concat(args));
416 const map = new Map<MalSymbol, MalFunction>();
417 Object.keys(ns).forEach(key => map.set(MalSymbol.get(key), MalFunction.fromBootstrap(ns[key])));