fez-lisp 1.1.10 → 1.1.12

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.1.10",
5
+ "version": "1.1.12",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "keywords": [
package/src/compiler.js CHANGED
@@ -66,7 +66,6 @@ const lispToJavaScriptVariableName = (name) =>
66
66
  )
67
67
  )
68
68
  const Helpers = {
69
- __string: `__string=(...args)=>{const str=args.flat();str.isString=true;return str}`,
70
69
  __add: `__add=(...numbers)=>{return numbers.reduce((a,b)=>a+b,0)}`,
71
70
  __sub: `__sub=(...numbers)=>{return numbers.reduce((a,b)=>a-b,0)}`,
72
71
  __mult: `__mult=(...numbers)=>{return numbers.reduce((a,b)=>a*b,1)}`,
@@ -81,20 +80,21 @@ const Helpers = {
81
80
  for(let i=0; i<args.length-1;++i){
82
81
  circuit=args[i]
83
82
  if(circuit) continue
84
- else return circuit
83
+ else return 0
85
84
  }
86
- return args.at(-1)
85
+ return args.at(-1) ? 1 : 0
87
86
  }`,
88
87
  or: `or=(...args)=>{
89
88
  let circuit;
90
89
  for(let i=0;i<args.length-1;++i) {
91
90
  circuit = args[i]
92
- if(circuit)return circuit
91
+ if(circuit) return 1
93
92
  else continue
94
93
  }
95
- return args.at(-1)
94
+ return args.at(-1) ? 1 : 0
96
95
  }`,
97
96
  logEffect: `logEffect=(msg)=>{console.log(msg);return msg}`,
97
+ logStringEffect: `logStringEffect=(msg)=>{console.log(msg.map(x=>String.fromCharCode(x)).join(''));return msg}`,
98
98
  clearEffect: `clearEffect=()=>{console.clear();return 0}`,
99
99
  array_cons: `array_cons=(A, ...B)=> B.reduce((a, b) => a.concat(b), A)`,
100
100
  car: 'car=(arr)=>arr.at(0)',
@@ -105,7 +105,6 @@ const Helpers = {
105
105
  numberPredicate: `numberPredicate=(number)=>+(typeof number==='number')`,
106
106
  lambdaPredicate: `lambdaPredicate=(lambda)=>+(typeof lambda==='function')`,
107
107
  arrayPredicate: `arrayPredicate=(array)=>+Array.isArray(array)`,
108
- error: `error=(error)=>{throw new Error(error)}`,
109
108
  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}`
110
109
  }
