555f7fc7 |
1 | import * as fs from "fs"; |
2 | |
3 | import { MalType, MalSymbol, MalFunction, MalNull, MalList, MalVector, MalBoolean, MalNumber, MalString, MalAtom, equals } from "./types"; |
4 | import { readStr } from "./reader"; |
dfe70453 |
5 | import { prStr } from "./printer"; |
6 | |
7 | export 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 | })(); |