fez-lisp 1.3.1 → 1.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/baked/macros.js +29 -4
- package/lib/baked/std.js +1 -1
- package/package.json +2 -3
- package/src/compiler.js +32 -10
- package/src/interpreter.js +314 -329
- package/src/keywords.js +0 -13
- package/src/macros.js +406 -0
- package/src/utils.js +15 -374
package/src/keywords.js
CHANGED
@@ -6,19 +6,6 @@ export const ATOM = 2
|
|
6
6
|
export const TRUE = 1
|
7
7
|
export const FALSE = 0
|
8
8
|
export const PLACEHOLDER = '.'
|
9
|
-
export const SUGGAR = {
|
10
|
-
// Syntactic suggars
|
11
|
-
PIPE: '|>',
|
12
|
-
NOT_EQUAL_1: '!=',
|
13
|
-
NOT_EQUAL_2: '<>',
|
14
|
-
UNLESS: 'unless',
|
15
|
-
LIST_TYPE: 'list',
|
16
|
-
POWER: '**',
|
17
|
-
INTEGER_DEVISION: '//',
|
18
|
-
CONDITION: 'cond',
|
19
|
-
RECURSION: 'recursive',
|
20
|
-
CACHE: 'memoized'
|
21
|
-
}
|
22
9
|
export const KEYWORDS = {
|
23
10
|
NUMBER_TYPE: 'number',
|
24
11
|
ARRAY_TYPE: 'array',
|
package/src/macros.js
ADDED
@@ -0,0 +1,406 @@
|
|
1
|
+
import { isLeaf } from './parser.js'
|
2
|
+
import {
|
3
|
+
EXPONENTIATION,
|
4
|
+
INTEGER_DIVISION,
|
5
|
+
NOT_EQUAL
|
6
|
+
} from '../lib/baked/macros.js'
|
7
|
+
import {
|
8
|
+
APPLY,
|
9
|
+
ATOM,
|
10
|
+
FALSE,
|
11
|
+
KEYWORDS,
|
12
|
+
TRUE,
|
13
|
+
TYPE,
|
14
|
+
VALUE,
|
15
|
+
WORD
|
16
|
+
} from './keywords.js'
|
17
|
+
import { stringifyArgs } from './utils.js'
|
18
|
+
export const SUGGAR = {
|
19
|
+
// Syntactic suggars
|
20
|
+
PIPE: '|>',
|
21
|
+
NOT_EQUAL_1: '!=',
|
22
|
+
NOT_EQUAL_2: '<>',
|
23
|
+
REMAINDER_OF_DIVISION_1: '%',
|
24
|
+
UNLESS: 'unless',
|
25
|
+
LIST_TYPE: 'list',
|
26
|
+
POWER: '**',
|
27
|
+
INTEGER_DEVISION: '//',
|
28
|
+
CONDITION: 'cond'
|
29
|
+
}
|
30
|
+
export const deSuggarAst = (ast) => {
|
31
|
+
if (ast.length === 0) throw new SyntaxError(`No expressions to evaluate`)
|
32
|
+
// for (const node of ast)
|
33
|
+
// if (node[0] && node[0][TYPE] === APPLY && node[0][VALUE] === KEYWORDS.BLOCK)
|
34
|
+
// throw new SyntaxError(`Top level (${KEYWORDS.BLOCK}) is not allowed`)
|
35
|
+
let prev = undefined
|
36
|
+
const evaluate = (exp) => {
|
37
|
+
const [first, ...rest] = isLeaf(exp) ? [exp] : exp
|
38
|
+
if (first != undefined) {
|
39
|
+
switch (first[TYPE]) {
|
40
|
+
case WORD:
|
41
|
+
{
|
42
|
+
switch (first[VALUE]) {
|
43
|
+
case SUGGAR.REMAINDER_OF_DIVISION_1:
|
44
|
+
exp[VALUE] = KEYWORDS.REMAINDER_OF_DIVISION
|
45
|
+
break
|
46
|
+
case SUGGAR.NOT_EQUAL_1:
|
47
|
+
case SUGGAR.NOT_EQUAL_2:
|
48
|
+
exp.length = 0
|
49
|
+
exp.push(...NOT_EQUAL)
|
50
|
+
break
|
51
|
+
case SUGGAR.POWER:
|
52
|
+
exp.length = 0
|
53
|
+
exp.push(...EXPONENTIATION)
|
54
|
+
break
|
55
|
+
case SUGGAR.INTEGER_DEVISION:
|
56
|
+
exp.length = 0
|
57
|
+
exp.push(...INTEGER_DIVISION)
|
58
|
+
break
|
59
|
+
}
|
60
|
+
}
|
61
|
+
break
|
62
|
+
case ATOM:
|
63
|
+
break
|
64
|
+
case APPLY:
|
65
|
+
{
|
66
|
+
switch (first[VALUE]) {
|
67
|
+
case KEYWORDS.BLOCK:
|
68
|
+
{
|
69
|
+
if (
|
70
|
+
prev == undefined ||
|
71
|
+
(prev &&
|
72
|
+
prev[TYPE] === APPLY &&
|
73
|
+
prev[VALUE] !== KEYWORDS.ANONYMOUS_FUNCTION)
|
74
|
+
) {
|
75
|
+
exp[0][VALUE] = KEYWORDS.CALL_FUNCTION
|
76
|
+
exp[0][TYPE] = APPLY
|
77
|
+
exp.length = 1
|
78
|
+
exp[1] = [
|
79
|
+
[APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
|
80
|
+
[[APPLY, KEYWORDS.BLOCK], ...rest]
|
81
|
+
]
|
82
|
+
deSuggarAst(exp)
|
83
|
+
}
|
84
|
+
}
|
85
|
+
break
|
86
|
+
case SUGGAR.PIPE:
|
87
|
+
{
|
88
|
+
if (rest.length < 1)
|
89
|
+
throw new RangeError(
|
90
|
+
`Invalid number of arguments to (${
|
91
|
+
SUGGAR.PIPE
|
92
|
+
}) (>= 1 required). (${SUGGAR.PIPE} ${stringifyArgs(
|
93
|
+
rest
|
94
|
+
)})`
|
95
|
+
)
|
96
|
+
let inp = rest[0]
|
97
|
+
exp.length = 0
|
98
|
+
for (let i = 1; i < rest.length; ++i) {
|
99
|
+
if (!rest[i].length || rest[i][0][TYPE] !== APPLY)
|
100
|
+
throw new TypeError(
|
101
|
+
`Argument at position (${i}) of (${
|
102
|
+
SUGGAR.PIPE
|
103
|
+
}) is not an invoked (${
|
104
|
+
KEYWORDS.ANONYMOUS_FUNCTION
|
105
|
+
}). (${SUGGAR.PIPE} ${stringifyArgs(rest)})`
|
106
|
+
)
|
107
|
+
inp = [rest[i].shift(), inp, ...rest[i]]
|
108
|
+
}
|
109
|
+
for (let i = 0; i < inp.length; ++i) exp[i] = inp[i]
|
110
|
+
deSuggarAst(exp)
|
111
|
+
}
|
112
|
+
break
|
113
|
+
case SUGGAR.CONDITION:
|
114
|
+
{
|
115
|
+
if (rest.length < 2)
|
116
|
+
throw new RangeError(
|
117
|
+
`Invalid number of arguments for (${
|
118
|
+
SUGGAR.CONDITION
|
119
|
+
}), expected (> 2 required) but got ${rest.length} (${
|
120
|
+
SUGGAR.CONDITION
|
121
|
+
} ${stringifyArgs(rest)})`
|
122
|
+
)
|
123
|
+
if (rest.length % 2 !== 0)
|
124
|
+
throw new RangeError(
|
125
|
+
`Invalid number of arguments for (${
|
126
|
+
SUGGAR.CONDITION
|
127
|
+
}), expected even number of arguments but got ${
|
128
|
+
rest.length
|
129
|
+
} (${SUGGAR.CONDITION} ${stringifyArgs(rest)})`
|
130
|
+
)
|
131
|
+
exp.length = 0
|
132
|
+
let temp = exp
|
133
|
+
for (let i = 0; i < rest.length; i += 2) {
|
134
|
+
if (i === rest.length - 2) {
|
135
|
+
temp.push([APPLY, KEYWORDS.IF], rest[i], rest.at(-1))
|
136
|
+
} else {
|
137
|
+
temp.push([APPLY, KEYWORDS.IF], rest[i], rest[i + 1], [])
|
138
|
+
temp = temp.at(-1)
|
139
|
+
}
|
140
|
+
}
|
141
|
+
deSuggarAst(exp)
|
142
|
+
}
|
143
|
+
break
|
144
|
+
case SUGGAR.LIST_TYPE:
|
145
|
+
{
|
146
|
+
exp.length = 0
|
147
|
+
let temp = exp
|
148
|
+
for (const item of rest) {
|
149
|
+
temp.push([APPLY, KEYWORDS.ARRAY_TYPE], item, [])
|
150
|
+
temp = temp.at(-1)
|
151
|
+
}
|
152
|
+
temp.push([APPLY, KEYWORDS.ARRAY_TYPE])
|
153
|
+
}
|
154
|
+
deSuggarAst(exp)
|
155
|
+
break
|
156
|
+
case SUGGAR.INTEGER_DEVISION:
|
157
|
+
{
|
158
|
+
if (rest.length !== 2)
|
159
|
+
throw new RangeError(
|
160
|
+
`Invalid number of arguments for (${
|
161
|
+
SUGGAR.INTEGER_DEVISION
|
162
|
+
}), expected (= 2) but got ${rest.length} (${
|
163
|
+
SUGGAR.INTEGER_DEVISION
|
164
|
+
} ${stringifyArgs(rest)})`
|
165
|
+
)
|
166
|
+
else if (rest.some((x) => x[TYPE] === APPLY)) {
|
167
|
+
exp.length = 0
|
168
|
+
exp.push(
|
169
|
+
[0, KEYWORDS.CALL_FUNCTION],
|
170
|
+
INTEGER_DIVISION,
|
171
|
+
...rest
|
172
|
+
)
|
173
|
+
} else {
|
174
|
+
exp.length = 1
|
175
|
+
exp[0] = [APPLY, KEYWORDS.BITWISE_OR]
|
176
|
+
exp.push([[APPLY, KEYWORDS.DIVISION], ...rest])
|
177
|
+
exp.push([ATOM, 0])
|
178
|
+
}
|
179
|
+
}
|
180
|
+
break
|
181
|
+
case SUGGAR.POWER:
|
182
|
+
{
|
183
|
+
if (rest.length !== 2)
|
184
|
+
throw new RangeError(
|
185
|
+
`Invalid number of arguments for (${
|
186
|
+
SUGGAR.POWER
|
187
|
+
}), expected (= 2) but got ${rest.length} (${
|
188
|
+
SUGGAR.POWER
|
189
|
+
} ${stringifyArgs(rest)})`
|
190
|
+
)
|
191
|
+
const isExponentAtom = exp[1][TYPE] === ATOM
|
192
|
+
const isPowerAtom = exp[2][TYPE] === ATOM
|
193
|
+
const isExponentWord = exp[1][TYPE] === WORD
|
194
|
+
if ((isExponentWord || isExponentAtom) && isPowerAtom) {
|
195
|
+
if (isExponentAtom) {
|
196
|
+
exp[0][VALUE] = KEYWORDS.MULTIPLICATION
|
197
|
+
const exponent = exp[1]
|
198
|
+
const power = exp[2][VALUE]
|
199
|
+
exp.length = 1
|
200
|
+
exp.push(exponent, [ATOM, exponent[VALUE] ** (power - 1)])
|
201
|
+
} else if (isExponentWord) {
|
202
|
+
const exponent = exp[1]
|
203
|
+
const power = exp[2]
|
204
|
+
exp.length = 0
|
205
|
+
exp.push(
|
206
|
+
[0, KEYWORDS.CALL_FUNCTION],
|
207
|
+
EXPONENTIATION,
|
208
|
+
exponent,
|
209
|
+
power
|
210
|
+
)
|
211
|
+
}
|
212
|
+
} else {
|
213
|
+
const exponent = exp[1]
|
214
|
+
const power = exp[2]
|
215
|
+
exp.length = 0
|
216
|
+
exp.push(
|
217
|
+
[0, KEYWORDS.CALL_FUNCTION],
|
218
|
+
EXPONENTIATION,
|
219
|
+
exponent,
|
220
|
+
power
|
221
|
+
)
|
222
|
+
}
|
223
|
+
deSuggarAst(exp)
|
224
|
+
}
|
225
|
+
break
|
226
|
+
case KEYWORDS.MULTIPLICATION:
|
227
|
+
if (!rest.length) {
|
228
|
+
exp[0][TYPE] = ATOM
|
229
|
+
exp[0][VALUE] = TRUE
|
230
|
+
} else if (rest.length > 2) {
|
231
|
+
exp.length = 0
|
232
|
+
let temp = exp
|
233
|
+
for (let i = 0; i < rest.length; i += 1) {
|
234
|
+
if (i < rest.length - 1) {
|
235
|
+
temp.push([APPLY, KEYWORDS.MULTIPLICATION], rest[i], [])
|
236
|
+
temp = temp.at(-1)
|
237
|
+
} else {
|
238
|
+
temp.push(...rest[i])
|
239
|
+
}
|
240
|
+
}
|
241
|
+
deSuggarAst(exp)
|
242
|
+
}
|
243
|
+
break
|
244
|
+
case KEYWORDS.ADDITION:
|
245
|
+
if (!rest.length) {
|
246
|
+
exp[0][TYPE] = ATOM
|
247
|
+
exp[0][VALUE] = FALSE
|
248
|
+
} else if (rest.length > 2) {
|
249
|
+
exp.length = 0
|
250
|
+
let temp = exp
|
251
|
+
for (let i = 0; i < rest.length; i += 1) {
|
252
|
+
if (i < rest.length - 1) {
|
253
|
+
temp.push([APPLY, KEYWORDS.ADDITION], rest[i], [])
|
254
|
+
temp = temp.at(-1)
|
255
|
+
} else {
|
256
|
+
temp.push(...rest[i])
|
257
|
+
}
|
258
|
+
}
|
259
|
+
deSuggarAst(exp)
|
260
|
+
}
|
261
|
+
break
|
262
|
+
case KEYWORDS.DIVISION:
|
263
|
+
if (!rest.length) {
|
264
|
+
exp[0][TYPE] = ATOM
|
265
|
+
exp[0][VALUE] = FALSE
|
266
|
+
} else if (rest.length === 1) {
|
267
|
+
exp.length = 1
|
268
|
+
exp.push([ATOM, 1], rest[0])
|
269
|
+
} else if (rest.length > 2) {
|
270
|
+
exp.length = 0
|
271
|
+
let temp = exp
|
272
|
+
for (let i = 0; i < rest.length; i += 1) {
|
273
|
+
if (i < rest.length - 1) {
|
274
|
+
temp.push([APPLY, KEYWORDS.DIVISION], rest[i], [])
|
275
|
+
temp = temp.at(-1)
|
276
|
+
} else {
|
277
|
+
temp.push(...rest[i])
|
278
|
+
}
|
279
|
+
}
|
280
|
+
deSuggarAst(exp)
|
281
|
+
}
|
282
|
+
break
|
283
|
+
case KEYWORDS.AND:
|
284
|
+
if (!rest.length) {
|
285
|
+
exp[0][TYPE] = ATOM
|
286
|
+
exp[0][VALUE] = FALSE
|
287
|
+
} else if (rest.length > 2) {
|
288
|
+
exp.length = 0
|
289
|
+
let temp = exp
|
290
|
+
for (let i = 0; i < rest.length; i += 1) {
|
291
|
+
if (i < rest.length - 1) {
|
292
|
+
temp.push([APPLY, KEYWORDS.AND], rest[i], [])
|
293
|
+
temp = temp.at(-1)
|
294
|
+
} else {
|
295
|
+
temp.push(...rest[i])
|
296
|
+
}
|
297
|
+
}
|
298
|
+
deSuggarAst(exp)
|
299
|
+
}
|
300
|
+
break
|
301
|
+
case KEYWORDS.OR:
|
302
|
+
if (!rest.length) {
|
303
|
+
exp[0][TYPE] = ATOM
|
304
|
+
exp[0][VALUE] = FALSE
|
305
|
+
} else if (rest.length > 2) {
|
306
|
+
exp.length = 0
|
307
|
+
let temp = exp
|
308
|
+
for (let i = 0; i < rest.length; i += 1) {
|
309
|
+
if (i < rest.length - 1) {
|
310
|
+
temp.push([APPLY, KEYWORDS.OR], rest[i], [])
|
311
|
+
temp = temp.at(-1)
|
312
|
+
} else {
|
313
|
+
temp.push(...rest[i])
|
314
|
+
}
|
315
|
+
}
|
316
|
+
deSuggarAst(exp)
|
317
|
+
}
|
318
|
+
break
|
319
|
+
case SUGGAR.UNLESS:
|
320
|
+
{
|
321
|
+
if (rest.length > 3 || rest.length < 2)
|
322
|
+
throw new RangeError(
|
323
|
+
`Invalid number of arguments for (${
|
324
|
+
SUGGAR.UNLESS
|
325
|
+
}), expected (or (= 3) (= 2)) but got ${rest.length} (${
|
326
|
+
SUGGAR.UNLESS
|
327
|
+
} ${stringifyArgs(rest)})`
|
328
|
+
)
|
329
|
+
exp[0][VALUE] = KEYWORDS.IF
|
330
|
+
const temp = exp[2]
|
331
|
+
exp[2] = exp[3] ?? [ATOM, FALSE]
|
332
|
+
exp[3] = temp
|
333
|
+
}
|
334
|
+
deSuggarAst(exp)
|
335
|
+
break
|
336
|
+
case SUGGAR.REMAINDER_OF_DIVISION_1:
|
337
|
+
{
|
338
|
+
if (rest.length !== 2)
|
339
|
+
throw new RangeError(
|
340
|
+
`Invalid number of arguments for (${
|
341
|
+
exp[0][1]
|
342
|
+
}), expected (= 2) but got ${rest.length} (${
|
343
|
+
exp[0][1]
|
344
|
+
} ${stringifyArgs(rest)})`
|
345
|
+
)
|
346
|
+
exp[0][VALUE] = KEYWORDS.REMAINDER_OF_DIVISION
|
347
|
+
deSuggarAst(exp)
|
348
|
+
}
|
349
|
+
break
|
350
|
+
case SUGGAR.NOT_EQUAL_1:
|
351
|
+
case SUGGAR.NOT_EQUAL_2:
|
352
|
+
{
|
353
|
+
if (rest.length !== 2)
|
354
|
+
throw new RangeError(
|
355
|
+
`Invalid number of arguments for (${
|
356
|
+
exp[0][1]
|
357
|
+
}), expected (= 2) but got ${rest.length} (${
|
358
|
+
exp[0][1]
|
359
|
+
} ${stringifyArgs(rest)})`
|
360
|
+
)
|
361
|
+
exp[0][VALUE] = KEYWORDS.NOT
|
362
|
+
exp[1] = [[APPLY, KEYWORDS.EQUAL], exp[1], exp[2]]
|
363
|
+
exp.length = 2
|
364
|
+
deSuggarAst(exp)
|
365
|
+
}
|
366
|
+
break
|
367
|
+
}
|
368
|
+
prev = first
|
369
|
+
}
|
370
|
+
break
|
371
|
+
default:
|
372
|
+
for (const e of exp) evaluate(e)
|
373
|
+
break
|
374
|
+
}
|
375
|
+
for (const r of rest) evaluate(r)
|
376
|
+
}
|
377
|
+
}
|
378
|
+
evaluate(ast)
|
379
|
+
return ast
|
380
|
+
}
|
381
|
+
export const replaceStrings = (source) => {
|
382
|
+
// const quotes = source.match(/"(.*?)"/g)
|
383
|
+
const quotes = source.match(/"(?:.*?(\n|\r))*?.*?"/g)
|
384
|
+
// TODO handle escaping
|
385
|
+
if (quotes)
|
386
|
+
for (const q of quotes)
|
387
|
+
source = source.replaceAll(
|
388
|
+
q,
|
389
|
+
`(${KEYWORDS.ARRAY_TYPE} ${[...q.replaceAll('\r', '')]
|
390
|
+
.slice(1, -1)
|
391
|
+
.map((x) => x.charCodeAt(0))
|
392
|
+
.join(' ')})`
|
393
|
+
)
|
394
|
+
return source
|
395
|
+
}
|
396
|
+
export const replaceQuotes = (source) =>
|
397
|
+
source
|
398
|
+
.replaceAll(/\'\(/g, `(${KEYWORDS.ARRAY_TYPE} `)
|
399
|
+
.replaceAll(/\`\(/g, `(${SUGGAR.LIST_TYPE} `)
|
400
|
+
.replaceAll(/\(\)/g, `(${KEYWORDS.ARRAY_TYPE})`)
|
401
|
+
export const deSuggarSource = (source) => replaceQuotes(replaceStrings(source))
|
402
|
+
export const handleUnbalancedQuotes = (source) => {
|
403
|
+
const diff = (source.match(/\"/g) ?? []).length % 2
|
404
|
+
if (diff !== 0) throw new SyntaxError(`Quotes are unbalanced "`)
|
405
|
+
return source
|
406
|
+
}
|