fez-lisp 1.3.27 → 1.4.0
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/README.md +29 -107
- package/index.js +3 -4
- package/lib/baked/std.js +1 -1
- package/package.json +1 -1
- package/src/compiler.js +25 -33
- package/src/evaluator.js +34 -10
- package/src/interpreter.js +164 -168
- package/src/keywords.js +8 -6
- package/src/macros.js +1 -6
- package/src/parser.js +1 -1
- package/src/utils.js +187 -6
package/src/keywords.js
CHANGED
@@ -39,12 +39,7 @@ export const KEYWORDS = {
|
|
39
39
|
CALL_FUNCTION: 'apply',
|
40
40
|
DEFINE_VARIABLE: 'let',
|
41
41
|
|
42
|
-
SET_ARRAY: 'set!'
|
43
|
-
|
44
|
-
LOG: 'log!',
|
45
|
-
LOG_STRING: 'log-string!',
|
46
|
-
LOG_CHAR: 'log-char!',
|
47
|
-
CLEAR_CONSOLE: 'clear!'
|
42
|
+
SET_ARRAY: 'set!'
|
48
43
|
}
|
49
44
|
|
50
45
|
export const TYPES = {
|
@@ -56,3 +51,10 @@ export const RUNTIME_TYPES = {
|
|
56
51
|
NUMBER: 'number',
|
57
52
|
ARRAY: 'array'
|
58
53
|
}
|
54
|
+
export const DEBUG = {
|
55
|
+
LOG: 'log',
|
56
|
+
ASSERT: 'assert',
|
57
|
+
ERROR: 'error'
|
58
|
+
}
|
59
|
+
|
60
|
+
export const SPECIAL_FORMS_SET = new Set(Object.values(KEYWORDS))
|
package/src/macros.js
CHANGED
@@ -31,7 +31,7 @@ export const SUGGAR = {
|
|
31
31
|
}
|
32
32
|
export const deSuggarAst = (ast, scope) => {
|
33
33
|
if (scope === undefined) scope = ast
|
34
|
-
if (ast.length === 0) throw new SyntaxError(`No expressions
|
34
|
+
if (ast.length === 0) throw new SyntaxError(`No expressions...`)
|
35
35
|
// for (const node of ast)
|
36
36
|
// if (node[0] && node[0][TYPE] === APPLY && node[0][VALUE] === KEYWORDS.BLOCK)
|
37
37
|
// throw new SyntaxError(`Top level (${KEYWORDS.BLOCK}) is not allowed`)
|
@@ -667,11 +667,6 @@ const iron2 = (scope, exp) => {
|
|
667
667
|
}
|
668
668
|
export const replaceQuotes = (source) =>
|
669
669
|
source
|
670
|
-
.replaceAll(/\'\(/g, `(${KEYWORDS.CREATE_ARRAY} `)
|
671
|
-
.replaceAll(/\`\(/g, `(${SUGGAR.CREATE_LIST} `)
|
672
|
-
.replaceAll(/\(\)/g, `(${KEYWORDS.CREATE_ARRAY})`)
|
673
|
-
.replaceAll(/\[\]/g, `(${KEYWORDS.CREATE_ARRAY})`)
|
674
|
-
.replaceAll(/\{\}/g, `(${SUGGAR.CREATE_LIST})`)
|
675
670
|
.replaceAll(/\[/g, `(${KEYWORDS.CREATE_ARRAY} `)
|
676
671
|
.replaceAll(/\]/g, ')')
|
677
672
|
.replaceAll(/\{/g, `(${SUGGAR.CREATE_LIST} `)
|
package/src/parser.js
CHANGED
package/src/utils.js
CHANGED
@@ -1,6 +1,17 @@
|
|
1
1
|
import std from '../lib/baked/std.js'
|
2
|
-
import {
|
3
|
-
import {
|
2
|
+
import { compile, OPTIMIZATIONS } from './compiler.js'
|
3
|
+
import {
|
4
|
+
APPLY,
|
5
|
+
ATOM,
|
6
|
+
DEBUG,
|
7
|
+
FALSE,
|
8
|
+
KEYWORDS,
|
9
|
+
RUNTIME_TYPES,
|
10
|
+
TRUE,
|
11
|
+
TYPE,
|
12
|
+
VALUE,
|
13
|
+
WORD
|
14
|
+
} from './keywords.js'
|
4
15
|
import { evaluate } from './evaluator.js'
|
5
16
|
import { AST, isLeaf, LISP } from './parser.js'
|
6
17
|
import {
|
@@ -11,7 +22,7 @@ import {
|
|
11
22
|
import { keywords } from './interpreter.js'
|
12
23
|
export const logError = (error) =>
|
13
24
|
console.log('\x1b[31m', `\n${error}\n`, '\x1b[0m')
|
14
|
-
export const logSuccess = (output) => console.log(output, '\x1b[0m')
|
25
|
+
export const logSuccess = (output) => console.log('\x1b[32m', output, '\x1b[0m')
|
15
26
|
// export const replaceEmptyArrays = (source) =>
|
16
27
|
// source
|
17
28
|
export const removeNoCode = (source) =>
|
@@ -236,19 +247,18 @@ export const fez = (source, options = {}) => {
|
|
236
247
|
const scope = deSuggarAst(parsed)
|
237
248
|
const ast = wrapInBlock(shake(scope, std))
|
238
249
|
// if (options.check) typeCheck(ast)
|
239
|
-
if (options.compile) return
|
250
|
+
if (options.compile) return compile(ast)
|
240
251
|
return evaluate(ast, env)
|
241
252
|
} else if (Array.isArray(source)) {
|
242
253
|
const ast = !options.mutation
|
243
254
|
? AST.parse(AST.stringify(source).replace(new RegExp(/!/g), 'ǃ'))
|
244
255
|
: source
|
245
|
-
if (options.compile) return
|
256
|
+
if (options.compile) return compile(ast)
|
246
257
|
return evaluate(ast, env)
|
247
258
|
} else {
|
248
259
|
throw new Error('Source has to be either a lisp source code or an AST')
|
249
260
|
}
|
250
261
|
} catch (error) {
|
251
|
-
// console.log(error)
|
252
262
|
const err = error.message.replace("'[object Array]'", '(array)')
|
253
263
|
// .replace('object', '(array)')
|
254
264
|
logError(err)
|
@@ -313,4 +323,175 @@ export const ast = (source, deps) =>
|
|
313
323
|
deps.reduce((a, b) => a.concat(b), [])
|
314
324
|
)
|
315
325
|
)
|
326
|
+
|
316
327
|
export const astWithStd = (source) => wrapInBlock(shake(prep(source), std))
|
328
|
+
export const parse = (source) =>
|
329
|
+
wrapInBlock(
|
330
|
+
shake(
|
331
|
+
deSuggarAst(
|
332
|
+
LISP.parse(
|
333
|
+
removeNoCode(
|
334
|
+
handleUnbalancedQuotes(
|
335
|
+
handleUnbalancedParens(deSuggarSource(source))
|
336
|
+
)
|
337
|
+
)
|
338
|
+
)
|
339
|
+
),
|
340
|
+
std
|
341
|
+
)
|
342
|
+
)
|
343
|
+
export const debug = (ast) => {
|
344
|
+
try {
|
345
|
+
const debugEnv = {
|
346
|
+
...keywords,
|
347
|
+
[DEBUG.LOG]: (args, env) => {
|
348
|
+
if (args.length !== 1 && args.length !== 2)
|
349
|
+
throw new RangeError(
|
350
|
+
`Invalid number of arguments to (${DEBUG.LOG}) (or (= 1) (= 2)) (${
|
351
|
+
DEBUG.LOG
|
352
|
+
} ${stringifyArgs(args)})`
|
353
|
+
)
|
354
|
+
const expression = evaluate(args[0], env)
|
355
|
+
if (args.length === 2) {
|
356
|
+
const option = evaluate(args[1], env)
|
357
|
+
if (!Array.isArray(option)) {
|
358
|
+
throw new TypeError(
|
359
|
+
`Second argument of (${DEBUG.LOG}) must be an (${
|
360
|
+
RUNTIME_TYPES.ARRAY
|
361
|
+
}) but got (${expression}) (${DEBUG.LOG} ${stringifyArgs(args)})`
|
362
|
+
)
|
363
|
+
}
|
364
|
+
const type = option.map((x) => String.fromCharCode(x)).join('')
|
365
|
+
switch (type) {
|
366
|
+
case 'string':
|
367
|
+
case 'str':
|
368
|
+
{
|
369
|
+
if (!Array.isArray(expression))
|
370
|
+
throw new TypeError(
|
371
|
+
`Argument of (${DEBUG.LOG}) must be an (${
|
372
|
+
RUNTIME_TYPES.ARRAY
|
373
|
+
}) in the case ${type} but got (${expression}) (${
|
374
|
+
DEBUG.LOG
|
375
|
+
} ${stringifyArgs(args)})`
|
376
|
+
)
|
377
|
+
console.log(
|
378
|
+
expression.map((x) => String.fromCharCode(x)).join('')
|
379
|
+
)
|
380
|
+
}
|
381
|
+
break
|
382
|
+
case 'char':
|
383
|
+
case 'ch':
|
384
|
+
{
|
385
|
+
if (typeof expression !== 'number')
|
386
|
+
throw new TypeError(
|
387
|
+
`Argument argument of (${DEBUG.LOG}) must be a (${
|
388
|
+
RUNTIME_TYPES.NUMBER
|
389
|
+
}) in the case ${type} but got (${expression}) (${
|
390
|
+
DEBUG.LOG
|
391
|
+
} ${stringifyArgs(args)})`
|
392
|
+
)
|
393
|
+
console.log(String.fromCharCode(expression))
|
394
|
+
}
|
395
|
+
|
396
|
+
break
|
397
|
+
case '*':
|
398
|
+
console.log(expression)
|
399
|
+
break
|
400
|
+
default:
|
401
|
+
throw new TypeError(
|
402
|
+
`Invalid number of option to (${
|
403
|
+
DEBUG.LOG
|
404
|
+
}) got ${option} ${stringifyArgs(args)})`
|
405
|
+
)
|
406
|
+
}
|
407
|
+
} else console.log(expression)
|
408
|
+
return expression
|
409
|
+
},
|
410
|
+
[DEBUG.ERROR]: (args, env) => {
|
411
|
+
if (args.length !== 1)
|
412
|
+
throw new RangeError(
|
413
|
+
`Invalid number of arguments to (${DEBUG.ERROR}) (= 1 required) (${
|
414
|
+
DEBUG.ERROR
|
415
|
+
} ${stringifyArgs(args)})`
|
416
|
+
)
|
417
|
+
const expression = evaluate(args[0], env)
|
418
|
+
if (!Array.isArray(expression))
|
419
|
+
throw new TypeError(
|
420
|
+
`Argument of (${DEBUG.ERROR}) must be an (${
|
421
|
+
DEBUG.ARRAY_TYPE
|
422
|
+
}) but got (${expression}) (${DEBUG.ERROR} ${stringifyArgs(args)})`
|
423
|
+
)
|
424
|
+
throw new Error(expression.map((x) => String.fromCharCode(x)).join(''))
|
425
|
+
},
|
426
|
+
[DEBUG.ASSERT]: (args, env) => {
|
427
|
+
if (args.length < 2)
|
428
|
+
throw new RangeError(
|
429
|
+
`Invalid number of arguments for (${
|
430
|
+
DEBUG.ASSERT
|
431
|
+
}), expected (> 2 required) but got ${args.length} (${
|
432
|
+
DEBUG.ASSERT
|
433
|
+
} ${stringifyArgs(args)})`
|
434
|
+
)
|
435
|
+
if (args.length % 2 !== 0)
|
436
|
+
throw new RangeError(
|
437
|
+
`Invalid number of arguments for (${
|
438
|
+
DEBUG.ASSERT
|
439
|
+
}), expected even number of arguments but got ${args.length} (${
|
440
|
+
DEBUG.ASSERT
|
441
|
+
} ${stringifyArgs(args)})`
|
442
|
+
)
|
443
|
+
for (let i = 0; i < args.length; i += 2) {
|
444
|
+
const condition = evaluate(args[i], env)
|
445
|
+
if (condition !== FALSE && condition !== TRUE)
|
446
|
+
throw new TypeError(
|
447
|
+
`Condition of (${
|
448
|
+
DEBUG.ASSERT
|
449
|
+
}) must be ${TRUE} or ${FALSE} but got (${
|
450
|
+
DEBUG.ASSERT
|
451
|
+
} ${stringifyArgs(args)})`
|
452
|
+
)
|
453
|
+
if (condition) {
|
454
|
+
const error = args[i + 1]
|
455
|
+
if (error[0][TYPE] === APPLY && error[0][VALUE] === DEBUG.ERROR)
|
456
|
+
return evaluate(error, env)
|
457
|
+
else
|
458
|
+
throw new TypeError(
|
459
|
+
`Concequence of (${DEBUG.ASSERT}) must be (${
|
460
|
+
DEBUG.ERROR
|
461
|
+
}) but got (${DEBUG.ASSERT} ${stringifyArgs(args)})`
|
462
|
+
)
|
463
|
+
}
|
464
|
+
}
|
465
|
+
return 0
|
466
|
+
}
|
467
|
+
}
|
468
|
+
evaluate(ast, debugEnv)
|
469
|
+
} catch (error) {
|
470
|
+
if (
|
471
|
+
!error.message.includes('Maximum call stack size exceeded') &&
|
472
|
+
!error.message.includes('too much recursion') &&
|
473
|
+
error.message !== 'Maximum function invocation limit exceeded'
|
474
|
+
) {
|
475
|
+
error.message += `\n\nscope:\n(${evaluate.stack.at(-1)})`
|
476
|
+
throw error
|
477
|
+
} else logError(error.message)
|
478
|
+
}
|
479
|
+
const identity = (name) => [
|
480
|
+
[0, 'let'],
|
481
|
+
[1, name],
|
482
|
+
[
|
483
|
+
[0, 'lambda'],
|
484
|
+
[1, 'x'],
|
485
|
+
[1, 'x']
|
486
|
+
]
|
487
|
+
]
|
488
|
+
const block = ast[1][1]
|
489
|
+
const temp = block.shift()
|
490
|
+
block.unshift(
|
491
|
+
temp,
|
492
|
+
identity(DEBUG.LOG),
|
493
|
+
identity(DEBUG.ERROR),
|
494
|
+
identity(DEBUG.ASSERT)
|
495
|
+
)
|
496
|
+
return compile(ast)
|
497
|
+
}
|