111
110
  const semiColumnEdgeCases = new Set([
@@ -260,9 +259,9 @@ const compile = (tree, Drill) => {
260
259
  )
261
260
  }
262
261
  case KEYWORDS.AND:
263
- return `(${parseArgs(Arguments, Drill, '&&')});`
262
+ return `((${parseArgs(Arguments, Drill, '&&')}) ? 1 : 0);`
264
263
  case KEYWORDS.OR:
265
- return `((${parseArgs(Arguments, Drill, '||')}) || 0);`
264
+ return `((${parseArgs(Arguments, Drill, '||')}) ? 1 : 0);`
266
265
  case KEYWORDS.EQUAL:
267
266
  return `+(${parseArgs(Arguments, Drill, '===')});`
268
267
  case KEYWORDS.GREATHER_THAN_OR_EQUAL:
@@ -197,11 +197,6 @@ const keywords = {
197
197
  if (evaluate(args[i], env)) return evaluate(args[i + 1], env)
198
198
  return 0
199
199
  },
200
- [KEYWORDS.STRING_TYPE]: (args, env) => {
201
- const str = args.flatMap((x) => evaluate(x, env))
202
- str.isString = true
203
- return str
204
- },
205
200
  [KEYWORDS.ARRAY_TYPE]: (args, env) => {
206
201
  if (!args.length) return []
207
202
  const isCapacity =
@@ -499,9 +494,9 @@ const keywords = {
499
494
  for (let i = 0; i < args.length - 1; ++i) {
500
495
  circuit = evaluate(args[i], env)
501
496
  if (circuit) continue
502
- else return circuit
497
+ else return 0
503
498
  }
504
- return evaluate(args.at(-1), env)
499
+ return evaluate(args.at(-1), env) ? 1 : 0
505
500
  },
506
501
  [KEYWORDS.OR]: (args, env) => {
507
502
  if (args.length < 2)
@@ -513,10 +508,10 @@ const keywords = {
513
508
  let circuit
514
509
  for (let i = 0; i < args.length - 1; ++i) {
515
510
  circuit = evaluate(args[i], env)
516
- if (circuit) return circuit
511
+ if (circuit) return 1
517
512
  else continue
518
513
  }
519
- return evaluate(args.at(-1), env)
514
+ return evaluate(args.at(-1), env) ? 1 : 0
520
515
  },
521
516
  [KEYWORDS.CALL_FUNCTION]: (args, env) => {
522
517
  if (!args.length)
@@ -906,15 +901,26 @@ const keywords = {
906
901
  return array
907
902
  },
908
903
  [KEYWORDS.LOG]: (args, env) => {
909
- if (!args.length)
904
+ if (args.length !== 1)
910
905
  throw new RangeError(
911
- `Invalid number of arguments to (${KEYWORDS.LOG}) (>= 1 required) (${
906
+ `Invalid number of arguments to (${KEYWORDS.LOG}) (= 1 required) (${
912
907
  KEYWORDS.LOG
913
908
  } ${stringifyArgs(args)})`
914
909
  )
915
- const expressions = args.map((x) => evaluate(x, env))
916
- console.log(...expressions)
917
- return expressions.at(-1)
910
+ const expression = evaluate(args[0], env)
911
+ console.log(expression)
912
+ return expression
913
+ },
914
+ [KEYWORDS.LOG_STRING]: (args, env) => {
915
+ if (args.length !== 1)
916
+ throw new RangeError(
917
+ `Invalid number of arguments to (${
918
+ KEYWORDS.LOG_STRING
919
+ }) (= 1 required) (${KEYWORDS.LOG_STRING} ${stringifyArgs(args)})`
920
+ )
921
+ const expression = evaluate(args[0], env)
922
+ console.log(expression.map((x) => String.fromCharCode(x)).join(''))
923
+ return expression
918
924
  },
919
925
  [KEYWORDS.CLEAR_CONSOLE]: (args) => {
920
926
  if (args.length)
package/src/keywords.js CHANGED
@@ -6,7 +6,6 @@ export const ATOM = 2
6
6
  export const PLACEHOLDER = '.'
7
7
  export const KEYWORDS = {
8
8
  NUMBER_TYPE: 'number',
9
- STRING_TYPE: 'string',
10
9
  ARRAY_TYPE: 'array',
11
10
  ARRAY_LENGTH: 'length',
12
11
  IS_ARRAY: 'array?',
@@ -50,6 +49,7 @@ export const KEYWORDS = {
50
49
  TEST_CASE: 'case',
51
50
  TEST_BED: 'assert',
52
51
  LOG: 'log!',
52
+ LOG_STRING: 'log-string!',
53
53
  CLEAR_CONSOLE: 'clear!',
54
54
  SET_ARRAY: 'array:set!',
55
55
  DOC: 'fez-manual'
package/src/utils.js CHANGED
@@ -1,7 +1,5 @@
1
1
  import std from '../lib/baked/std.js'
2
- import { comp as JavaScript } from './compiler.js'
3
- import { comp as OCaml } from './ocaml.js'
4
-
2
+ import { comp } from './compiler.js'
5
3
  import { APPLY, ATOM, KEYWORDS, TYPE, VALUE, WORD } from './keywords.js'
6
4
  import { run } from './evaluator.js'
7
5
  import { AST, isLeaf, LISP } from './parser.js'
@@ -14,7 +12,7 @@ export const replaceStrings = (source) => {
14
12
  for (const q of quotes)
15
13
  source = source.replaceAll(
16
14
  q,
17
- `(string ${[...q]
15
+ `(array ${[...q]
18
16
  .slice(1, -1)
19
17
  .map((x) => x.charCodeAt(0))
20
18
  .join(' ')})`
@@ -136,7 +134,7 @@ export const handleUnbalancedQuotes = (source) => {
136
134
  }
137
135
  export const removeMutation = (source) => source.replace(new RegExp(/!/g), 'ǃ')
138
136
  export const treeShake = (ast, libs) => {
139
- const deps = libs.reduce((a, x) => a.add(x.at(1)[VALUE]), new Set())
137
+ const deps = libs.reduce((a, x) => a.set(x.at(1)[VALUE], x), new Map())
140
138
  const visited = new Set()
141
139
  const dfs = (tree) => {
142
140
  if (!isLeaf(tree)) tree.forEach(dfs)
@@ -147,13 +145,14 @@ export const treeShake = (ast, libs) => {
147
145
  ) {
148
146
  visited.add(tree[VALUE])
149
147
  // Recursively explore the dependencies of the current node
150
- const dependency = libs.find((x) => x.at(1)[VALUE] === tree[VALUE])
148
+ const dependency = deps.get(tree[VALUE])
151
149
  if (dependency) dfs(dependency.at(-1))
152
150
  }
153
151
  }
154
152
  dfs(ast)
155
153
  // Filter out libraries that are not in the visited set
156
- return libs.filter((x) => visited.has(x.at(1)[VALUE]))
154
+ // return libs.filter((x) => visited.has(x.at(1)[VALUE]))
155
+ return [...visited].reverse().map((x) => deps.get(x))
157
156
  }
158
157
  export const dfs = (tree, callback) => {
159
158
  if (!isLeaf(tree)) for (const leaf of tree) dfs(leaf)
@@ -180,16 +179,8 @@ export const fez = (source, options = {}) => {
180
179
  )
181
180
  const ast = [...treeShake(parsed, std), ...parsed]
182
181
  if (options.compile) {
183
- switch (options.compile) {
184
- case 1: {
185
- const js = Object.values(JavaScript(deepClone(ast))).join('')
186
- return options.eval ? eval(js) : js
187
- }
188
- case 2: {
189
- const oCaml = Object.values(OCaml(deepClone(ast))).join('')
190
- return oCaml
191
- }
192
- }
182
+ const js = Object.values(comp(deepClone(ast))).join('')
183
+ return options.eval ? eval(js) : js
193
184
  }
194
185
  return run(ast, env)
195
186
  } else if (Array.isArray(source)) {
@@ -197,16 +188,8 @@ export const fez = (source, options = {}) => {
197
188
  ? AST.parse(AST.stringify(source).replace(new RegExp(/!/g), 'ǃ'))
198
189
  : source
199
190
  if (options.compile) {
200
- switch (options.compile) {
201
- case 1: {
202
- const js = Object.values(JavaScript(deepClone(ast))).join('')
203
- return options.eval ? eval(js) : js
204
- }
205
- case 2: {
206
- const OCaml = Object.values(OCaml(deepClone(ast))).join('')
207
- return OCaml
208
- }
209
- }
191
+ const js = Object.values(comp(deepClone(ast))).join('')
192
+ return options.eval ? eval(js) : js
210
193
  }
211
194
  return run(ast, env)
212
195
  } else {
package/src/ocaml.js DELETED
@@ -1,373 +0,0 @@
1
- import {
2
- APPLY,
3
- ATOM,
4
- PLACEHOLDER,
5
- KEYWORDS,
6
- TYPE,
7
- VALUE,
8
- WORD
9
- } from './keywords.js'
10
- import { leaf, isLeaf } from './parser.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
- )
68
- const Helpers = {
69
- // __string: `__string=(...args)=>{const str=args.flat();str.isString=true;return str}`,
70
- // __add: `__add=(...numbers)=>{return numbers.reduce((a,b)=>a+b,0)}`,
71
- // __sub: `__sub=(...numbers)=>{return numbers.reduce((a,b)=>a-b,0)}`,
72
- // __mult: `__mult=(...numbers)=>{return numbers.reduce((a,b)=>a*b,1)}`,
73
- // __gteq: '__gteq=(a,b)=>+(a>=b)',
74
- // __gt: '__gt=(a,b)=>+(a>b)',
75
- // __eq: '__eq=(a,b)=>+(a===b)',
76
- // __lteq: '__lteq=(a,b)=>+(a<=b)',
77
- // __lt: '__lt=(a,b)=>+(a<b)',
78
- // not: 'not=(a)=>+!a',
79
- // and: `and=(...args)=>{
80
- // let circuit;
81
- // for(let i=0; i<args.length-1;++i){
82
- // circuit=args[i]
83
- // if(circuit) continue
84
- // else return circuit
85
- // }
86
- // return args.at(-1)
87
- // }`,
88
- // or: `or=(...args)=>{
89
- // let circuit;
90
- // for(let i=0;i<args.length-1;++i) {
91
- // circuit = args[i]
92
- // if(circuit)return circuit
93
- // else continue
94
- // }
95
- // return args.at(-1)
96
- // }`,
97
- // logEffect: `logEffect=(msg)=>{console.log(msg);return msg}`,
98
- // clearEffect: `clearEffect=()=>{console.clear();return 0}`,
99
- array_cons: `rec array_cons lists =
100
- match lists with
101
- | [] -> []
102
- | hd::tl -> hd @ array_cons tl`,
103
- car: 'car = fun (arr::_) -> arr',
104
- cdr: 'cdr = fun (_::arr) -> arr',
105
- array_get: `rec array_get = function
106
- | [], _ -> raise (Failure "array_get")
107
- | list, n when n < 0 -> array_get(list, List.length(list) - n)
108
- | x::_, 0 -> x
109
- | x::xs, n -> array_get(xs, n - 1)`,
110
- length: 'length(arr)=float_of_int(List.length(arr))'
111
- // numberPredicate: `numberPredicate=(number)=>+(typeof number==='number')`,
112
- // lambdaPredicate: `lambdaPredicate=(lambda)=>+(typeof lambda==='function')`,
113
- // arrayPredicate: `arrayPredicate=(array)=>+Array.isArray(array)`,
114
- // error: `error=(error)=>{throw new Error(error)}`,
115
- // 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}`
116
- // 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}`
117
- }
118
- const semiColumnEdgeCases = new Set([
119
- ';)',
120
- ';-',
121
- ';+',
122
- ';*',
123
- ';%',
124
- ';&',
125
- ';/',
126
- ';:',
127
- ';.',
128
- ';=',
129
- ';<',
130
- ';>',
131
- ';|',
132
- ';,',
133
- ';?',
134
- ',,',
135
- // ';;',
136
- ';]'
137
- ])
138
- const parse = (Arguments, Drill) => Arguments.map((x) => compile(x, Drill))
139
- const parseArgs = (Arguments, Drill, separator = ',') =>
140
- parse(Arguments, Drill).join(separator)
141
- const compile = (tree, Drill) => {
142
- if (!tree) return ''
143
- const [first, ...Arguments] = !isLeaf(tree) ? tree : [tree]
144
- if (first == undefined) return '[];'
145
- const token = first[VALUE]
146
- if (first[TYPE] === APPLY) {
147
- switch (token) {
148
- case KEYWORDS.BLOCK: {
149
- if (Arguments.length > 1) {
150
- return `(${Arguments.map((x) =>
151
- (compile(x, Drill) ?? '').toString().trimStart().replace(';;', '')
152
- )
153
- .filter(Boolean)
154
- .join(' in\n')})`
155
- } else {
156
- const res = compile(Arguments[0], Drill)
157
- return res !== undefined ? res.toString().trim() : ''
158
- }
159
- }
160
- case KEYWORDS.CALL_FUNCTION: {
161
- const [first, ...rest] = Arguments
162
- const apply = compile(first, Drill)
163
- return `${
164
- apply[apply.length - 1] === ';'
165
- ? apply.substring(0, apply.length - 1)
166
- : apply
167
- }(${parseArgs(rest, Drill)})`
168
- }
169
- case KEYWORDS.DEFINE_VARIABLE: {
170
- let name,
171
- out = 'let '
172
- if (Arguments[0][TYPE] === WORD) {
173
- name = lispToJavaScriptVariableName(Arguments[0][VALUE])
174
- Drill.Variables.add(name)
175
- }
176
- out += `${name} = ${compile(Arguments[1], Drill)}`
177
- out += `;;`
178
- return out
179
- }
180
- case KEYWORDS.IS_NUMBER:
181
- Drill.Helpers.add('numberPredicate')
182
- return `numberPredicate(${compile(Arguments[0], Drill)});`
183
- case KEYWORDS.IS_FUNCTION:
184
- Drill.Helpers.add('lambdaPredicate')
185
- return `lambdaPredicate(${compile(Arguments[0], Drill)});`
186
- case KEYWORDS.IS_ARRAY:
187
- Drill.Helpers.add('arrayPredicate')
188
- return `arrayPredicate(${compile(Arguments[0], Drill)});`
189
- case KEYWORDS.NUMBER_TYPE:
190
- return '0'
191
- case KEYWORDS.BOOLEAN_TYPE:
192
- return '1'
193
- case KEYWORDS.STRING_TYPE:
194
- Drill.Helpers.add('__string')
195
- return `__string(${parseArgs(Arguments, Drill)});`
196
- case KEYWORDS.ARRAY_TYPE:
197
- return Arguments.length === 2 &&
198
- Arguments[1][TYPE] === WORD &&
199
- Arguments[1][VALUE] === 'length'
200
- ? `(List.make ${compile(Arguments[0], Drill)} 0)`
201
- : `[${parseArgs(Arguments, Drill, ';')}]`
202
- case KEYWORDS.ARRAY_LENGTH:
203
- Drill.Helpers.add('length')
204
- return `length(${compile(Arguments[0], Drill)})`
205
- case KEYWORDS.FIRST_ARRAY:
206
- Drill.Helpers.add('car')
207
- return `car(${compile(Arguments[0], Drill)})`
208
- case KEYWORDS.REST_ARRAY:
209
- Drill.Helpers.add('cdr')
210
- return `cdr(${compile(Arguments[0], Drill)})`
211
- case KEYWORDS.GET_ARRAY:
212
- Drill.Helpers.add('array_get')
213
- return `array_get(${compile(
214
- Arguments[0],
215
- Drill
216
- )}, int_of_float(${compile(Arguments[1], Drill)}))`
217
- case KEYWORDS.CONS:
218
- Drill.Helpers.add('array_cons')
219
- return `array_cons[${parseArgs(Arguments, Drill, ';')}]`
220
- case KEYWORDS.ANONYMOUS_FUNCTION: {
221
- const functionArgs = Arguments
222
- const body = Arguments.pop()
223
- const InnerDrills = { Variables: new Set(), Helpers: Drill.Helpers }
224
- const evaluatedBody = compile(body, InnerDrills)
225
- // const vars = InnerDrills.Variables.size
226
- // ? `let ${[...InnerDrills.Variables].join(',')};`
227
- // : ''
228
- return `(fun (${parseArgs(
229
- functionArgs.map((node, index) =>
230
- node[VALUE] === PLACEHOLDER
231
- ? leaf(node[TYPE], `_${index}`)
232
- : leaf(node[TYPE], node[VALUE])
233
- ),
234
- InnerDrills
235
- )}) -> ${evaluatedBody.toString().trimStart()})`
236
- }
237
- case KEYWORDS.TAIL_CALLS_OPTIMISED_RECURSIVE_FUNCTION: {
238
- const arg = Arguments[0]
239
- const val = Arguments[1]
240
- if (val[0][0] === APPLY && val[0][1] === KEYWORDS.ANONYMOUS_FUNCTION) {
241
- const name = lispToJavaScriptVariableName(arg[VALUE])
242
- const functionArgs = val.slice(1)
243
- const body = functionArgs.pop()
244
- const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
245
- const evaluatedBody = compile(body, FunctionDrill)
246
- return `let rec ${name} (${parseArgs(
247
- functionArgs.map((node, index) =>
248
- node[VALUE] === PLACEHOLDER
249
- ? leaf(node[TYPE], `_${index}`)
250
- : leaf(node[TYPE], node[VALUE])
251
- )
252
- )}) = ${evaluatedBody.toString().trimStart()};;`
253
- }
254
- }
255
- case KEYWORDS.AND:
256
- return `(${parseArgs(Arguments, Drill, '&&')})`
257
- case KEYWORDS.OR:
258
- return `((${parseArgs(Arguments, Drill, '||')}) || 0.0)`
259
- case KEYWORDS.EQUAL:
260
- return `(if ${parseArgs(Arguments, Drill, '==')} then 1.0 else 0.0)`
261
- case KEYWORDS.GREATHER_THAN_OR_EQUAL:
262
- case KEYWORDS.LESS_THAN_OR_EQUAL:
263
- case KEYWORDS.GREATHER_THAN:
264
- case KEYWORDS.LESS_THAN:
265
- return `(if ${parseArgs(Arguments, Drill, token)} then 1.0 else 0.0)`
266
- case KEYWORDS.SUBTRACTION:
267
- return Arguments.length === 1
268
- ? `(-.${compile(Arguments[0], Drill)})`
269
- : `(${parse(Arguments, Drill)
270
- // Add space so it doesn't consider it 2--1 but 2- -1
271
- .map((x) => (typeof x === 'number' && x < 0 ? ` ${x}` : x))
272
- .join(`${token}.`)})`
273
- case KEYWORDS.MULTIPLICATION:
274
- return Arguments.length
275
- ? `(${parseArgs(Arguments, Drill, `${token}.`)})`
276
- : `(1.0)`
277
- case KEYWORDS.DIVISION:
278
- return Arguments.length
279
- ? Arguments.length === 1
280
- ? `(1.0/.${compile(Arguments[0], Drill)})`
281
- : `(${parseArgs(Arguments, Drill, `${token}.`)})`
282
- : `(0.0)`
283
- case KEYWORDS.ADDITION:
284
- return Arguments.length
285
- ? `(${parseArgs(Arguments, Drill, `${token}.`)})`
286
- : `(0.0)`
287
- case KEYWORDS.BITWISE_AND:
288
- case KEYWORDS.BITWISE_OR:
289
- case KEYWORDS.BITWISE_XOR:
290
- case KEYWORDS.BITWISE_LEFT_SHIFT:
291
- case KEYWORDS.BITWISE_RIGHT_SHIFT:
292
- case KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT:
293
- return `(${parseArgs(Arguments, Drill, token)})`
294
- case KEYWORDS.REMAINDER_OF_DIVISION:
295
- return `(mod_float ${compile(Arguments[0], Drill)} ${compile(
296
- Arguments[1],
297
- Drill
298
- )})`
299
- case KEYWORDS.BIT_TYPE:
300
- return `(${compile(Arguments[0], Drill)}>>>0).toString(2)`
301
- case KEYWORDS.BITWISE_NOT:
302
- return `~(${compile(Arguments[0], Drill)})`
303
- case KEYWORDS.NOT:
304
- return `(if ${compile(Arguments[0], Drill)} == 0.0 then 1.0 else 0.0)`
305
- case KEYWORDS.IF: {
306
- return `(if ${compile(Arguments[0], Drill)} != 0.0 then ${compile(
307
- Arguments[1],
308
- Drill
309
- )} else ${Arguments.length === 3 ? compile(Arguments[2], Drill) : 0})`
310
- }
311
- case KEYWORDS.CONDITION: {
312
- let out = ''
313
- for (let i = 0; i < Arguments.length; i += 2)
314
- out += `(if ${compile(Arguments[i], Drill)} != 0.0 then ${compile(
315
- Arguments[i + 1],
316
- Drill
317
- )} else `
318
- out += '0.0)'
319
- return out
320
- }
321
- // case KEYWORDS.PIPE: {
322
- // let inp = Arguments[0]
323
- // for (let i = 1; i < Arguments.length; ++i)
324
- // inp = [Arguments[i].shift(), inp, ...Arguments[i]]
325
- // return compile(inp, Drill)
326
- // }
327
- case KEYWORDS.IMMUTABLE_FUNCTION: {
328
- const [first, ...rest] = Arguments
329
- return compile([leaf(APPLY, first[VALUE]), ...rest], Drill)
330
- }
331
- case KEYWORDS.NOT_COMPILED_BLOCK:
332
- case KEYWORDS.TEST_CASE:
333
- case KEYWORDS.TEST_BED:
334
- case KEYWORDS.DOC:
335
- return ''
336
- default: {
337
- const camelCased = lispToJavaScriptVariableName(token)
338
- if (camelCased in Helpers) Drill.Helpers.add(camelCased)
339
- return `${camelCased}(${parseArgs(Arguments, Drill)})`
340
- }
341
- }
342
- } else if (first[TYPE] === ATOM)
343
- return Number.isInteger(first[TYPE])
344
- ? `${first[VALUE]}.0`
345
- : first[VALUE].toString()
346
- else if (first[TYPE] === WORD) {
347
- const camelCased = lispToJavaScriptVariableName(token)
348
- if (camelCased in Helpers) Drill.Helpers.add(camelCased)
349
- return camelCased
350
- }
351
- }
352
- const HelpersEntries = new Map(Object.entries(Helpers))
353
- export const comp = (ast) => {
354
- const Drill = { Variables: new Set(), Helpers: new Set() }
355
- const raw = ast
356
- .map((tree) => compile(tree, Drill))
357
- .filter(Boolean)
358
- .join('\n')
359
- let program = ''
360
- for (let i = 0; i < raw.length; ++i) {
361
- const current = raw[i]
362
- const next = raw[i + 1]
363
- if (!semiColumnEdgeCases.has(current + next)) program += current
364
- }
365
- const help = Drill.Helpers.size
366
- ? `${[...Drill.Helpers.keys()]
367
- .map((x) => `let ${HelpersEntries.get(x)};;`)
368
- .join('\n')}\n`
369
- : ''
370
-
371
- const top = `${help}`
372
- return { top, program }
373
- }