fez-lisp 1.2.43 → 1.2.45

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.2.43",
5
+ "version": "1.2.45",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "keywords": [
package/src/compiler.js CHANGED
@@ -8,13 +8,22 @@ import {
8
8
  WORD
9
9
  } from './keywords.js'
10
10
  import { leaf, isLeaf } from './parser.js'
11
- const deepRename = (name, newName, tree) => {
11
+ const deepRenameTco = (name, newName, tree) => {
12
12
  if (!isLeaf(tree))
13
13
  for (const leaf of tree) {
14
14
  // Figure out a non mutable solution so
15
15
  // I can get rid of deep copy
16
16
  if (leaf[VALUE] === name) leaf[VALUE] = `()=>${newName}`
17
- deepRename(name, newName, leaf)
17
+ deepRenameTco(name, newName, leaf)
18
+ }
19
+ }
20
+ const deepRenameCache = (name, newName, tree) => {
21
+ if (!isLeaf(tree))
22
+ for (const leaf of tree) {
23
+ // Figure out a non mutable solution so
24
+ // I can get rid of deep copy
25
+ if (leaf[VALUE] === name) leaf[VALUE] = newName
26
+ deepRenameCache(name, newName, leaf)
18
27
  }
19
28
  }
20
29
  const earMuffsToLodashes = (name) => name.replace(new RegExp(/\*/g), '_')
@@ -109,7 +118,7 @@ const Helpers = {
109
118
  __tco: `__tco=fn=>(...args)=>{let result=fn(...args);while(typeof result==='function')result=result();return result}`,
110
119
  atom_predicate: `atom_predicate=(number)=>+(typeof number==='number')`,
111
120
  lambda_predicate: `lambda_predicate=(fm)=>+(typeof fn==='function')`,
112
- set_effect: `set_effect=(array,index,value)=>{if(index<0){const target=array.length+index;while(array.length!==target)array.pop()}else array[index] = value;return array}`,
121
+ set_effect: `set_effect=function(array,index,value){if(arguments.length===1){array.pop()}else{array[index] = value};return array}`,
113
122
  __error: `__error=(error)=>{throw new Error(error.map((x)=>String.fromCharCode(x)).join(''))}`
114
123
  }
115
124
  const semiColumnEdgeCases = new Set([
@@ -166,16 +175,20 @@ const compile = (tree, Drill) => {
166
175
  }
167
176
  case KEYWORDS.DEFINE_VARIABLE: {
168
177
  const n = Arguments[0][VALUE]
169
- if (n.split(':')[0] === KEYWORDS.RECURSION) {
178
+ const prefix = n.split(':')[0]
179
+ if (prefix === KEYWORDS.RECURSION) {
170
180
  const name = lispToJavaScriptVariableName(n)
171
- const newName = `rec_${performance.now().toString().replace('.', 7)}`
181
+ const newName = `recursive_${performance
182
+ .now()
183
+ .toString()
184
+ .replace('.', 7)}`
172
185
  Drill.Variables.add(name)
173
186
  Drill.Variables.add(newName)
174
187
  Drill.Helpers.add('__tco')
175
188
  const functionArgs = Arguments.at(-1).slice(1)
176
189
  const body = functionArgs.pop()
177
190
  const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
178
- deepRename(n, newName, body)
191
+ deepRenameTco(n, newName, body)
179
192
  const evaluatedBody = compile(body, FunctionDrill)
180
193
  const vars = FunctionDrill.Variables.size
181
194
  ? `var ${[...FunctionDrill.Variables].join(',')};`
@@ -186,6 +199,25 @@ const compile = (tree, Drill) => {
186
199
  )})=>{${vars}return ${evaluatedBody
187
200
  .toString()
188
201
  .trim()}}, ${newName})));`
202
+ } else if (prefix === KEYWORDS.CACHE) {
203
+ // memoization here
204
+ const name = lispToJavaScriptVariableName(n)
205
+ const newName = name.substring(KEYWORDS.CACHE.length + 1)
206
+ Drill.Variables.add(name)
207
+ const functionArgs = Arguments.at(-1).slice(1)
208
+ const body = functionArgs.pop()
209
+ deepRenameCache(n, newName, body)
210
+ const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
211
+ const evaluatedBody = compile(body, FunctionDrill)
212
+ const vars = FunctionDrill.Variables.size
213
+ ? `var ${[...FunctionDrill.Variables].join(',')};`
214
+ : ''
215
+ return `(${name}=function(){var __${newName}_map = new Map();var ${newName}=(function(${parseArgs(
216
+ functionArgs,
217
+ Drill
218
+ )}){${vars};var __key=[...arguments].join(',');if(__${newName}_map.has(__key)){return __${newName}_map.get(__key)}else{var __res = ${evaluatedBody
219
+ .toString()
220
+ .trim()};__${newName}_map.set(__key, __res);return __res}});return ${newName}(...arguments)});`
189
221
  } else {
190
222
  const name = lispToJavaScriptVariableName(n)
191
223
  Drill.Variables.add(name)
@@ -198,10 +230,6 @@ const compile = (tree, Drill) => {
198
230
  case KEYWORDS.IS_LAMBDA:
199
231
  Drill.Helpers.add('lambda_predicate')
200
232
  return `lambda_predicate(${compile(Arguments[0], Drill)});`
201
- case KEYWORDS.NUMBER_TYPE:
202
- return '0'
203
- case KEYWORDS.BOOLEAN_TYPE:
204
- return '1'
205
233
  case KEYWORDS.ARRAY_TYPE:
206
234
  return `[${parseArgs(Arguments, Drill)}];`
207
235
  case KEYWORDS.ARRAY_LENGTH:
@@ -675,11 +675,11 @@ export const keywords = {
675
675
  // return evaluate(inp, env)
676
676
  // },
677
677
  [KEYWORDS.SET_ARRAY]: (args, env) => {
678
- if (args.length !== 2 && args.length !== 3)
678
+ if (args.length !== 1 && args.length !== 3)
679
679
  throw new RangeError(
680
680
  `Invalid number of arguments for (${
681
681
  KEYWORDS.SET_ARRAY
682
- }) (or 2 3) required (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
682
+ }) (or 1 3) required (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
683
683
  )
684
684
  const array = evaluate(args[0], env)
685
685
  if (!Array.isArray(array))
@@ -688,31 +688,17 @@ export const keywords = {
688
688
  KEYWORDS.ARRAY_TYPE
689
689
  }) but got (${array}) (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
690
690
  )
