TypeScript: step 7
[jackhill/mal.git] / ts / core.ts
CommitLineData
555f7fc7 1import * as fs from "fs";
2
3import { MalType, MalSymbol, MalFunction, MalNull, MalList, MalVector, MalBoolean, MalNumber, MalString, MalAtom, equals } from "./types";
4import { readStr } from "./reader";
dfe70453 5import { prStr } from "./printer";
6
7export const ns: Map<MalSymbol, MalFunction> = (() => {
8 const ns: { [symbol: string]: typeof MalFunction.prototype.func; } = {
9 "pr-str"(...args: MalType[]): MalString {
10 return new MalString(args.map(v => prStr(v, true)).join(" "));
11 },
12 "str"(...args: MalType[]): MalString {
13 return new MalString(args.map(v => prStr(v, false)).join(""));
14 },
15 prn(...args: MalType[]): MalNull {
16 const str = args.map(v => prStr(v, true)).join(" ");
17 console.log(str);
18 return MalNull.instance;
19 },
20 println(...args: MalType[]): MalNull {
21 const str = args.map(v => prStr(v, false)).join(" ");
22 console.log(str);
23 return MalNull.instance;
24 },
555f7fc7 25 "read-string"(v: MalType) {
26 if (!MalString.is(v)) {
27 throw new Error(`unexpected symbol: ${v.type}, expected: string`);
28 }
29 return readStr(v.v);
30 },
31 slurp(v: MalType) {
32 if (!MalString.is(v)) {
33 throw new Error(`unexpected symbol: ${v.type}, expected: string`);
34 }
35 const content = fs.readFileSync(v.v, "UTF-8");
36 return new MalString(content);
37 },
8d6bad07 38 cons(a: MalType, b: MalType) {
39 if (!MalList.is(b) && !MalVector.is(b)) {
40 throw new Error(`unexpected symbol: ${b.type}, expected: list or vector`);
41 }
42
43 return new MalList([a].concat(b.list));
44 },
45 concat(...args: MalType[]) {
46 const list = args
47 .map(arg => {
48 if (!MalList.is(arg) && !MalVector.is(arg)) {
49 throw new Error(`unexpected symbol: ${arg.type}, expected: list or vector`);
50 }
51 return arg;
52 })
53 .reduce((p, c) => p.concat(c.list), [] as MalType[]);
54
55 return new MalList(list);
56 },
dfe70453 57 list(...args: MalType[]): MalList {
58 return new MalList(args);
59 },
60 "list?"(v: MalType): MalBoolean {
61 return new MalBoolean(v instanceof MalList);
62 },
63 "empty?"(v: MalType): MalBoolean {
64 if (!MalList.is(v) && !MalVector.is(v)) {
65 return new MalBoolean(false);
66 }
67 return new MalBoolean(v.list.length === 0);
68 },
69 count(v: MalType): MalNumber {
70 if (MalList.is(v) || MalVector.is(v)) {
71 return new MalNumber(v.list.length);
72 }
73 if (MalNull.is(v)) {
74 return new MalNumber(0);
75 }
76 throw new Error(`unexpected symbol: ${v.type}`);
77 },
555f7fc7 78 atom(v: MalType): MalAtom {
79 return new MalAtom(v);
80 },
81 "atom?"(v: MalType): MalBoolean {
82 return new MalBoolean(MalAtom.is(v));
83 },
84 deref(v: MalType): MalType {
85 if (!MalAtom.is(v)) {
86 throw new Error(`unexpected symbol: ${v.type}, expected: atom`);
87 }
88 return v.v;
89 },
90 "reset!"(atom: MalType, v: MalType): MalType {
91 if (!MalAtom.is(atom)) {
92 throw new Error(`unexpected symbol: ${atom.type}, expected: atom`);
93 }
94 atom.v = v;
95 return v;
96 },
97 "swap!"(atom: MalType, f: MalType, ...args: MalType[]): MalType {
98 if (!MalAtom.is(atom)) {
99 throw new Error(`unexpected symbol: ${atom.type}, expected: atom`);
100 }
101 if (!MalFunction.is(f)) {
102 throw new Error(`unexpected symbol: ${f.type}, expected: function`);
103 }
104 atom.v = f.func(...[atom.v].concat(args));
105 return atom.v;
106 },
dfe70453 107 "+"(a: MalType, b: MalType): MalNumber {
108 if (!MalNumber.is(a)) {
109 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
110 }
111 if (!MalNumber.is(b)) {
112 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
113 }
114
115 return new MalNumber(a.v + b.v);
116 },
117 "-"(a: MalType, b: MalType): MalNumber {
118 if (!MalNumber.is(a)) {
119 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
120 }
121 if (!MalNumber.is(b)) {
122 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
123 }
124
125 return new MalNumber(a.v - b.v);
126 },
127 "*"(a: MalType, b: MalType): MalNumber {
128 if (!MalNumber.is(a)) {
129 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
130 }
131 if (!MalNumber.is(b)) {
132 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
133 }
134
135 return new MalNumber(a.v * b.v);
136 },
137 "/"(a: MalType, b: MalType): MalNumber {
138 if (!MalNumber.is(a)) {
139 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
140 }
141 if (!MalNumber.is(b)) {
142 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
143 }
144
145 return new MalNumber(a.v / b.v);
146 },
147 "="(a: MalType, b: MalType): MalBoolean {
148 return new MalBoolean(equals(a, b));
149 },
150 "<"(a: MalType, b: MalType): MalBoolean {
151 if (!MalNumber.is(a)) {
152 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
153 }
154 if (!MalNumber.is(b)) {
155 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
156 }
157
158 return new MalBoolean(a.v < b.v);
159 },
160 "<="(a: MalType, b: MalType): MalBoolean {
161 if (!MalNumber.is(a)) {
162 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
163 }
164 if (!MalNumber.is(b)) {
165 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
166 }
167
168 return new MalBoolean(a.v <= b.v);
169 },
170 ">"(a: MalType, b: MalType): MalBoolean {
171 if (!MalNumber.is(a)) {
172 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
173 }
174 if (!MalNumber.is(b)) {
175 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
176 }
177
178 return new MalBoolean(a.v > b.v);
179 },
180 ">="(a: MalType, b: MalType): MalBoolean {
181 if (!MalNumber.is(a)) {
182 throw new Error(`unexpected symbol: ${a.type}, expected: number`);
183 }
184 if (!MalNumber.is(b)) {
185 throw new Error(`unexpected symbol: ${b.type}, expected: number`);
186 }
187
188 return new MalBoolean(a.v >= b.v);
189 },
190 };
191
192 const map = new Map<MalSymbol, MalFunction>();
79a10a6e 193 Object.keys(ns).forEach(key => map.set(MalSymbol.get(key), MalFunction.fromBootstrap(ns[key])));
dfe70453 194 return map;
195})();