fez-lisp 1.0.54 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
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.0.54",
5
+ "version": "1.1.2",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "keywords": [
package/src/compiler.js CHANGED
@@ -8,8 +8,65 @@ import {
8
8
  WORD
9
9
  } from './keywords.js'
10
10
  import { leaf, isLeaf } from './parser.js'
11
- import { deepRename, lispToJavaScriptVariableName } from './utils.js'
11
+ import { deepRename } from './utils.js'
12
+ const earMuffsToLodashes = (name) => name.replace(new RegExp(/\*/g), '_')
13
+ const dotNamesToEmpty = (name) => name.replace(new RegExp(/\./g), '')
14
+ const commaToLodash = (name) => name.replace(new RegExp(/\,/g), '_')
15
+ const arrowFromTo = (name) => name.replace(new RegExp(/->/g), '-to-')
16
+ const moduleNameToLodashes = (name) => name.replace(new RegExp(/:/g), '_')
17
+ const questionMarkToPredicate = (name) =>
18
+ name.replace(new RegExp(/\?/g), 'Predicate')
19
+ const exclamationMarkMarkToEffect = (name) =>
20
+ name.replace(new RegExp(/\!/g), 'Effect')
21
+ const toCamelCase = (name) => {
22
+ let out = name[0]
23
+ for (let i = 1; i < name.length; ++i) {
24
+ const current = name[i],
25
+ prev = name[i - 1]
26
+ if (current === '-') continue
27
+ else if (prev === '-') out += current.toUpperCase()
28
+ else out += current
29
+ }
30
+ return out
31
+ }
32
+ const keywordToHelper = (name) => {
33
+ switch (name) {
34
+ case KEYWORDS.ADDITION:
35
+ return '__add'
36
+ case KEYWORDS.MULTIPLICATION:
37
+ return '__mult'
38
+ case KEYWORDS.SUBTRACTION:
39
+ return '__sub'
40
+ case KEYWORDS.GREATHER_THAN:
41
+ return '__gt'
42
+ case KEYWORDS.EQUAL:
43
+ return '__eq'
44
+ case KEYWORDS.GREATHER_THAN_OR_EQUAL:
45
+ return '__gteq'
46
+ case KEYWORDS.LESS_THAN:
47
+ return '__lt'
48
+ case KEYWORDS.LESS_THAN_OR_EQUAL:
49
+ return '__lteq'
50
+ default:
51
+ return name
52
+ }
53
+ }
54
+ const lispToJavaScriptVariableName = (name) =>
55
+ toCamelCase(
56
+ arrowFromTo(
57
+ dotNamesToEmpty(
58
+ exclamationMarkMarkToEffect(
59
+ questionMarkToPredicate(
60
+ commaToLodash(
61
+ moduleNameToLodashes(earMuffsToLodashes(keywordToHelper(name)))
62
+ )
63
+ )
64
+ )
65
+ )
66
+ )
67
+ )
12
68
  const Helpers = {
69
+ __string: `__string=(...args)=>{const str=args.flat();str.isString=true;return str}`,
13
70
  __add: `__add=(...numbers)=>{return numbers.reduce((a,b)=>a+b,0)}`,
14
71
  __sub: `__sub=(...numbers)=>{return numbers.reduce((a,b)=>a-b,0)}`,
15
72
  __mult: `__mult=(...numbers)=>{return numbers.reduce((a,b)=>a*b,1)}`,
@@ -46,13 +103,10 @@ const Helpers = {
46
103
  length: 'length=(arr)=>arr.length',
47
104
  __tco: `__tco=fn=>(...args)=>{let result=fn(...args);while(typeof result==='function')result=result();return result}`,
48
105
  numberPredicate: `numberPredicate=(number)=>+(typeof number==='number')`,
49
- stringPredicate: `stringPredicate=(string)=>+(typeof string==='string')`,
50
106
  lambdaPredicate: `lambdaPredicate=(lambda)=>+(typeof lambda==='function')`,
51
107
  arrayPredicate: `arrayPredicate=(array)=>+Array.isArray(array)`,
52
- atomPredicate: `atomPredicate=(value)=>+(typeof value==='number'||typeof value==='string')`,
53
108
  error: `error=(error)=>{throw new Error(error)}`,
54
- array_setEffect: `array_setEffect=(array,index,value)=>{if(index<0){const target=array.length+index;while(array.length!==target)array.pop()}else array[index] = value;return array}`,
55
- cast: `cast=(type,value)=>{switch(type){case '${KEYWORDS.NUMBER_TYPE}':return Number(value);case '${KEYWORDS.STRING_TYPE}':return value.toString();case '${KEYWORDS.ARRAY_TYPE}':return typeof value==='number'?[...Number(value).toString()].map(Number):[...value];case '${KEYWORDS.BOOLEAN_TYPE}':return +!!value;case '${KEYWORDS.ANONYMOUS_FUNCTION}':return ()=>value;case '${KEYWORDS.CHAR_CODE_TYPE}':return value.charCodeAt(0);case '${KEYWORDS.CHAR_TYPE}':return String.fromCharCode(value);default:return 0}}`
109
+ array_setEffect: `array_setEffect=(array,index,value)=>{if(index<0){const target=array.length+index;while(array.length!==target)array.pop()}else array[index] = value;return array}`
56
110
  }
57
111
  const semiColumnEdgeCases = new Set([
58
112
  ';)',
@@ -116,9 +170,6 @@ const compile = (tree, Drill) => {
116
170
  out += `),${name});`
117
171
  return out
118
172
  }
119
- case KEYWORDS.IS_STRING:
120
- Drill.Helpers.add('stringPredicate')
121
- return `stringPredicate(${compile(Arguments[0], Drill)});`
122
173
  case KEYWORDS.IS_NUMBER:
123
174
  Drill.Helpers.add('numberPredicate')
124
175
  return `numberPredicate(${compile(Arguments[0], Drill)});`
@@ -133,19 +184,17 @@ const compile = (tree, Drill) => {
133
184
  case KEYWORDS.BOOLEAN_TYPE:
134
185
  return '1'
135
186
  case KEYWORDS.STRING_TYPE:
136
- return '""'
187
+ Drill.Helpers.add('__string')
188
+ return `__string(${parseArgs(Arguments, Drill)});`
137
189
  case KEYWORDS.ARRAY_TYPE:
138
190
  return Arguments.length === 2 &&
139
191
  Arguments[1][TYPE] === WORD &&
140
192
  Arguments[1][VALUE] === 'length'
141
- ? `(new Array(${compile(Arguments[0], Drill)}).fill(0))`
193
+ ? `(new Array(${compile(Arguments[0], Drill)}).fill(0));`
142
194
  : `[${parseArgs(Arguments, Drill)}];`
143
- case KEYWORDS.ARRAY_OR_STRING_LENGTH:
195
+ case KEYWORDS.ARRAY_LENGTH:
144
196
  Drill.Helpers.add('length')
145
197
  return `length(${compile(Arguments[0], Drill)})`
146
- case KEYWORDS.IS_ATOM:
147
- Drill.Helpers.add('atomPredicate')
148
- return `atomPredicate(${compile(Arguments[0], Drill)});`
149
198
  case KEYWORDS.FIRST_ARRAY:
150
199
  Drill.Helpers.add('car')
151
200
  return `car(${compile(Arguments[0], Drill)});`
@@ -295,9 +344,6 @@ const compile = (tree, Drill) => {
295
344
  out += '0);'
296
345
  return out
297
346
  }
298
- case KEYWORDS.CAST_TYPE:
299
- Drill.Helpers.add('cast')
300
- return `cast("${Arguments[1][VALUE]}", ${compile(Arguments[0], Drill)})`
301
347
  case KEYWORDS.PIPE: {
302
348
  let inp = Arguments[0]
303
349
  for (let i = 1; i < Arguments.length; ++i)
@@ -319,10 +365,7 @@ const compile = (tree, Drill) => {
319
365
  return `${camelCased}(${parseArgs(Arguments, Drill)});`
320
366
  }
321
367
  }
322
- } else if (first[TYPE] === ATOM)
323
- return typeof first[VALUE] === 'string'
324
- ? `\`${first[VALUE]}\``
325
- : first[VALUE]
368
+ } else if (first[TYPE] === ATOM) return first[VALUE]
326
369
  else if (first[TYPE] === WORD) {
327
370
  const camelCased = lispToJavaScriptVariableName(token)
328
371
  if (camelCased in Helpers) Drill.Helpers.add(camelCased)
package/src/evaluator.js CHANGED
@@ -36,7 +36,7 @@ export const isAtom = (arg, env) => {
36
36
  if (arg[TYPE] === ATOM) return 1
37
37
  else {
38
38
  const atom = evaluate(arg, env)
39
- return +(typeof atom === 'number' || typeof atom === 'string')
39
+ return +(typeof atom === 'number')
40
40
  }
41
41
  }
42
42
  export const run = (tree, env = {}) =>
@@ -8,26 +8,7 @@ import {
8
8
  isForbiddenVariableName,
9
9
  stringifyArgs
10
10
  } from './utils.js'
11
-
12
11
  const keywords = {
13
- [KEYWORDS.CONCATENATION]: (args, env) => {
14
- if (args.length < 2)
15
- throw new RangeError(
16
- `Invalid number of arguments for (${
17
- KEYWORDS.CONCATENATION
18
- }), expected > 1 but got ${args.length}. (${
19
- KEYWORDS.CONCATENATION
20
- } ${stringifyArgs(args)})`
21
- )
22
- const operands = args.map((x) => evaluate(x, env))
23
- if (operands.some((x) => typeof x !== 'string'))
24
- throw new TypeError(
25
- `Not all arguments of (${KEYWORDS.CONCATENATION}) are (${
26
- KEYWORDS.STRING_TYPE
27
- }) (${KEYWORDS.CONCATENATION} ${stringifyArgs(args)})`
28
- )
29
- return operands.reduce((a, b) => a + b, '')
30
- },
31
12
  [KEYWORDS.REMAINDER_OF_DIVISION]: (args, env) => {
32
13
  if (args.length < 2)
33
14
  throw new RangeError(
@@ -92,23 +73,19 @@ const keywords = {
92
73
  )
93
74
  return operands.reduce((a, b) => a / b)
94
75
  },
95
- [KEYWORDS.ARRAY_OR_STRING_LENGTH]: (args, env) => {
76
+ [KEYWORDS.ARRAY_LENGTH]: (args, env) => {
96
77
  if (args.length !== 1)
97
78
  throw new RangeError(
98
79
  `Invalid number of arguments for (${
99
- KEYWORDS.ARRAY_OR_STRING_LENGTH
100
- }) (= 1 required) (${KEYWORDS.ARRAY_OR_STRING_LENGTH} ${stringifyArgs(
101
- args
102
- )})`
80
+ KEYWORDS.ARRAY_LENGTH
81
+ }) (= 1 required) (${KEYWORDS.ARRAY_LENGTH} ${stringifyArgs(args)})`
103
82
  )
104
83
  const array = evaluate(args[0], env)
105
- if (!(Array.isArray(array) || typeof array === 'string'))
84
+ if (!Array.isArray(array))
106
85
  throw new TypeError(
107
- `First argument of (${
108
- KEYWORDS.ARRAY_OR_STRING_LENGTH
109
- }) must be an (or ${KEYWORDS.ARRAY_TYPE} ${KEYWORDS.STRING_TYPE}) (${
110
- KEYWORDS.ARRAY_OR_STRING_LENGTH
111
- } ${stringifyArgs(args)})`
86
+ `First argument of (${KEYWORDS.ARRAY_LENGTH}) must be a ${
87
+ KEYWORDS.ARRAY_TYPE
88
+ } (${KEYWORDS.ARRAY_LENGTH} ${stringifyArgs(args)})`
112
89
  )
113
90
  return array.length
114
91
  },
@@ -131,15 +108,6 @@ const keywords = {
131
108
  )
132
109
  return +(typeof evaluate(args[0], env) === 'number')
133
110
  },
134
- [KEYWORDS.IS_STRING]: (args, env) => {
135
- if (args.length !== 1)
136
- throw new RangeError(
137
- `Invalid number of arguments for (${
138
- KEYWORDS.IS_STRING
139
- }) (= 1 required) (${KEYWORDS.IS_STRING} ${stringifyArgs(args)})`
140
- )
141
- return +(typeof evaluate(args[0], env) === 'string')
142
- },
143
111
  [KEYWORDS.IS_FUNCTION]: (args, env) => {
144
112
  if (args.length !== 1)
145
113
  throw new RangeError(
@@ -262,6 +230,11 @@ const keywords = {
262
230
  if (evaluate(args[i], env)) return evaluate(args[i + 1], env)
263
231
  return 0
264
232
  },
233
+ [KEYWORDS.STRING_TYPE]: (args, env) => {
234
+ const str = args.flatMap((x) => evaluate(x, env))
235
+ str.isString = true
236
+ return str
237
+ },
265
238
  [KEYWORDS.ARRAY_TYPE]: (args, env) => {
266
239
  if (!args.length) return []
267
240
  const isCapacity =
@@ -284,15 +257,6 @@ const keywords = {
284
257
  }
285
258
  return args.map((x) => evaluate(x, env))
286
259
  },
287
- [KEYWORDS.IS_ATOM]: (args, env) => {
288
- if (args.length !== 1)
289
- throw new RangeError(
290
- `Invalid number of arguments for (${
291
- KEYWORDS.IS_ATOM
292
- }) (= 1 required) (${KEYWORDS.IS_ATOM} ${stringifyArgs(args)})`
293
- )
294
- return isAtom(args[0], env)
295
- },
296
260
  [KEYWORDS.FIRST_ARRAY]: (args, env) => {
297
261
  if (args.length !== 1)
298
262
  throw new RangeError(
@@ -439,7 +403,7 @@ const keywords = {
439
403
  const b = evaluate(args[1], env)
440
404
  if (typeof a !== 'number')
441
405
  throw new TypeError(
442
- `Invalid use of (${KEYWORDS.EQUAL}), first arguments are not an ${
406
+ `Invalid use of (${KEYWORDS.EQUAL}), first argument is not an ${
443
407
  KEYWORDS.NUMBER_TYPE
444
408
  } (${KEYWORDS.EQUAL} ${stringifyArgs(args)})`
445
409
  )
@@ -462,7 +426,7 @@ const keywords = {
462
426
  const b = evaluate(args[1], env)
463
427
  if (typeof a !== 'number')
464
428
  throw new TypeError(
465
- `Invalid use of (${KEYWORDS.LESS_THAN}), first arguments are not an ${
429
+ `Invalid use of (${KEYWORDS.LESS_THAN}), first argument is not an ${
466
430
  KEYWORDS.NUMBER_TYPE
467
431
  } (${KEYWORDS.LESS_THAN} ${stringifyArgs(args)})`
468
432
  )
@@ -485,11 +449,9 @@ const keywords = {
485
449
  const b = evaluate(args[1], env)
486
450
  if (typeof a !== 'number')
487
451
  throw new TypeError(
488
- `Invalid use of (${
489
- KEYWORDS.GREATHER_THAN
490
- }), first arguments are not an ${KEYWORDS.NUMBER_TYPE} (${
491
- KEYWORDS.GREATHER_THAN
492
- } ${stringifyArgs(args)})`
452
+ `Invalid use of (${KEYWORDS.GREATHER_THAN}), first argument is not an ${
453
+ KEYWORDS.NUMBER_TYPE
454
+ } (${KEYWORDS.GREATHER_THAN} ${stringifyArgs(args)})`
493
455
  )
494
456
  if (typeof b !== 'number')
495
457
  throw new TypeError(
@@ -516,7 +478,7 @@ const keywords = {
516
478
  throw new TypeError(
517
479
  `Invalid use of (${
518
480
  KEYWORDS.GREATHER_THAN_OR_EQUAL
519
- }), first arguments are not an ${KEYWORDS.NUMBER_TYPE} (${
481
+ }), first argument is not an ${KEYWORDS.NUMBER_TYPE} (${
520
482
  KEYWORDS.GREATHER_THAN_OR_EQUAL
521
483
  } ${stringifyArgs(args)})`
522
484
  )
@@ -545,7 +507,7 @@ const keywords = {
545
507
  throw new TypeError(
546
508
  `Invalid use of (${
547
509
  KEYWORDS.LESS_THAN_OR_EQUAL
548
- }), first arguments are not an ${KEYWORDS.NUMBER_TYPE} (${
510
+ }), first argument is not an ${KEYWORDS.NUMBER_TYPE} (${
549
511
  KEYWORDS.LESS_THAN_OR_EQUAL
550
512
  } ${stringifyArgs(args)})`
551
513
  )
@@ -643,7 +605,6 @@ const keywords = {
643
605
  })
644
606
  return env[name]
645
607
  },
646
- [KEYWORDS.STRING_TYPE]: () => '',
647
608
  [KEYWORDS.NUMBER_TYPE]: () => 0,
648
609
  [KEYWORDS.BOOLEAN_TYPE]: () => 1,
649
610
  [KEYWORDS.CAST_TYPE]: (args, env) => {
@@ -671,60 +632,13 @@ const keywords = {
671
632
  )
672
633
  return num
673
634
  }
674
- case KEYWORDS.STRING_TYPE:
675
- return value.toString()
676
635
  case KEYWORDS.BOOLEAN_TYPE:
677
636
  return +!!value
678
- case KEYWORDS.ARRAY_TYPE: {
679
- if (typeof value === 'number')
680
- return [...Number(value).toString()].map(Number)
681
- else if (typeof value[Symbol.iterator] !== 'function')
682
- throw new TypeError(
683
- `Arguments are not iterable for ${KEYWORDS.ARRAY_TYPE} at (${
684
- KEYWORDS.CAST_TYPE
685
- }) (${KEYWORDS.CAST_TYPE} ${stringifyArgs(args)})`
686
- )
687
- return [...value]
688
- }
689
- case KEYWORDS.CHAR_TYPE: {
690
- const index = evaluate(args[0], env)
691
- if (!Number.isInteger(index) || index < 0)
692
- throw new TypeError(
693
- `Arguments are not (+ ${KEYWORDS.NUMBER_TYPE}) for ${
694
- KEYWORDS.CHAR_TYPE
695
- } at (${KEYWORDS.CAST_TYPE}) (${
696
- KEYWORDS.CAST_TYPE
697
- } ${stringifyArgs(args)})`
698
- )
699
- return String.fromCharCode(index)
700
- }
701
- case KEYWORDS.CHAR_CODE_TYPE: {
702
- const string = evaluate(args[0], env)
703
- if (typeof string !== 'string')
704
- throw new TypeError(
705
- `Argument is not (${KEYWORDS.STRING_TYPE}) for ${
706
- KEYWORDS.CHAR_CODE_TYPE
707
- } at (${KEYWORDS.CAST_TYPE}) (${
708
- KEYWORDS.CAST_TYPE
709
- } ${stringifyArgs(args)})`
710
- )
711
- if (string.length !== 1)
712
- throw new RangeError(
713
- `Argument is not of (= (length ${KEYWORDS.STRING_TYPE}) 1) for ${
714
- KEYWORDS.CHAR_CODE_TYPE
715
- } at (${KEYWORDS.CAST_TYPE}) (${
716
- KEYWORDS.CAST_TYPE
717
- } ${stringifyArgs(args)})`
718
- )
719
- return string.charCodeAt(0)
720
- }
721
637
  default:
722
638
  throw new TypeError(
723
- `Can only cast (or ${KEYWORDS.NUMBER_TYPE} ${
724
- KEYWORDS.STRING_TYPE
725
- } ${KEYWORDS.ARRAY_TYPE} ${KEYWORDS.BOOLEAN_TYPE} ${
726
- KEYWORDS.CHAR_TYPE
727
- } ${KEYWORDS.CHAR_CODE_TYPE}) at (${KEYWORDS.CAST_TYPE}) (${
639
+ `Can only cast (or ${KEYWORDS.NUMBER_TYPE} ${KEYWORDS.ARRAY_TYPE} ${
640
+ KEYWORDS.BOOLEAN_TYPE
641
+ }) at (${KEYWORDS.CAST_TYPE}) (${
728
642
  KEYWORDS.CAST_TYPE
729
643
  } ${stringifyArgs(args)})`
730
644
  )
@@ -965,18 +879,17 @@ const keywords = {
965
879
  return keywords[KEYWORDS.DEFINE_VARIABLE](args, env)
966
880
  },
967
881
  [KEYWORDS.TEST_CASE]: (args, env) => {
968
- if (args.length !== 3)
882
+ if (args.length !== 2)
969
883
  throw new RangeError(
970
884
  `Invalid number of arguments to (${
971
885
  KEYWORDS.TEST_CASE
972
- }) (= 3 required) (${KEYWORDS.TEST_CASE} ${stringifyArgs(args)})`
886
+ }) (= 2 required) (${KEYWORDS.TEST_CASE} ${stringifyArgs(args)})`
973
887
  )
974
- const description = evaluate(args[0], env)
975
- const a = evaluate(args[1], env)
976
- const b = evaluate(args[2], env)
888
+ const a = evaluate(args[0], env)
889
+ const b = evaluate(args[1], env)
977
890
  return !isEqualTypes(a, b) || !isEqual(a, b)
978
- ? [0, description, stringifyArgs([args[1]]), b, a]
979
- : [1, description, stringifyArgs([args[1]]), a]
891
+ ? [0, stringifyArgs([args[0]]), b, a]
892
+ : [1, stringifyArgs([args[0]]), a]
980
893
  },
981
894
  [KEYWORDS.TEST_BED]: (args, env) => {
982
895
  let tests = []
@@ -994,15 +907,13 @@ const keywords = {
994
907
  )
995
908
  tests = args.map((x) => evaluate(x, env))
996
909
  res = tests.reduce(
997
- (acc, [state, describe, ...rest]) =>
910
+ (acc, [state, ...rest]) =>
998
911
  `${acc}${
999
912
  !state
1000
- ? `x ${describe} Failed:\n ${rest[0]}\n + ${LISP.stringify(
913
+ ? `x ${rest[0]}\n + ${LISP.stringify(
1001
914
  rest[1]
1002
915
  )}\n - ${LISP.stringify(rest[2])}\n`
1003
- : `✓ ${describe} Passed:\n ${rest[0]}\n + ${LISP.stringify(
1004
- rest[1]
1005
- )}\n`
916
+ : `✓ ${rest[0]}\n + ${LISP.stringify(rest[1])}\n`
1006
917
  }`,
1007
918
  ''
1008
919
  )
package/src/keywords.js CHANGED
@@ -9,20 +9,13 @@ export const ATOM = 2
9
9
  export const PLACEHOLDER = '.'
10
10
  // keywords aliases
11
11
  export const KEYWORDS = {
12
- STRING_TYPE: 'string',
13
12
  NUMBER_TYPE: 'number',
14
- BOOLEAN_TYPE: 'boolean',
13
+ STRING_TYPE: 'string',
15
14
  ARRAY_TYPE: 'array',
16
- CHAR_CODE_TYPE: 'char-code',
17
- CHAR_TYPE: 'char',
18
- CAST_TYPE: 'type',
19
- CONCATENATION: 'string:merge',
20
- ARRAY_OR_STRING_LENGTH: 'length',
15
+ ARRAY_LENGTH: 'length',
21
16
  IS_ARRAY: 'array?',
22
17
  IS_NUMBER: 'number?',
23
- IS_STRING: 'string?',
24
18
  IS_FUNCTION: 'lambda?',
25
- IS_ATOM: 'atom?',
26
19
  ADDITION: '+',
27
20
  SUBTRACTION: '-',
28
21
  MULTIPLICATION: '*',
package/src/parser.js CHANGED
@@ -1,5 +1,4 @@
1
1
  import { APPLY, ATOM, TYPE, WORD, VALUE } from './keywords.js'
2
- import { escape, preserveEscape } from './utils.js'
3
2
  export const leaf = (type, value) => [type, value]
4
3
  export const isLeaf = ([car]) => car === APPLY || car === ATOM || car === WORD
5
4
  export const LISP = {
@@ -10,15 +9,6 @@ export const LISP = {
10
9
  acc = ''
11
10
  for (let i = 0; i < source.length; ++i) {
12
11
  const cursor = source[i]
13
- if (cursor === '"') {
14
- acc += '"'
15
- ++i
16
- while (source[i] !== '"') {
17
- if (source[i] === '\\') acc += escape(source[++i])
18
- else acc += source[i]
19
- ++i
20
- }
21
- }
22
12
  if (cursor === '(') {
23
13
  head.push([])
24
14
  stack.push(head)
@@ -28,8 +18,6 @@ export const LISP = {
28
18
  acc = ''
29
19
  if (token) {
30
20
  if (!head.length) head.push(leaf(APPLY, token))
31
- else if (token.match(/^"([^"]*)"/))
32
- head.push(leaf(ATOM, token.substring(1, token.length - 1)))
33
21
  else if (token.match(/^-?[0-9]\d*(\.\d+)?$/))
34
22
  head.push(leaf(ATOM, Number(token)))
35
23
  else head.push(leaf(WORD, token))
@@ -50,7 +38,6 @@ export const LISP = {
50
38
  return `(array ${array
51
39
  .map(([key, value]) => `("${key}" ${LISP.stringify(value)})`)
52
40
  .join(' ')})`
53
- else if (typeof array === 'string') return `"${array}"`
54
41
  else if (typeof array === 'function') return '()'
55
42
  else if (typeof array === 'boolean') return +array
56
43
  else return array
@@ -65,10 +52,7 @@ export const LISP = {
65
52
  out += first[VALUE]
66
53
  break
67
54
  case ATOM:
68
- out +=
69
- typeof first[VALUE] === 'string'
70
- ? `"${preserveEscape(first[VALUE])}"`
71
- : first[VALUE]
55
+ out += first[VALUE]
72
56
  break
73
57
  case APPLY:
74
58
  out += `(${first[VALUE]} ${rest.map(dfs).join(' ')})`
@@ -117,6 +101,6 @@ export const AST = {
117
101
  typeof ast === 'object'
118
102
  ? `[${ast.map(AST.stringify).join(',')}]`
119
103
  : typeof ast === 'string'
120
- ? `"${preserveEscape(ast)}"`
104
+ ? `"${ast}"`
121
105
  : ast
122
106
  }
package/src/utils.js CHANGED
@@ -5,16 +5,29 @@ import { run } from './evaluator.js'
5
5
  import { AST, isLeaf, LISP } from './parser.js'
6
6
  export const logError = (error) => console.log('\x1b[31m', error, '\x1b[0m')
7
7
  export const logSuccess = (output) => console.log(output, '\x1b[0m')
8
+ export const replaceStrings = (source) => {
9
+ const quotes = source.match(/"(.*?)"/g)
10
+ if (quotes)
11
+ for (const q of quotes)
12
+ source = source.replaceAll(
13
+ q,
14
+ `(string ${[...q]
15
+ .slice(1, -1)
16
+ .map((x) => x.charCodeAt(0))
17
+ .join(' ')})`
18
+ )
19
+ return source
20
+ }
8
21
  export const removeNoCode = (source) =>
9
22
  source
10
- // .replace(/;.+/g, '')
11
- .replace(/;(?=(?:(?:[^"]*"){2})*[^"]*$).+/g, '')
12
- .replace(/[\s\s]+(?=[^"]*(?:"[^"]*"[^"]*)*$)/g, ' ')
23
+ .replace(/;.+/g, '')
24
+ .replace(/[\s\s]/g, ' ')
13
25
  .trim()
26
+
14
27
  export const isBalancedParenthesis = (sourceCode) => {
15
28
  let count = 0
16
29
  const stack = []
17
- const str = sourceCode.match(/[/\(|\)](?=[^"]*(?:"[^"]*"[^"]*)*$)/g) ?? []
30
+ const str = sourceCode.match(/[/\(|\)]/g) ?? []
18
31
  for (let i = 0; i < str.length; ++i)
19
32
  if (str[i] === '(') stack.push(str[i])
20
33
  else if (str[i] === ')') if (stack.pop() !== '(') ++count
@@ -38,15 +51,6 @@ export const escape = (Char) => {
38
51
  return ''
39
52
  }
40
53
  }
41
- const escapeChars = {
42
- '\n': '\\n',
43
- '\r': '\\r',
44
- '\t': '\\t',
45
- s: '\\s',
46
- '"': '\\"'
47
- }
48
- export const preserveEscape = (str) =>
49
- str.replace(/[\n\r\t\s\"]/g, (match) => escapeChars[match] || match)
50
54
  export const stringifyType = (type) =>
51
55
  !isLeaf(type)
52
56
  ? `(array ${type.map((t) => stringifyType(t)).join(' ')})`
@@ -156,9 +160,10 @@ export const dfs = (tree, callback) => {
156
160
  }
157
161
  export const deepClone = (ast) => AST.parse(AST.stringify(ast))
158
162
  export const fez = (source, options = {}) => {
159
- const env = options.env ?? {}
163
+ const env = Object.create(null)
160
164
  try {
161
165
  if (typeof source === 'string') {
166
+ if (options.strings) source = replaceStrings(source)
162
167
  let code
163
168
  if (!options.compile)
164
169
  code = handleUnbalancedQuotes(
@@ -172,7 +177,7 @@ export const fez = (source, options = {}) => {
172
177
  throw new Error(
173
178
  'Top level expressions need to be wrapped in a (do) block'
174
179
  )
175
- const ast = [...(options.std ? treeShake(parsed, std) : []), ...parsed]
180
+ const ast = [...treeShake(parsed, std), ...parsed]
176
181
  if (options.compile) {
177
182
  const js = Object.values(comp(deepClone(ast))).join('')
178
183
  return options.eval ? eval(js) : js
@@ -199,51 +204,6 @@ export const fez = (source, options = {}) => {
199
204
  return err
200
205
  }
201
206
  }
202
-
203
- export const earMuffsToLodashes = (name) => name.replace(new RegExp(/\*/g), '_')
204
- export const dotNamesToEmpty = (name) => name.replace(new RegExp(/\./g), '')
205
- export const colonNamesTo$ = (name) => name.replace(new RegExp(/\:/g), '$')
206
- export const commaToLodash = (name) => name.replace(new RegExp(/\,/g), '_')
207
- export const arrowFromTo = (name) => name.replace(new RegExp(/->/g), '-to-')
208
- export const moduleNameToLodashes = (name) =>
209
- name.replace(new RegExp(/:/g), '_')
210
- export const questionMarkToLodash = (name) =>
211
- name.replace(new RegExp(/\?/g), 'Predicate')
212
- export const exclamationMarkMarkToLodash = (name) =>
213
- name.replace(new RegExp(/\!/g), 'Effect')
214
- export const toCamelCase = (name) => {
215
- let out = name[0]
216
- for (let i = 1; i < name.length; ++i) {
217
- const current = name[i],
218
- prev = name[i - 1]
219
- if (current === '-') continue
220
- else if (prev === '-') out += current.toUpperCase()
221
- else out += current
222
- }
223
- return out
224
- }
225
- export const keywordToHelper = (name) => {
226
- switch (name) {
227
- case KEYWORDS.ADDITION:
228
- return '__add'
229
- case KEYWORDS.MULTIPLICATION:
230
- return '__mult'
231
- case KEYWORDS.SUBTRACTION:
232
- return '__sub'
233
- case KEYWORDS.GREATHER_THAN:
234
- return '__gt'
235
- case KEYWORDS.EQUAL:
236
- return '__eq'
237
- case KEYWORDS.GREATHER_THAN_OR_EQUAL:
238
- return '__gteq'
239
- case KEYWORDS.LESS_THAN:
240
- return '__lt'
241
- case KEYWORDS.LESS_THAN_OR_EQUAL:
242
- return '__lteq'
243
- default:
244
- return name
245
- }
246
- }
247
207
  export const deepRename = (name, newName, tree) => {
248
208
  if (!isLeaf(tree))
249
209
  for (const leaf of tree) {
@@ -291,19 +251,3 @@ export const tree = (source, std) =>
291
251
  std
292
252
  ? shake(LISP.parse(removeNoCode(source)), std)
293
253
  : LISP.parse(removeNoCode(source))
294
- export const lispToJavaScriptVariableName = (name) =>
295
- toCamelCase(
296
- arrowFromTo(
297
- dotNamesToEmpty(
298
- colonNamesTo$(
299
- exclamationMarkMarkToLodash(
300
- questionMarkToLodash(
301
- commaToLodash(
302
- moduleNameToLodashes(earMuffsToLodashes(keywordToHelper(name)))
303
- )
304
- )
305
- )
306
- )
307
- )
308
- )
309
- )