691
- const index = evaluate(args[1], env)
692
- if (!Number.isInteger(index))
693
- throw new TypeError(
694
- `Second argument of (${KEYWORDS.SET_ARRAY}) must be an (${
695
- KEYWORDS.NUMBER_TYPE
696
- } integer) (${index}) (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
697
- )
698
- if (index > array.length)
699
- throw new RangeError(
700
- `Second argument of (${KEYWORDS.SET_ARRAY}) is outside of the (${
701
- KEYWORDS.ARRAY_TYPE
702
- }) bounds (index ${index} bounds ${array.length}) (${
703
- KEYWORDS.SET_ARRAY
704
- } ${stringifyArgs(args)})`
705
- )
706
- if (index < 0) {
707
- if (args.length !== 2)
708
- throw new RangeError(
709
- `Invalid number of arguments for (${
710
- KEYWORDS.SET_ARRAY
711
- }) (if (< index 0) then 2 required) (${
712
- KEYWORDS.SET_ARRAY
713
- } ${stringifyArgs(args)})`
691
+ if (args.length === 1) {
692
+ array.pop()
693
+ } else {
694
+ const index = evaluate(args[1], env)
695
+ if (!Number.isInteger(index) || index < 0)
696
+ throw new TypeError(
697
+ `Second argument of (${KEYWORDS.SET_ARRAY}) must be a positive (${
698
+ KEYWORDS.NUMBER_TYPE
699
+ } integer) (${index}) (${KEYWORDS.SET_ARRAY} ${stringifyArgs(args)})`
714
700
  )
715
- if (index * -1 > array.length)
701
+ if (index > array.length)
716
702
  throw new RangeError(
717
703
  `Second argument of (${KEYWORDS.SET_ARRAY}) is outside of the (${
718
704
  KEYWORDS.ARRAY_TYPE
@@ -720,17 +706,6 @@ export const keywords = {
720
706
  KEYWORDS.SET_ARRAY
721
707
  } ${stringifyArgs(args)})`
722
708
  )
723
- const target = array.length + index
724
- while (array.length !== target) array.pop()
725
- } else {
726
- if (args.length !== 3)
727
- throw new RangeError(
728
- `Invalid number of arguments for (${
729
- KEYWORDS.SET_ARRAY
730
- }) (if (>= index 0) then 3 required) (${
731
- KEYWORDS.SET_ARRAY
732
- } ${stringifyArgs(args)})`
733
- )
734
709
  const value = evaluate(args[2], env)
735
710
  if (value == undefined)
736
711
  throw new RangeError(
@@ -819,4 +794,4 @@ export const keywords = {
819
794
  )
820
795
  throw new Error(expression.map((x) => String.fromCharCode(x)).join(''))
821
796
  }
822
- }
797
+ }
package/src/keywords.js CHANGED
@@ -57,7 +57,9 @@ export const KEYWORDS = {
57
57
  NOT_EQUAL_2: '<>',
58
58
  UNLESS: 'unless',
59
59
  LIST_TYPE: 'list',
60
- RECURSION: 'rec'
60
+
61
+ RECURSION: 'recursive',
62
+ CACHE: 'memoized'
61
63
  }
62
64
 
63
65
  export const TYPES = {
package/src/utils.js CHANGED
@@ -102,6 +102,8 @@ export const isForbiddenVariableName = (name) => {
102
102
  switch (name) {
103
103
  case '_':
104
104
  case KEYWORDS.DEFINE_VARIABLE:
105
+ case KEYWORDS.RECURSION:
106
+ case KEYWORDS.CACHE:
105
107
  return true
106
108
  default:
107
109
  return !isNaN(name[0])
@@ -262,9 +264,8 @@ export const fez = (source, options = {}) => {
262
264
  }
263
265
  } catch (error) {
264
266
  // console.log(error)
265
- const err = error.message
266
- .replace("'[object Array]'", '(array)')
267
- .replace('object', '(array)')
267
+ const err = error.message.replace("'[object Array]'", '(array)')
268
+ // .replace('object', '(array)')
268
269
  logError(err)
269
270
  if (options.throw) throw err
270
271
  return err