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/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
+ }