fez-lisp 1.1.10 → 1.1.12

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.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
- }