fez-lisp 1.4.11 → 1.4.13

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/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "fez-lisp",
3
3
  "description": "Lisp interpreted & compiled to JavaScript",
4
4
  "author": "AT290690",
5
- "version": "1.4.11",
5
+ "version": "1.4.13",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "keywords": [
package/src/compiler.js CHANGED
@@ -7,18 +7,15 @@ import {
7
7
  VALUE,
8
8
  WORD
9
9
  } from './keywords.js'
10
+ import { OPTIMIZATIONS } from './macros.js'
10
11
  import { leaf, isLeaf, AST } from './parser.js'
11
- export const OPTIMIZATIONS = {
12
- RECURSION: 'recursive',
13
- CACHE: 'memoized'
14
- }
15
12
  const deepRename = (name, newName, tree) => {
16
13
  if (!isLeaf(tree))
17
14
  for (const leaf of tree) {
18
15
  // Figure out a non mutable solution so
19
16
  // I can get rid of deep clone AST.parse(AST.stringify(ast))
20
17
  if (leaf[VALUE] === name) leaf[VALUE] = newName
21
- deepRename(name, newName, leaf)
18
+ else deepRename(name, newName, leaf)
22
19
  }
23
20
  }
24
21
  const earMuffsToLodashes = (name) => name.replace(new RegExp(/\*/g), '_')
@@ -180,52 +177,53 @@ const comp = (tree, Drill) => {
180
177
  }
181
178
  case KEYWORDS.DEFINE_VARIABLE: {
182
179
  const n = tail[0][VALUE]
183
- const prefix = n.split(':')[0]
184
- if (prefix === OPTIMIZATIONS.RECURSION) {
185
- const name = lispToJavaScriptVariableName(n)
186
- const newName = `${OPTIMIZATIONS.RECURSION}_${performance
187
- .now()
188
- .toString()
189
- .replace('.', 7)}`
190
- Drill.Variables.add(name)
191
- Drill.Variables.add(newName)
192
- Drill.Helpers.add('__tco')
193
- const functionArgs = tail.at(-1).slice(1)
194
- const body = functionArgs.pop()
195
- const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
196
- deepRename(n, `()=>${newName}`, body)
197
- const evaluatedBody = comp(body, FunctionDrill)
198
- const vars = FunctionDrill.Variables.size
199
- ? `var ${[...FunctionDrill.Variables].join(',')};`
200
- : ''
201
- return `(${name}=(__tco(${newName}=(${parseArgs(
202
- functionArgs,
203
- Drill
204
- )})=>{${vars}return ${evaluatedBody.toString().trim()}})));`
205
- } else if (prefix === OPTIMIZATIONS.CACHE) {
206
- // memoization here
207
- const name = lispToJavaScriptVariableName(n)
208
- const newName = name.substring(OPTIMIZATIONS.CACHE.length + 1)
209
- Drill.Variables.add(name)
210
- const functionArgs = tail.at(-1).slice(1)
211
- const body = functionArgs.pop()
212
- deepRename(n, newName, body)
213
- const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
214
- const evaluatedBody = comp(body, FunctionDrill)
215
- const vars = FunctionDrill.Variables.size
216
- ? `var ${[...FunctionDrill.Variables].join(',')};`
217
- : ''
218
- return `(${name}=function(){var __${newName}_map = new Map();var ${newName}=(function(${parseArgs(
219
- functionArgs,
220
- Drill
221
- )}){${vars};var __key=[...arguments].join(',');if(__${newName}_map.has(__key)){return __${newName}_map.get(__key)}else{var __res = ${evaluatedBody
222
- .toString()
223
- .trim()};__${newName}_map.set(__key, __res);return __res}});return ${newName}(...arguments)});`
224
- } else {
225
- const name = lispToJavaScriptVariableName(n)
226
- Drill.Variables.add(name)
227
- return `${name}=${comp(tail[1], Drill)};`
228
- }
180
+ // const prefix = n.split(':')[0]
181
+ // if (prefix === OPTIMIZATIONS.RECURSION) {
182
+ // const name = lispToJavaScriptVariableName(n)
183
+ // const newName = `${OPTIMIZATIONS.RECURSION}_${performance
184
+ // .now()
185
+ // .toString()
186
+ // .replace('.', 7)}`
187
+ // Drill.Variables.add(name)
188
+ // Drill.Variables.add(newName)
189
+ // Drill.Helpers.add('__tco')
190
+ // const functionArgs = tail.at(-1).slice(1)
191
+ // const body = functionArgs.pop()
192
+ // const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
193
+ // deepRename(n, `()=>${newName}`, body)
194
+ // const evaluatedBody = comp(body, FunctionDrill)
195
+ // const vars = FunctionDrill.Variables.size
196
+ // ? `var ${[...FunctionDrill.Variables].join(',')};`
197
+ // : ''
198
+ // return `(${name}=(__tco(${newName}=(${parseArgs(
199
+ // functionArgs,
200
+ // Drill
201
+ // )})=>{${vars}return ${evaluatedBody.toString().trim()}})));`
202
+ // } else
203
+
204
+ // if (prefix === OPTIMIZATIONS.CACHE) {
205
+ // // memoization here
206
+ // const name = lispToJavaScriptVariableName(n)
207
+ // const newName = name.substring(OPTIMIZATIONS.CACHE.length + 1)
208
+ // Drill.Variables.add(name)
209
+ // const functionArgs = tail.at(-1).slice(1)
210
+ // const body = functionArgs.pop()
211
+ // deepRename(n, newName, body)
212
+ // const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
213
+ // const evaluatedBody = comp(body, FunctionDrill)
214
+ // const vars = FunctionDrill.Variables.size
215
+ // ? `var ${[...FunctionDrill.Variables].join(',')};`
216
+ // : ''
217
+ // return `(${name}=function(){var __${newName}_map = new Map();var ${newName}=(function(${parseArgs(
218
+ // functionArgs,
219
+ // Drill
220
+ // )}){${vars};var __key=[...arguments].join(',');if(__${newName}_map.has(__key)){return __${newName}_map.get(__key)}else{var __res = ${evaluatedBody
221
+ // .toString()
222
+ // .trim()};__${newName}_map.set(__key, __res);return __res}});return ${newName}(...arguments)});`
223
+ // }
224
+ const name = lispToJavaScriptVariableName(n)
225
+ Drill.Variables.add(name)
226
+ return `${name}=${comp(tail[1], Drill)};`
229
227
  }
230
228
  case KEYWORDS.IS_ATOM:
231
229
  Drill.Helpers.add('atom_predicate')
@@ -306,6 +304,12 @@ const comp = (tree, Drill) => {
306
304
  tail.length === 3 ? comp(tail[2], Drill) : 0
307
305
  });`
308
306
  }
307
+ case KEYWORDS.LOOP: {
308
+ return `(()=>{while(${comp(tail[0], Drill)}){${comp(
309
+ tail[1],
310
+ Drill
311
+ )}}return 0})();`
312
+ }
309
313
  case KEYWORDS.ERROR: {
310
314
  Drill.Helpers.add('__error')
311
315
  return `__error(${compile(tail[0], Drill)})`
@@ -13,6 +13,12 @@ import { evaluate } from './evaluator.js'
13
13
  import { isForbiddenVariableName, stringifyArgs } from './utils.js'
14
14
  import { LISP } from './parser.js'
15
15
  export const keywords = {
16
+ [KEYWORDS.LOOP]: (args, env) => {
17
+ if (args.length != 2)
18
+ throw new RangeError(`Wrong number of args to ${KEYWORDS.LOOP}`)
19
+ while (evaluate(args[0], env) !== FALSE) evaluate(args[1], env)
20
+ return FALSE
21
+ },
16
22
  [KEYWORDS.ADDITION]: (args, env) => {
17
23
  if (args.length !== 2)
18
24
  throw new RangeError(
package/src/keywords.js CHANGED
@@ -7,6 +7,7 @@ export const TRUE = 1
7
7
  export const FALSE = 0
8
8
  export const PLACEHOLDER = '.'
9
9
  export const KEYWORDS = {
10
+ LOOP: 'loop',
10
11
  CREATE_ARRAY: 'array',
11
12
  ARRAY_LENGTH: 'length',
12
13
  IS_ATOM: 'atom?',
@@ -40,7 +41,7 @@ export const KEYWORDS = {
40
41
  DEFINE_VARIABLE: 'let',
41
42
 
42
43
  SET_ARRAY: 'set!',
43
- ERROR: 'throw',
44
+ ERROR: 'throw'
44
45
  }
45
46
 
46
47
  export const TYPES = {
@@ -56,7 +57,6 @@ export const DEBUG = {
56
57
  LOG: 'log',
57
58
  ASSERT: 'assert',
58
59
  CALLSTACK: '(CALLSTACK)' // so that you can't use it in the code
59
-
60
60
  }
61
61
 
62
62
  export const SPECIAL_FORMS_SET = new Set(Object.values(KEYWORDS))
package/src/macros.js CHANGED
@@ -1,9 +1,8 @@
1
- import { AST, isLeaf } from './parser.js'
1
+ import { isLeaf } from './parser.js'
2
2
  import {
3
- EXPONENTIATION,
3
+ EXPONENTIATION_RAW,
4
4
  INTEGER_DIVISION,
5
- NOT_EQUAL,
6
- SLICE
5
+ NOT_EQUAL
7
6
  } from '../lib/baked/macros.js'
8
7
  import {
9
8
  APPLY,
@@ -29,6 +28,19 @@ export const SUGGAR = {
29
28
  INTEGER_DEVISION: '//',
30
29
  CONDITION: 'cond'
31
30
  }
31
+ export const OPTIMIZATIONS = {
32
+ RECURSION: 'recursive',
33
+ CACHE: 'memoized'
34
+ }
35
+ const deepTransform = (predicate, transform, tree) => {
36
+ if (!isLeaf(tree))
37
+ for (const leaf of tree) {
38
+ // Figure out a non mutable solution so
39
+ // I can get rid of deep clone AST.parse(AST.stringify(ast))
40
+ if (predicate(leaf)) transform(leaf)
41
+ else deepTransform(predicate, transform, leaf)
42
+ }
43
+ }
32
44
  export const deSuggarAst = (ast, scope) => {
33
45
  if (scope === undefined) scope = ast
34
46
  if (ast.length === 0) throw new SyntaxError(`No expressions...`)
@@ -53,7 +65,7 @@ export const deSuggarAst = (ast, scope) => {
53
65
  break
54
66
  case SUGGAR.POWER:
55
67
  exp.length = 0
56
- exp.push(...EXPONENTIATION)
68
+ exp.push(...EXPONENTIATION_RAW)
57
69
  break
58
70
  case SUGGAR.INTEGER_DEVISION:
59
71
  exp.length = 0
@@ -214,23 +226,13 @@ export const deSuggarAst = (ast, scope) => {
214
226
  const exponent = exp[1]
215
227
  const power = exp[2]
216
228
  exp.length = 0
217
- exp.push(
218
- [0, KEYWORDS.CALL_FUNCTION],
219
- exponent,
220
- power,
221
- EXPONENTIATION
222
- )
229
+ exp.push([APPLY, 'math:power'], exponent, power)
223
230
  }
224
231
  } else {
225
232
  const exponent = exp[1]
226
233
  const power = exp[2]
227
234
  exp.length = 0
228
- exp.push(
229
- [0, KEYWORDS.CALL_FUNCTION],
230
- exponent,
231
- power,
232
- EXPONENTIATION
233
- )
235
+ exp.push([APPLY, 'math:power'], exponent, power)
234
236
  }
235
237
  deSuggarAst(exp, scope)
236
238
  }
@@ -393,9 +395,10 @@ export const deSuggarAst = (ast, scope) => {
393
395
  break
394
396
  case KEYWORDS.DEFINE_VARIABLE:
395
397
  {
398
+ const last = exp.at(-1)
396
399
  if (!isLeaf(exp[1]) && exp[1][0][TYPE] === APPLY) {
397
400
  const left = exp[1].slice(1)
398
- const right = exp.at(-1)
401
+ const right = last
399
402
  let newScope
400
403
  if (exp[1][0][VALUE] === SUGGAR.CREATE_LIST) {
401
404
  const lastLeft = left.pop()
@@ -478,10 +481,9 @@ export const deSuggarAst = (ast, scope) => {
478
481
  [APPLY, KEYWORDS.DEFINE_VARIABLE],
479
482
  lastLeft,
480
483
  [
481
- [APPLY, KEYWORDS.CALL_FUNCTION],
484
+ [APPLY, 'array:drop'],
482
485
  right,
483
- [ATOM, indexes.at(-1)[0] + 1],
484
- SLICE
486
+ [ATOM, indexes.at(-1)[0] + 1]
485
487
  ]
486
488
  ])
487
489
  }
@@ -489,6 +491,172 @@ export const deSuggarAst = (ast, scope) => {
489
491
  exp.iron = true
490
492
  exp.push(newScope)
491
493
  deSuggarAst(scope)
494
+ } else if (
495
+ !isLeaf(last) &&
496
+ Array.isArray(last) &&
497
+ last[0] &&
498
+ last[0][TYPE] === APPLY &&
499
+ last[0][VALUE] === KEYWORDS.ANONYMOUS_FUNCTION
500
+ ) {
501
+ const name = exp[1][VALUE]
502
+ const prefix = name.split(':')[0]
503
+ if (prefix === OPTIMIZATIONS.RECURSION) {
504
+ const args = last.slice(1, -1)
505
+ const newName = `*${performance
506
+ .now()
507
+ .toString()
508
+ .replace('.', 0)}*`
509
+ deepTransform(
510
+ (leaf) =>
511
+ Array.isArray(leaf) &&
512
+ leaf[0] &&
513
+ leaf[0][TYPE] === APPLY &&
514
+ leaf[0][VALUE] === name,
515
+ (leaf) => {
516
+ const copy = [...leaf]
517
+ leaf.length = 0
518
+ copy[0][VALUE] = newName
519
+ leaf.push([APPLY, KEYWORDS.ANONYMOUS_FUNCTION], copy)
520
+ },
521
+ last
522
+ )
523
+ exp[exp.length - 1] = [
524
+ [APPLY, KEYWORDS.CALL_FUNCTION],
525
+ [
526
+ [APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
527
+ [
528
+ [APPLY, KEYWORDS.BLOCK],
529
+ [
530
+ [APPLY, KEYWORDS.DEFINE_VARIABLE],
531
+ [WORD, newName],
532
+ last
533
+ ],
534
+ [
535
+ [APPLY, KEYWORDS.CALL_FUNCTION],
536
+ [WORD, newName],
537
+ [
538
+ [APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
539
+ [WORD, '*fn*'],
540
+ [
541
+ [APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
542
+ ...args,
543
+ [
544
+ [APPLY, KEYWORDS.BLOCK],
545
+ [
546
+ [APPLY, KEYWORDS.DEFINE_VARIABLE],
547
+ [WORD, '*res*'],
548
+ [
549
+ [APPLY, KEYWORDS.CREATE_ARRAY],
550
+ [[APPLY, '*fn*'], ...args]
551
+ ]
552
+ ],
553
+ [
554
+ [APPLY, KEYWORDS.LOOP],
555
+ [
556
+ [APPLY, KEYWORDS.IS_LAMBDA],
557
+ [
558
+ [APPLY, KEYWORDS.GET_ARRAY],
559
+ [WORD, '*res*'],
560
+ [ATOM, 0]
561
+ ]
562
+ ],
563
+ [
564
+ [APPLY, KEYWORDS.SET_ARRAY],
565
+ [WORD, '*res*'],
566
+ [ATOM, 0],
567
+ [
568
+ [APPLY, KEYWORDS.CALL_FUNCTION],
569
+ [
570
+ [APPLY, KEYWORDS.GET_ARRAY],
571
+ [WORD, '*res*'],
572
+ [ATOM, 0]
573
+ ]
574
+ ]
575
+ ]
576
+ ],
577
+ [
578
+ [APPLY, KEYWORDS.GET_ARRAY],
579
+ [WORD, '*res*'],
580
+ [ATOM, 0]
581
+ ]
582
+ ]
583
+ ]
584
+ ]
585
+ ]
586
+ ]
587
+ ]
588
+ ]
589
+ deSuggarAst(exp[exp.length - 1])
590
+ } else if (prefix === OPTIMIZATIONS.CACHE) {
591
+ // TODO: Make this
592
+ const args = last.slice(1, -1)
593
+ const newName = `*${performance
594
+ .now()
595
+ .toString()
596
+ .replace('.', 0)}*`
597
+ deepTransform(
598
+ (leaf) =>
599
+ Array.isArray(leaf) &&
600
+ leaf[0] &&
601
+ leaf[0][TYPE] === APPLY &&
602
+ leaf[0][VALUE] === name,
603
+ (leaf) => (leaf[0][VALUE] = newName),
604
+ last
605
+ )
606
+ const memoName = newName + ':memo'
607
+ const keyName = newName + ':key'
608
+
609
+ exp[exp.length - 1] = [
610
+ [APPLY, KEYWORDS.CALL_FUNCTION],
611
+ [
612
+ [APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
613
+ [
614
+ [APPLY, KEYWORDS.BLOCK],
615
+ [
616
+ [APPLY, KEYWORDS.DEFINE_VARIABLE],
617
+ [WORD, memoName],
618
+ [[APPLY, 'new:map64']]
619
+ ],
620
+ [
621
+ [APPLY, KEYWORDS.DEFINE_VARIABLE],
622
+ [WORD, newName],
623
+ [
624
+ [APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
625
+ ...args,
626
+ [
627
+ [APPLY, KEYWORDS.BLOCK],
628
+ [
629
+ [APPLY, KEYWORDS.DEFINE_VARIABLE],
630
+ [WORD, keyName],
631
+ [[APPLY, KEYWORDS.CREATE_ARRAY], ...args]
632
+ ],
633
+ [
634
+ [APPLY, KEYWORDS.IF],
635
+ [
636
+ [APPLY, 'map:has?'],
637
+ [WORD, memoName],
638
+ [WORD, keyName]
639
+ ],
640
+ [
641
+ [APPLY, 'map:get'],
642
+ [WORD, memoName],
643
+ [WORD, keyName]
644
+ ],
645
+ [
646
+ [APPLY, 'map:set-and-get!'],
647
+ [WORD, memoName],
648
+ [WORD, keyName],
649
+ last.at(-1)
650
+ ]
651
+ ]
652
+ ]
653
+ ]
654
+ ],
655
+ [WORD, newName]
656
+ ]
657
+ ]
658
+ ]
659
+ }
492
660
  }
493
661
  }
494
662
  break
@@ -507,12 +675,10 @@ export const deSuggarAst = (ast, scope) => {
507
675
  const left = exp[i].slice(1)
508
676
  const right = [WORD, `_arg${i}`]
509
677
  const lastLeft = left.pop()
510
-
511
678
  const vars = left
512
679
  const indexes = vars
513
680
  .map((x, i) => [x, i])
514
681
  .filter((x) => x[0][VALUE] !== PLACEHOLDER)
515
-
516
682
  newBlock.push(
517
683
  ...indexes.map(([name, n]) => {
518
684
  let wrap = right
@@ -545,7 +711,6 @@ export const deSuggarAst = (ast, scope) => {
545
711
  wrap
546
712
  ])
547
713
  }
548
-
549
714
  left[0][TYPE] = WORD
550
715
  exp[i] = right
551
716
  exp[i].length = 2
@@ -572,10 +737,9 @@ export const deSuggarAst = (ast, scope) => {
572
737
  [APPLY, KEYWORDS.DEFINE_VARIABLE],
573
738
  lastLeft,
574
739
  [
575
- [APPLY, KEYWORDS.CALL_FUNCTION],
740
+ [APPLY, 'array:drop'],
576
741
  right,
577
- [ATOM, indexes.at(-1)[0] + 1],
578
- SLICE
742
+ [ATOM, indexes.at(-1)[0] + 1]
579
743
  ]
580
744
  ])
581
745
  exp[i] = right
package/src/utils.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import std from '../lib/baked/std.js'
2
- import { compile, OPTIMIZATIONS } from './compiler.js'
2
+ import { compile } from './compiler.js'
3
3
  import {
4
4
  APPLY,
5
5
  ATOM,
@@ -17,7 +17,8 @@ import { isLeaf, LISP } from './parser.js'
17
17
  import {
18
18
  deSuggarAst,
19
19
  deSuggarSource,
20
- handleUnbalancedQuotes
20
+ handleUnbalancedQuotes,
21
+ OPTIMIZATIONS
21
22
  } from './macros.js'
22
23
  import { keywords } from './interpreter.js'
23
24
  export const logError = (error) =>