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/utils.js CHANGED
@@ -1,42 +1,16 @@
1
1
  import std from '../lib/baked/std.js'
2
- import { comp } from './compiler.js'
3
- import {
4
- APPLY,
5
- ATOM,
6
- FALSE,
7
- KEYWORDS,
8
- SUGGAR,
9
- TRUE,
10
- TYPE,
11
- VALUE,
12
- WORD
13
- } from './keywords.js'
2
+ import { comp, OPTIMIZATIONS } from './compiler.js'
3
+ import { APPLY, ATOM, KEYWORDS, TYPE, VALUE, WORD } from './keywords.js'
14
4
  import { evaluate, run } from './evaluator.js'
15
5
  import { AST, isLeaf, LISP } from './parser.js'
16
- import { EXPONENTIATION, FLOOR } from '../lib/baked/macros.js'
6
+ import {
7
+ deSuggarAst,
8
+ deSuggarSource,
9
+ handleUnbalancedQuotes
10
+ } from './macros.js'
17
11
  export const logError = (error) =>
18
12
  console.log('\x1b[31m', `\n${error}\n`, '\x1b[0m')
19
13
  export const logSuccess = (output) => console.log(output, '\x1b[0m')
20
- export const replaceStrings = (source) => {
21
- // const quotes = source.match(/"(.*?)"/g)
22
- const quotes = source.match(/"(?:.*?(\n|\r))*?.*?"/g)
23
- // TODO handle escaping
24
- if (quotes)
25
- for (const q of quotes)
26
- source = source.replaceAll(
27
- q,
28
- `(array ${[...q.replaceAll('\r', '')]
29
- .slice(1, -1)
30
- .map((x) => x.charCodeAt(0))
31
- .join(' ')})`
32
- )
33
- return source
34
- }
35
- export const replaceQuotes = (source) =>
36
- source
37
- .replaceAll(/\'\(/g, '(array ')
38
- .replaceAll(/\`\(/g, '(list ')
39
- .replaceAll(/\(\)/g, '(array)')
40
14
  // export const replaceEmptyArrays = (source) =>
41
15
  // source
42
16
  export const removeNoCode = (source) =>
@@ -113,8 +87,8 @@ export const isForbiddenVariableName = (name) => {
113
87
  switch (name) {
114
88
  case '_':
115
89
  case KEYWORDS.DEFINE_VARIABLE:
116
- case SUGGAR.RECURSION:
117
- case SUGGAR.CACHE:
90
+ case OPTIMIZATIONS.RECURSION:
91
+ case OPTIMIZATIONS.CACHE:
118
92
  return true
119
93
  default:
120
94
  return !isNaN(name[0])
@@ -168,11 +142,6 @@ export const handleUnbalancedParens = (source) => {
168
142
  )
169
143
  return source
170
144
  }
171
- export const handleUnbalancedQuotes = (source) => {
172
- const diff = (source.match(/\"/g) ?? []).length % 2
173
- if (diff !== 0) throw new SyntaxError(`Quotes are unbalanced "`)
174
- return source
175
- }
176
145
  export const removeMutation = (source) => source.replace(new RegExp(/!/g), 'ǃ')
177
146
  const isDefinition = (x) =>
178
147
  x[TYPE] === APPLY && x[VALUE] === KEYWORDS.DEFINE_VARIABLE
@@ -246,13 +215,13 @@ export const fez = (source, options = {}) => {
246
215
  const env = Object.create(null)
247
216
  try {
248
217
  if (typeof source === 'string') {
249
- source = replaceQuotes(replaceStrings(source))
218
+ source = deSuggarSource(source)
250
219
  const valid = handleUnbalancedQuotes(
251
220
  handleUnbalancedParens(removeNoCode(source))
252
221
  )
253
222
  const code = !options.mutation ? removeMutation(valid) : valid
254
223
  if (!code.length && options.throw) throw new Error('Nothing to parse!')
255
- const parsed = deSuggar(LISP.parse(code))
224
+ const parsed = deSuggarAst(LISP.parse(code))
256
225
  const ast = [...treeShake(parsed, std), ...parsed]
257
226
  // if (options.check) typeCheck(ast)
258
227
  if (options.compile) return comp(ast)
@@ -310,17 +279,12 @@ export const decompress = (raw) => {
310
279
  export const shake = (parsed, std) => [...treeShake(parsed, std), ...parsed]
311
280
  export const tree = (source, std) =>
312
281
  std
313
- ? shake(
314
- LISP.parse(replaceQuotes(replaceStrings(removeNoCode(source)))),
315
- std
316
- )
317
- : LISP.parse(replaceQuotes(replaceStrings(removeNoCode(source))))
282
+ ? shake(LISP.parse(deSuggarSource(removeNoCode(source))), std)
283
+ : LISP.parse(deSuggarSource(removeNoCode(source)))
318
284
  export const minify = (source) =>
319
- LISP.source(
320
- deSuggar(LISP.parse(replaceQuotes(replaceStrings(removeNoCode(source)))))
321
- )
285
+ LISP.source(deSuggarAst(LISP.parse(deSuggarSource(removeNoCode(source)))))
322
286
  export const prep = (source) =>
323
- deSuggar(LISP.parse(removeNoCode(replaceQuotes(replaceStrings(source)))))
287
+ deSuggarAst(LISP.parse(removeNoCode(deSuggarSource(source))))
324
288
  export const src = (source, deps) => {
325
289
  source = prep(source)
326
290
  return LISP.source([
@@ -363,326 +327,3 @@ export const js = (source, deps) => {
363
327
  ])
364
328
  return `${top}${program}`
365
329
  }
366
-
367
- export const deSuggar = (ast) => {
368
- if (ast.length === 0) throw new SyntaxError(`No expressions to evaluate`)
369
- // for (const node of ast)
370
- // if (node[0] && node[0][TYPE] === APPLY && node[0][VALUE] === KEYWORDS.BLOCK)
371
- // throw new SyntaxError(`Top level (${KEYWORDS.BLOCK}) is not allowed`)
372
- let prev = undefined
373
- const evaluate = (exp) => {
374
- const [first, ...rest] = isLeaf(exp) ? [exp] : exp
375
- if (first != undefined) {
376
- switch (first[TYPE]) {
377
- case WORD:
378
- {
379
- switch (first[VALUE]) {
380
- case SUGGAR.POWER:
381
- exp.length = 0
382
- exp.push(...EXPONENTIATION)
383
- break
384
- case SUGGAR.INTEGER_DEVISION:
385
- exp.length = 0
386
- exp.push(...FLOOR)
387
- break
388
- }
389
- }
390
- break
391
- case ATOM:
392
- break
393
- case APPLY:
394
- {
395
- switch (first[VALUE]) {
396
- case KEYWORDS.BLOCK:
397
- {
398
- if (
399
- prev == undefined ||
400
- (prev &&
401
- prev[TYPE] === APPLY &&
402
- prev[VALUE] !== KEYWORDS.ANONYMOUS_FUNCTION)
403
- ) {
404
- exp[0][VALUE] = KEYWORDS.CALL_FUNCTION
405
- exp[0][TYPE] = APPLY
406
- exp.length = 1
407
- exp[1] = [
408
- [APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
409
- [[APPLY, KEYWORDS.BLOCK], ...rest]
410
- ]
411
- deSuggar(exp)
412
- }
413
- }
414
- break
415
- case SUGGAR.PIPE:
416
- {
417
- if (rest.length < 1)
418
- throw new RangeError(
419
- `Invalid number of arguments to (${
420
- SUGGAR.PIPE
421
- }) (>= 1 required). (${SUGGAR.PIPE} ${stringifyArgs(
422
- rest
423
- )})`
424
- )
425
- let inp = rest[0]
426
- exp.length = 0
427
- for (let i = 1; i < rest.length; ++i) {
428
- if (!rest[i].length || rest[i][0][TYPE] !== APPLY)
429
- throw new TypeError(
430
- `Argument at position (${i}) of (${
431
- SUGGAR.PIPE
432
- }) is not an invoked (${
433
- KEYWORDS.ANONYMOUS_FUNCTION
434
- }). (${SUGGAR.PIPE} ${stringifyArgs(rest)})`
435
- )
436
- inp = [rest[i].shift(), inp, ...rest[i]]
437
- }
438
- for (let i = 0; i < inp.length; ++i) exp[i] = inp[i]
439
- deSuggar(exp)
440
- }
441
- break
442
- case SUGGAR.CONDITION:
443
- {
444
- if (rest.length < 2)
445
- throw new RangeError(
446
- `Invalid number of arguments for (${
447
- SUGGAR.CONDITION
448
- }), expected (> 2 required) but got ${rest.length} (${
449
- SUGGAR.CONDITION
450
- } ${stringifyArgs(rest)})`
451
- )
452
- if (rest.length % 2 !== 0)
453
- throw new RangeError(
454
- `Invalid number of arguments for (${
455
- SUGGAR.CONDITION
456
- }), expected even number of arguments but got ${
457
- rest.length
458
- } (${SUGGAR.CONDITION} ${stringifyArgs(rest)})`
459
- )
460
- exp.length = 0
461
- let temp = exp
462
- for (let i = 0; i < rest.length; i += 2) {
463
- if (i === rest.length - 2) {
464
- temp.push([APPLY, KEYWORDS.IF], rest[i], rest.at(-1))
465
- } else {
466
- temp.push([APPLY, KEYWORDS.IF], rest[i], rest[i + 1], [])
467
- temp = temp.at(-1)
468
- }
469
- }
470
- deSuggar(exp)
471
- }
472
- break
473
- case SUGGAR.LIST_TYPE:
474
- {
475
- exp.length = 0
476
- let temp = exp
477
- for (const item of rest) {
478
- temp.push([APPLY, KEYWORDS.ARRAY_TYPE], item, [])
479
- temp = temp.at(-1)
480
- }
481
- temp.push([APPLY, KEYWORDS.ARRAY_TYPE])
482
- }
483
- deSuggar(exp)
484
- break
485
- case SUGGAR.INTEGER_DEVISION:
486
- {
487
- if (rest.length !== 2)
488
- throw new RangeError(
489
- `Invalid number of arguments for (${
490
- SUGGAR.INTEGER_DEVISION
491
- }), expected (= 2) but got ${rest.length} (${
492
- SUGGAR.INTEGER_DEVISION
493
- } ${stringifyArgs(rest)})`
494
- )
495
- else if (rest.some((x) => x[TYPE] === APPLY)) {
496
- exp.length = 0
497
- exp.push([0, KEYWORDS.CALL_FUNCTION], FLOOR, ...rest)
498
- } else {
499
- exp.length = 1
500
- exp[0] = [APPLY, KEYWORDS.BITWISE_OR]
501
- exp.push([[APPLY, KEYWORDS.DIVISION], ...rest])
502
- exp.push([ATOM, 0])
503
- }
504
- }
505
- break
506
- case SUGGAR.POWER:
507
- {
508
- if (rest.length !== 2)
509
- throw new RangeError(
510
- `Invalid number of arguments for (${
511
- SUGGAR.POWER
512
- }), expected (= 2) but got ${rest.length} (${
513
- SUGGAR.POWER
514
- } ${stringifyArgs(rest)})`
515
- )
516
- const isExponentAtom = exp[1][TYPE] === ATOM
517
- const isPowerAtom = exp[2][TYPE] === ATOM
518
- const isExponentWord = exp[1][TYPE] === WORD
519
- if ((isExponentWord || isExponentAtom) && isPowerAtom) {
520
- if (isExponentAtom) {
521
- exp[0][VALUE] = KEYWORDS.MULTIPLICATION
522
- const exponent = exp[1]
523
- const power = exp[2][VALUE]
524
- exp.length = 1
525
- exp.push(exponent, [ATOM, exponent[VALUE] ** (power - 1)])
526
- } else if (isExponentWord) {
527
- const exponent = exp[1]
528
- const power = exp[2]
529
- exp.length = 0
530
- exp.push(
531
- [0, KEYWORDS.CALL_FUNCTION],
532
- EXPONENTIATION,
533
- exponent,
534
- power
535
- )
536
- }
537
- } else {
538
- const exponent = exp[1]
539
- const power = exp[2]
540
- exp.length = 0
541
- exp.push(
542
- [0, KEYWORDS.CALL_FUNCTION],
543
- EXPONENTIATION,
544
- exponent,
545
- power
546
- )
547
- }
548
- deSuggar(exp)
549
- }
550
- break
551
- case KEYWORDS.MULTIPLICATION:
552
- if (!rest.length) {
553
- exp[0][TYPE] = ATOM
554
- exp[0][VALUE] = TRUE
555
- } else if (rest.length > 2) {
556
- exp.length = 0
557
- let temp = exp
558
- for (let i = 0; i < rest.length; i += 1) {
559
- if (i < rest.length - 1) {
560
- temp.push([APPLY, KEYWORDS.MULTIPLICATION], rest[i], [])
561
- temp = temp.at(-1)
562
- } else {
563
- temp.push(...rest[i])
564
- }
565
- }
566
- deSuggar(exp)
567
- }
568
- break
569
- case KEYWORDS.ADDITION:
570
- if (!rest.length) {
571
- exp[0][TYPE] = ATOM
572
- exp[0][VALUE] = FALSE
573
- } else if (rest.length > 2) {
574
- exp.length = 0
575
- let temp = exp
576
- for (let i = 0; i < rest.length; i += 1) {
577
- if (i < rest.length - 1) {
578
- temp.push([APPLY, KEYWORDS.ADDITION], rest[i], [])
579
- temp = temp.at(-1)
580
- } else {
581
- temp.push(...rest[i])
582
- }
583
- }
584
- deSuggar(exp)
585
- }
586
- break
587
- case KEYWORDS.DIVISION:
588
- if (!rest.length) {
589
- exp[0][TYPE] = ATOM
590
- exp[0][VALUE] = FALSE
591
- } else if (rest.length > 2) {
592
- exp.length = 0
593
- let temp = exp
594
- for (let i = 0; i < rest.length; i += 1) {
595
- if (i < rest.length - 1) {
596
- temp.push([APPLY, KEYWORDS.DIVISION], rest[i], [])
597
- temp = temp.at(-1)
598
- } else {
599
- temp.push(...rest[i])
600
- }
601
- }
602
- deSuggar(exp)
603
- }
604
- break
605
- case KEYWORDS.AND:
606
- if (!rest.length) {
607
- exp[0][TYPE] = ATOM
608
- exp[0][VALUE] = FALSE
609
- } else if (rest.length > 2) {
610
- exp.length = 0
611
- let temp = exp
612
- for (let i = 0; i < rest.length; i += 1) {
613
- if (i < rest.length - 1) {
614
- temp.push([APPLY, KEYWORDS.AND], rest[i], [])
615
- temp = temp.at(-1)
616
- } else {
617
- temp.push(...rest[i])
618
- }
619
- }
620
- deSuggar(exp)
621
- }
622
- break
623
- case KEYWORDS.OR:
624
- if (!rest.length) {
625
- exp[0][TYPE] = ATOM
626
- exp[0][VALUE] = FALSE
627
- } else if (rest.length > 2) {
628
- exp.length = 0
629
- let temp = exp
630
- for (let i = 0; i < rest.length; i += 1) {
631
- if (i < rest.length - 1) {
632
- temp.push([APPLY, KEYWORDS.OR], rest[i], [])
633
- temp = temp.at(-1)
634
- } else {
635
- temp.push(...rest[i])
636
- }
637
- }
638
- deSuggar(exp)
639
- }
640
- break
641
- case SUGGAR.UNLESS:
642
- {
643
- if (rest.length > 3 || rest.length < 2)
644
- throw new RangeError(
645
- `Invalid number of arguments for (${
646
- SUGGAR.UNLESS
647
- }), expected (or (= 3) (= 2)) but got ${rest.length} (${
648
- SUGGAR.UNLESS
649
- } ${stringifyArgs(rest)})`
650
- )
651
- exp[0][VALUE] = KEYWORDS.IF
652
- const temp = exp[2]
653
- exp[2] = exp[3] ?? [ATOM, FALSE]
654
- exp[3] = temp
655
- }
656
- deSuggar(exp)
657
- break
658
- case SUGGAR.NOT_EQUAL_1:
659
- case SUGGAR.NOT_EQUAL_2:
660
- {
661
- if (rest.length > 3 || rest.length < 2)
662
- throw new RangeError(
663
- `Invalid number of arguments for (${
664
- exp[0][1]
665
- }), expected (or (= 3) (= 2)) but got ${rest.length} (${
666
- exp[0][1]
667
- } ${stringifyArgs(rest)})`
668
- )
669
- exp[0][VALUE] = KEYWORDS.NOT
670
- exp[1] = [[APPLY, KEYWORDS.EQUAL], exp[1], exp[2]]
671
- exp.length = 2
672
- deSuggar(exp)
673
- }
674
- break
675
- }
676
- prev = first
677
- }
678
- break
679
- default:
680
- for (const e of exp) evaluate(e)
681
- break
682
- }
683
- for (const r of rest) evaluate(r)
684
- }
685
- }
686
- evaluate(ast)
687
- return ast
688
- }