fez-lisp 1.2.62 → 1.3.0

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/src/compiler.js CHANGED
@@ -1,350 +1,350 @@
1
- import {
2
- APPLY,
3
- ATOM,
4
- PLACEHOLDER,
5
- KEYWORDS,
6
- TYPE,
7
- VALUE,
8
- WORD,
9
- SUGGAR
10
- } from './keywords.js'
11
- import { leaf, isLeaf, AST } from './parser.js'
12
- const deepRename = (name, newName, tree) => {
13
- if (!isLeaf(tree))
14
- for (const leaf of tree) {
15
- // Figure out a non mutable solution so
16
- // I can get rid of deep clone AST.parse(AST.stringify(ast))
17
- if (leaf[VALUE] === name) leaf[VALUE] = newName
18
- deepRename(name, newName, leaf)
19
- }
20
- }
21
- const earMuffsToLodashes = (name) => name.replace(new RegExp(/\*/g), '_')
22
- const dotNamesToEmpty = (name) => name.replace(new RegExp(/\./g), '')
23
- const arrowFromTo = (name) => name.replace(new RegExp(/->/g), '-to-')
24
- const moduleNameToLodashes = (name) => name.replace(new RegExp(/:/g), '_')
25
- const questionMarkToPredicate = (name) =>
26
- name.replace(new RegExp(/\?/g), '_predicate')
27
- const exclamationMarkMarkToEffect = (name) =>
28
- name.replace(new RegExp(/\!/g), '_effect')
29
- const toCamelCase = (name) => {
30
- let out = name[0]
31
- for (let i = 1; i < name.length; ++i) {
32
- const current = name[i],
33
- prev = name[i - 1]
34
- if (current === '-') continue
35
- else if (prev === '-') out += current.toUpperCase()
36
- else out += current
37
- }
38
- return out
39
- }
40
- const dashToLodashes = (name) => name.replace(new RegExp(/-/g), '_')
41
- const keywordToHelper = (name) => {
42
- switch (name) {
43
- case KEYWORDS.ADDITION:
44
- return '__add'
45
- case KEYWORDS.MULTIPLICATION:
46
- return '__mult'
47
- case KEYWORDS.DIVISION:
48
- return '__div'
49
- case KEYWORDS.SUBTRACTION:
50
- return '__sub'
51
- case KEYWORDS.GREATHER_THAN:
52
- return '__gt'
53
- case KEYWORDS.EQUAL:
54
- return '__eq'
55
- case KEYWORDS.GREATHER_THAN_OR_EQUAL:
56
- return '__gteq'
57
- case KEYWORDS.LESS_THAN:
58
- return '__lt'
59
- case KEYWORDS.LESS_THAN_OR_EQUAL:
60
- return '__lteq'
61
- default:
62
- return name
63
- }
64
- }
65
- const lispToJavaScriptVariableName = (name) =>
66
- dashToLodashes(
67
- arrowFromTo(
68
- dotNamesToEmpty(
69
- exclamationMarkMarkToEffect(
70
- questionMarkToPredicate(
71
- moduleNameToLodashes(earMuffsToLodashes(keywordToHelper(name)))
72
- )
73
- )
74
- )
75
- )
76
- )
77
- const Helpers = {
78
- __add: `__add=(...numbers)=>{return numbers.reduce((a,b)=>a+b,0)}`,
79
- __sub: `__sub=(...numbers)=>{return numbers.length===1?-numbers[0]:numbers.reduce((a,b)=>a-b,0)}`,
80
- __mult: `__mult=(...numbers)=>{return numbers.reduce((a,b)=>a*b,1)}`,
81
- __div: `__div=(...numbers)=>{return numbers.length===1?1/numbers[0]:numbers.reduce((a, b)=>a/b)}`,
82
- __gteq: '__gteq=(a,b)=>+(a>=b)',
83
- __gt: '__gt=(a,b)=>+(a>b)',
84
- __eq: '__eq=(a,b)=>+(a===b)',
85
- __lteq: '__lteq=(a,b)=>+(a<=b)',
86
- __lt: '__lt=(a,b)=>+(a<b)',
87
- array: 'array=(...args)=>args',
88
- not: 'not=(a)=>+!a',
89
- and: `and=(...args)=>{
90
- let circuit;
91
- for(let i=0; i<args.length-1;++i){
92
- circuit=args[i]
93
- if(circuit) continue
94
- else return 0
95
- }
96
- return args.at(-1) ? 1 : 0
97
- }`,
98
- or: `or=(...args)=>{
99
- let circuit;
100
- for(let i=0;i<args.length-1;++i) {
101
- circuit = args[i]
102
- if(circuit) return 1
103
- else continue
104
- }
105
- return args.at(-1) ? 1 : 0
106
- }`,
107
- log_effect: `log_effect=(msg)=>{console.log(msg);return msg}`,
108
- log_char_effect: `log_char_effect=(msg)=>{console.log(String.fromCharCode(msg));return msg}`,
109
- log_string_effect: `log_string_effect=(msg)=>{console.log(msg.map(x=>String.fromCharCode(x)).join(''));return msg}`,
110
- clear_effect: `clear_effect=()=>{console.clear();return 0}`,
111
- get: 'get=(arr,i)=>arr.at(i)',
112
- length: 'length=(arr)=>arr.length',
113
- __tco: `__tco=fn=>(...args)=>{let result=fn(...args);while(typeof result==='function')result=result();return result}`,
114
- atom_predicate: `atom_predicate=(number)=>+(typeof number==='number')`,
115
- lambda_predicate: `lambda_predicate=(fm)=>+(typeof fn==='function')`,
116
- set_effect: `set_effect=function(array,index,value){if(arguments.length===1){array.pop()}else{array[index] = value};return array}`,
117
- __error: `__error=(error)=>{throw new Error(error.map((x)=>String.fromCharCode(x)).join(''))}`
118
- }
119
- const semiColumnEdgeCases = new Set([
120
- ';)',
121
- ';-',
122
- ';+',
123
- ';*',
124
- ';%',
125
- ';&',
126
- ';/',
127
- ';:',
128
- ';.',
129
- ';=',
130
- ';<',
131
- ';>',
132
- ';|',
133
- ';,',
134
- ';?',
135
- ',,',
136
- ';;',
137
- ';]',
138
- ';^'
139
- ])
140
- const parse = (Arguments, Drill) => Arguments.map((x) => compile(x, Drill))
141
- const parseArgs = (Arguments, Drill, separator = ',') =>
142
- parse(Arguments, Drill).join(separator)
143
- const compile = (tree, Drill) => {
144
- if (!tree) return ''
145
- const [first, ...Arguments] = !isLeaf(tree) ? tree : [tree]
146
- if (first == undefined) return '[];'
147
- const token = first[VALUE]
148
- if (first[TYPE] === APPLY) {
149
- switch (token) {
150
- case KEYWORDS.BLOCK: {
151
- if (Arguments.length > 1) {
152
- return `(${Arguments.map((x) =>
153
- (compile(x, Drill) ?? '').toString().trim()
154
- )
155
- .filter((x) => x !== undefined)
156
- .join(',')});`
157
- } else {
158
- const res = compile(Arguments[0], Drill)
159
- return res !== undefined ? res.toString().trim() : ''
160
- }
161
- }
162
- case KEYWORDS.CALL_FUNCTION: {
163
- const [first, ...rest] = Arguments
164
- const apply = compile(first, Drill)
165
- return `${
166
- apply[apply.length - 1] === ';'
167
- ? apply.substring(0, apply.length - 1)
168
- : apply
169
- }(${parseArgs(rest, Drill)})`
170
- }
171
- case KEYWORDS.DEFINE_VARIABLE: {
172
- const n = Arguments[0][VALUE]
173
- const prefix = n.split(':')[0]
174
- if (prefix === SUGGAR.RECURSION) {
175
- const name = lispToJavaScriptVariableName(n)
176
- const newName = `${SUGGAR.RECURSION}_${performance
177
- .now()
178
- .toString()
179
- .replace('.', 7)}`
180
- Drill.Variables.add(name)
181
- Drill.Variables.add(newName)
182
- Drill.Helpers.add('__tco')
183
- const functionArgs = Arguments.at(-1).slice(1)
184
- const body = functionArgs.pop()
185
- const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
186
- deepRename(n, `()=>${newName}`, body)
187
- const evaluatedBody = compile(body, FunctionDrill)
188
- const vars = FunctionDrill.Variables.size
189
- ? `var ${[...FunctionDrill.Variables].join(',')};`
190
- : ''
191
- return `(${name}=(__tco(${newName}=(${parseArgs(
192
- functionArgs,
193
- Drill
194
- )})=>{${vars}return ${evaluatedBody
195
- .toString()
196
- .trim()}}, ${newName})));`
197
- } else if (prefix === SUGGAR.CACHE) {
198
- // memoization here
199
- const name = lispToJavaScriptVariableName(n)
200
- const newName = name.substring(SUGGAR.CACHE.length + 1)
201
- Drill.Variables.add(name)
202
- const functionArgs = Arguments.at(-1).slice(1)
203
- const body = functionArgs.pop()
204
- deepRename(n, newName, body)
205
- const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
206
- const evaluatedBody = compile(body, FunctionDrill)
207
- const vars = FunctionDrill.Variables.size
208
- ? `var ${[...FunctionDrill.Variables].join(',')};`
209
- : ''
210
- return `(${name}=function(){var __${newName}_map = new Map();var ${newName}=(function(${parseArgs(
211
- functionArgs,
212
- Drill
213
- )}){${vars};var __key=[...arguments].join(',');if(__${newName}_map.has(__key)){return __${newName}_map.get(__key)}else{var __res = ${evaluatedBody
214
- .toString()
215
- .trim()};__${newName}_map.set(__key, __res);return __res}});return ${newName}(...arguments)});`
216
- } else {
217
- const name = lispToJavaScriptVariableName(n)
218
- Drill.Variables.add(name)
219
- return `${name}=${compile(Arguments[1], Drill)};`
220
- }
221
- }
222
- case KEYWORDS.IS_ATOM:
223
- Drill.Helpers.add('atom_predicate')
224
- return `atom_predicate(${compile(Arguments[0], Drill)});`
225
- case KEYWORDS.IS_LAMBDA:
226
- Drill.Helpers.add('lambda_predicate')
227
- return `lambda_predicate(${compile(Arguments[0], Drill)});`
228
- case KEYWORDS.ARRAY_TYPE:
229
- return `[${parseArgs(Arguments, Drill)}];`
230
- case KEYWORDS.ARRAY_LENGTH:
231
- Drill.Helpers.add('length')
232
- return `length(${compile(Arguments[0], Drill)})`
233
- case KEYWORDS.GET_ARRAY:
234
- Drill.Helpers.add('get')
235
- return `get(${compile(Arguments[0], Drill)}, ${compile(
236
- Arguments[1],
237
- Drill
238
- )});`
239
- case KEYWORDS.ANONYMOUS_FUNCTION: {
240
- const functionArgs = Arguments
241
- const body = Arguments.pop()
242
- const InnerDrills = { Variables: new Set(), Helpers: Drill.Helpers }
243
- const evaluatedBody = compile(body, InnerDrills)
244
- const vars = InnerDrills.Variables.size
245
- ? `var ${[...InnerDrills.Variables].join(',')};`
246
- : ''
247
- const args = parseArgs(
248
- functionArgs.map((node, index) =>
249
- node[VALUE] === PLACEHOLDER
250
- ? leaf(node[TYPE], `_${index}`)
251
- : leaf(node[TYPE], node[VALUE])
252
- ),
253
- InnerDrills
254
- )
255
- // const $ = [${args}];
256
- return `((${args})=>{${vars}return ${evaluatedBody
257
- .toString()
258
- .trim()}});`
259
- }
260
- case KEYWORDS.AND:
261
- return `((${parseArgs(Arguments, Drill, '&&')}) ? 1 : 0);`
262
- case KEYWORDS.OR:
263
- return `((${parseArgs(Arguments, Drill, '||')}) ? 1 : 0);`
264
- case KEYWORDS.EQUAL:
265
- return `+(${parseArgs(Arguments, Drill, '===')});`
266
- case KEYWORDS.GREATHER_THAN_OR_EQUAL:
267
- case KEYWORDS.LESS_THAN_OR_EQUAL:
268
- case KEYWORDS.GREATHER_THAN:
269
- case KEYWORDS.LESS_THAN:
270
- return `+(${parseArgs(Arguments, Drill, token)});`
271
- case KEYWORDS.SUBTRACTION:
272
- return Arguments.length === 1
273
- ? `(-${compile(Arguments[0], Drill)});`
274
- : `(${parse(Arguments, Drill)
275
- // Add space so it doesn't consider it 2--1 but 2- -1
276
- .map((x) => (typeof x === 'number' && x < 0 ? ` ${x}` : x))
277
- .join(token)});`
278
- case KEYWORDS.MULTIPLICATION:
279
- return `(${parseArgs(Arguments, Drill, token)});`
280
- case KEYWORDS.DIVISION:
281
- return Arguments.length === 1
282
- ? `(1/${compile(Arguments[0], Drill)});`
283
- : `(${parseArgs(Arguments, Drill, token)});`
284
- case KEYWORDS.ADDITION:
285
- return `(${parseArgs(Arguments, Drill, token)});`
286
- case KEYWORDS.BITWISE_AND:
287
- case KEYWORDS.BITWISE_OR:
288
- case KEYWORDS.BITWISE_XOR:
289
- case KEYWORDS.BITWISE_LEFT_SHIFT:
290
- case KEYWORDS.BITWISE_RIGHT_SHIFT:
291
- case KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT:
292
- return `(${parseArgs(Arguments, Drill, token)});`
293
- case KEYWORDS.REMAINDER_OF_DIVISION:
294
- return `(${compile(Arguments[0], Drill)}%${compile(
295
- Arguments[1],
296
- Drill
297
- )});`
298
- case KEYWORDS.BIT_TYPE:
299
- return `(${compile(Arguments[0], Drill)}>>>0).toString(2)`
300
- case KEYWORDS.BITWISE_NOT:
301
- return `~(${compile(Arguments[0], Drill)})`
302
- case KEYWORDS.NOT:
303
- return `(+!${compile(Arguments[0], Drill)})`
304
- case KEYWORDS.IF: {
305
- return `(${compile(Arguments[0], Drill)}?${compile(
306
- Arguments[1],
307
- Drill
308
- )}:${Arguments.length === 3 ? compile(Arguments[2], Drill) : 0});`
309
- }
310
- case KEYWORDS.THROW: {
311
- Drill.Helpers.add('__error')
312
- return `__error(${compile(Arguments[0], Drill)})`
313
- }
314
- default: {
315
- const camelCased = lispToJavaScriptVariableName(token)
316
- if (camelCased in Helpers) Drill.Helpers.add(camelCased)
317
- return `${camelCased}(${parseArgs(Arguments, Drill)});`
318
- }
319
- }
320
- } else if (first[TYPE] === ATOM) return first[VALUE]
321
- else if (first[TYPE] === WORD) {
322
- const camelCased = lispToJavaScriptVariableName(token)
323
- if (camelCased in Helpers) Drill.Helpers.add(camelCased)
324
- return camelCased
325
- }
326
- }
327
- const HelpersEntries = new Map(Object.entries(Helpers))
328
- export const comp = (ast) => {
329
- const Drill = { Variables: new Set(), Helpers: new Set() }
330
- const raw = AST.parse(AST.stringify(ast)) // cloning for fn renames mutations
331
- .map((tree) => compile(tree, Drill))
332
- .filter((x) => x !== undefined)
333
- .join('\n')
334
- let program = ''
335
- for (let i = 0; i < raw.length; ++i) {
336
- const current = raw[i]
337
- const next = raw[i + 1]
338
- if (!semiColumnEdgeCases.has(current + next)) program += current
339
- }
340
- const help = Drill.Helpers.size
341
- ? `var ${[...Drill.Helpers.keys()]
342
- .map((x) => HelpersEntries.get(x))
343
- .join(',')};\n`
344
- : ''
345
- const vars = Drill.Variables.size
346
- ? `var ${[...Drill.Variables].join(',')};`
347
- : ''
348
- const top = `${help}${vars}`
349
- return `${top}${program}`
350
- }
1
+ import {
2
+ APPLY,
3
+ ATOM,
4
+ PLACEHOLDER,
5
+ KEYWORDS,
6
+ TYPE,
7
+ VALUE,
8
+ WORD,
9
+ SUGGAR
10
+ } from './keywords.js'
11
+ import { leaf, isLeaf, AST } from './parser.js'
12
+ const deepRename = (name, newName, tree) => {
13
+ if (!isLeaf(tree))
14
+ for (const leaf of tree) {
15
+ // Figure out a non mutable solution so
16
+ // I can get rid of deep clone AST.parse(AST.stringify(ast))
17
+ if (leaf[VALUE] === name) leaf[VALUE] = newName
18
+ deepRename(name, newName, leaf)
19
+ }
20
+ }
21
+ const earMuffsToLodashes = (name) => name.replace(new RegExp(/\*/g), '_')
22
+ const dotNamesToEmpty = (name) => name.replace(new RegExp(/\./g), '')
23
+ const arrowFromTo = (name) => name.replace(new RegExp(/->/g), '-to-')
24
+ const moduleNameToLodashes = (name) => name.replace(new RegExp(/:/g), '_')
25
+ const questionMarkToPredicate = (name) =>
26
+ name.replace(new RegExp(/\?/g), '_predicate')
27
+ const exclamationMarkMarkToEffect = (name) =>
28
+ name.replace(new RegExp(/\!/g), '_effect')
29
+ const toCamelCase = (name) => {
30
+ let out = name[0]
31
+ for (let i = 1; i < name.length; ++i) {
32
+ const current = name[i],
33
+ prev = name[i - 1]
34
+ if (current === '-') continue
35
+ else if (prev === '-') out += current.toUpperCase()
36
+ else out += current
37
+ }
38
+ return out
39
+ }
40
+ const dashToLodashes = (name) => name.replace(new RegExp(/-/g), '_')
41
+ const keywordToHelper = (name) => {
42
+ switch (name) {
43
+ case KEYWORDS.ADDITION:
44
+ return '__add'
45
+ case KEYWORDS.MULTIPLICATION:
46
+ return '__mult'
47
+ case KEYWORDS.DIVISION:
48
+ return '__div'
49
+ case KEYWORDS.SUBTRACTION:
50
+ return '__sub'
51
+ case KEYWORDS.GREATHER_THAN:
52
+ return '__gt'
53
+ case KEYWORDS.EQUAL:
54
+ return '__eq'
55
+ case KEYWORDS.GREATHER_THAN_OR_EQUAL:
56
+ return '__gteq'
57
+ case KEYWORDS.LESS_THAN:
58
+ return '__lt'
59
+ case KEYWORDS.LESS_THAN_OR_EQUAL:
60
+ return '__lteq'
61
+ default:
62
+ return name
63
+ }
64
+ }
65
+ const lispToJavaScriptVariableName = (name) =>
66
+ dashToLodashes(
67
+ arrowFromTo(
68
+ dotNamesToEmpty(
69
+ exclamationMarkMarkToEffect(
70
+ questionMarkToPredicate(
71
+ moduleNameToLodashes(earMuffsToLodashes(keywordToHelper(name)))
72
+ )
73
+ )
74
+ )
75
+ )
76
+ )
77
+ const Helpers = {
78
+ __add: `__add=(a,b)=>{return a+b}`,
79
+ __sub: `__sub=function(a,b){return arguments.length===1?-a:a-b}`,
80
+ __mult: `__mult=(a,b)=>{return a*b}`,
81
+ __div: `__div=function(a,b){return arguments.length===1?1/a:a/b}`,
82
+ __gteq: '__gteq=(a,b)=>+(a>=b)',
83
+ __gt: '__gt=(a,b)=>+(a>b)',
84
+ __eq: '__eq=(a,b)=>+(a===b)',
85
+ __lteq: '__lteq=(a,b)=>+(a<=b)',
86
+ __lt: '__lt=(a,b)=>+(a<b)',
87
+ array: 'array=(...args)=>args',
88
+ not: 'not=(a)=>+!a',
89
+ and: `and=(...args)=>{
90
+ let circuit;
91
+ for(let i=0; i<args.length-1;++i){
92
+ circuit=args[i]
93
+ if(circuit) continue
94
+ else return 0
95
+ }
96
+ return args.at(-1) ? 1 : 0
97
+ }`,
98
+ or: `or=(...args)=>{
99
+ let circuit;
100
+ for(let i=0;i<args.length-1;++i) {
101
+ circuit = args[i]
102
+ if(circuit) return 1
103
+ else continue
104
+ }
105
+ return args.at(-1) ? 1 : 0
106
+ }`,
107
+ log_effect: `log_effect=(msg)=>{console.log(msg);return msg}`,
108
+ log_char_effect: `log_char_effect=(msg)=>{console.log(String.fromCharCode(msg));return msg}`,
109
+ log_string_effect: `log_string_effect=(msg)=>{console.log(msg.map(x=>String.fromCharCode(x)).join(''));return msg}`,
110
+ clear_effect: `clear_effect=()=>{console.clear();return 0}`,
111
+ get: 'get=(arr,i)=>arr.at(i)',
112
+ length: 'length=(arr)=>arr.length',
113
+ __tco: `__tco=fn=>(...args)=>{let result=fn(...args);while(typeof result==='function')result=result();return result}`,
114
+ atom_predicate: `atom_predicate=(number)=>+(typeof number==='number')`,
115
+ lambda_predicate: `lambda_predicate=(fm)=>+(typeof fn==='function')`,
116
+ set_effect: `set_effect=function(array,index,value){if(arguments.length===1){array.pop()}else{array[index] = value};return array}`,
117
+ __error: `__error=(error)=>{throw new Error(error.map((x)=>String.fromCharCode(x)).join(''))}`
118
+ }
119
+ const semiColumnEdgeCases = new Set([
120
+ ';)',
121
+ ';-',
122
+ ';+',
123
+ ';*',
124
+ ';%',
125
+ ';&',
126
+ ';/',
127
+ ';:',
128
+ ';.',
129
+ ';=',
130
+ ';<',
131
+ ';>',
132
+ ';|',
133
+ ';,',
134
+ ';?',
135
+ ',,',
136
+ ';;',
137
+ ';]',
138
+ ';^'
139
+ ])
140
+ const parse = (Arguments, Drill) => Arguments.map((x) => compile(x, Drill))
141
+ const parseArgs = (Arguments, Drill, separator = ',') =>
142
+ parse(Arguments, Drill).join(separator)
143
+ const compile = (tree, Drill) => {
144
+ if (!tree) return ''
145
+ const [first, ...Arguments] = !isLeaf(tree) ? tree : [tree]
146
+ if (first == undefined) return '[];'
147
+ const token = first[VALUE]
148
+ if (first[TYPE] === APPLY) {
149
+ switch (token) {
150
+ case KEYWORDS.BLOCK: {
151
+ if (Arguments.length > 1) {
152
+ return `(${Arguments.map((x) =>
153
+ (compile(x, Drill) ?? '').toString().trim()
154
+ )
155
+ .filter((x) => x !== undefined)
156
+ .join(',')});`
157
+ } else {
158
+ const res = compile(Arguments[0], Drill)
159
+ return res !== undefined ? res.toString().trim() : ''
160
+ }
161
+ }
162
+ case KEYWORDS.CALL_FUNCTION: {
163
+ const [first, ...rest] = Arguments
164
+ const apply = compile(first, Drill)
165
+ return `${
166
+ apply[apply.length - 1] === ';'
167
+ ? apply.substring(0, apply.length - 1)
168
+ : apply
169
+ }(${parseArgs(rest, Drill)})`
170
+ }
171
+ case KEYWORDS.DEFINE_VARIABLE: {
172
+ const n = Arguments[0][VALUE]
173
+ const prefix = n.split(':')[0]
174
+ if (prefix === SUGGAR.RECURSION) {
175
+ const name = lispToJavaScriptVariableName(n)
176
+ const newName = `${SUGGAR.RECURSION}_${performance
177
+ .now()
178
+ .toString()
179
+ .replace('.', 7)}`
180
+ Drill.Variables.add(name)
181
+ Drill.Variables.add(newName)
182
+ Drill.Helpers.add('__tco')
183
+ const functionArgs = Arguments.at(-1).slice(1)
184
+ const body = functionArgs.pop()
185
+ const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
186
+ deepRename(n, `()=>${newName}`, body)
187
+ const evaluatedBody = compile(body, FunctionDrill)
188
+ const vars = FunctionDrill.Variables.size
189
+ ? `var ${[...FunctionDrill.Variables].join(',')};`
190
+ : ''
191
+ return `(${name}=(__tco(${newName}=(${parseArgs(
192
+ functionArgs,
193
+ Drill
194
+ )})=>{${vars}return ${evaluatedBody
195
+ .toString()
196
+ .trim()}}, ${newName})));`
197
+ } else if (prefix === SUGGAR.CACHE) {
198
+ // memoization here
199
+ const name = lispToJavaScriptVariableName(n)
200
+ const newName = name.substring(SUGGAR.CACHE.length + 1)
201
+ Drill.Variables.add(name)
202
+ const functionArgs = Arguments.at(-1).slice(1)
203
+ const body = functionArgs.pop()
204
+ deepRename(n, newName, body)
205
+ const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
206
+ const evaluatedBody = compile(body, FunctionDrill)
207
+ const vars = FunctionDrill.Variables.size
208
+ ? `var ${[...FunctionDrill.Variables].join(',')};`
209
+ : ''
210
+ return `(${name}=function(){var __${newName}_map = new Map();var ${newName}=(function(${parseArgs(
211
+ functionArgs,
212
+ Drill
213
+ )}){${vars};var __key=[...arguments].join(',');if(__${newName}_map.has(__key)){return __${newName}_map.get(__key)}else{var __res = ${evaluatedBody
214
+ .toString()
215
+ .trim()};__${newName}_map.set(__key, __res);return __res}});return ${newName}(...arguments)});`
216
+ } else {
217
+ const name = lispToJavaScriptVariableName(n)
218
+ Drill.Variables.add(name)
219
+ return `${name}=${compile(Arguments[1], Drill)};`
220
+ }
221
+ }
222
+ case KEYWORDS.IS_ATOM:
223
+ Drill.Helpers.add('atom_predicate')
224
+ return `atom_predicate(${compile(Arguments[0], Drill)});`
225
+ case KEYWORDS.IS_LAMBDA:
226
+ Drill.Helpers.add('lambda_predicate')
227
+ return `lambda_predicate(${compile(Arguments[0], Drill)});`
228
+ case KEYWORDS.ARRAY_TYPE:
229
+ return `[${parseArgs(Arguments, Drill)}];`
230
+ case KEYWORDS.ARRAY_LENGTH:
231
+ Drill.Helpers.add('length')
232
+ return `length(${compile(Arguments[0], Drill)})`
233
+ case KEYWORDS.GET_ARRAY:
234
+ Drill.Helpers.add('get')
235
+ return `get(${compile(Arguments[0], Drill)}, ${compile(
236
+ Arguments[1],
237
+ Drill
238
+ )});`
239
+ case KEYWORDS.ANONYMOUS_FUNCTION: {
240
+ const functionArgs = Arguments
241
+ const body = Arguments.pop()
242
+ const InnerDrills = { Variables: new Set(), Helpers: Drill.Helpers }
243
+ const evaluatedBody = compile(body, InnerDrills)
244
+ const vars = InnerDrills.Variables.size
245
+ ? `var ${[...InnerDrills.Variables].join(',')};`
246
+ : ''
247
+ const args = parseArgs(
248
+ functionArgs.map((node, index) =>
249
+ node[VALUE] === PLACEHOLDER
250
+ ? leaf(node[TYPE], `_${index}`)
251
+ : leaf(node[TYPE], node[VALUE])
252
+ ),
253
+ InnerDrills
254
+ )
255
+ // const $ = [${args}];
256
+ return `((${args})=>{${vars}return ${evaluatedBody
257
+ .toString()
258
+ .trim()}});`
259
+ }
260
+ case KEYWORDS.AND:
261
+ return `((${parseArgs(Arguments, Drill, '&&')}) ? 1 : 0);`
262
+ case KEYWORDS.OR:
263
+ return `((${parseArgs(Arguments, Drill, '||')}) ? 1 : 0);`
264
+ case KEYWORDS.EQUAL:
265
+ return `+(${parseArgs(Arguments, Drill, '===')});`
266
+ case KEYWORDS.GREATHER_THAN_OR_EQUAL:
267
+ case KEYWORDS.LESS_THAN_OR_EQUAL:
268
+ case KEYWORDS.GREATHER_THAN:
269
+ case KEYWORDS.LESS_THAN:
270
+ return `+(${parseArgs(Arguments, Drill, token)});`
271
+ case KEYWORDS.SUBTRACTION:
272
+ return Arguments.length === 1
273
+ ? `(-${compile(Arguments[0], Drill)});`
274
+ : `(${parse(Arguments, Drill)
275
+ // Add space so it doesn't consider it 2--1 but 2- -1
276
+ .map((x) => (typeof x === 'number' && x < 0 ? ` ${x}` : x))
277
+ .join(token)});`
278
+ case KEYWORDS.MULTIPLICATION:
279
+ return `(${parseArgs(Arguments, Drill, token)});`
280
+ case KEYWORDS.DIVISION:
281
+ return Arguments.length === 1
282
+ ? `(1/${compile(Arguments[0], Drill)});`
283
+ : `(${parseArgs(Arguments, Drill, token)});`
284
+ case KEYWORDS.ADDITION:
285
+ return `(${parseArgs(Arguments, Drill, token)});`
286
+ case KEYWORDS.BITWISE_AND:
287
+ case KEYWORDS.BITWISE_OR:
288
+ case KEYWORDS.BITWISE_XOR:
289
+ case KEYWORDS.BITWISE_LEFT_SHIFT:
290
+ case KEYWORDS.BITWISE_RIGHT_SHIFT:
291
+ case KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT:
292
+ return `(${parseArgs(Arguments, Drill, token)});`
293
+ case KEYWORDS.REMAINDER_OF_DIVISION:
294
+ return `(${compile(Arguments[0], Drill)}%${compile(
295
+ Arguments[1],
296
+ Drill
297
+ )});`
298
+ case KEYWORDS.BIT_TYPE:
299
+ return `(${compile(Arguments[0], Drill)}>>>0).toString(2)`
300
+ case KEYWORDS.BITWISE_NOT:
301
+ return `~(${compile(Arguments[0], Drill)})`
302
+ case KEYWORDS.NOT:
303
+ return `(+!${compile(Arguments[0], Drill)})`
304
+ case KEYWORDS.IF: {
305
+ return `(${compile(Arguments[0], Drill)}?${compile(
306
+ Arguments[1],
307
+ Drill
308
+ )}:${Arguments.length === 3 ? compile(Arguments[2], Drill) : 0});`
309
+ }
310
+ case KEYWORDS.THROW: {
311
+ Drill.Helpers.add('__error')
312
+ return `__error(${compile(Arguments[0], Drill)})`
313
+ }
314
+ default: {
315
+ const camelCased = lispToJavaScriptVariableName(token)
316
+ if (camelCased in Helpers) Drill.Helpers.add(camelCased)
317
+ return `${camelCased}(${parseArgs(Arguments, Drill)});`
318
+ }
319
+ }
320
+ } else if (first[TYPE] === ATOM) return first[VALUE]
321
+ else if (first[TYPE] === WORD) {
322
+ const camelCased = lispToJavaScriptVariableName(token)
323
+ if (camelCased in Helpers) Drill.Helpers.add(camelCased)
324
+ return camelCased
325
+ }
326
+ }
327
+ const HelpersEntries = new Map(Object.entries(Helpers))
328
+ export const comp = (ast) => {
329
+ const Drill = { Variables: new Set(), Helpers: new Set() }
330
+ const raw = AST.parse(AST.stringify(ast)) // cloning for fn renames mutations
331
+ .map((tree) => compile(tree, Drill))
332
+ .filter((x) => x !== undefined)
333
+ .join('\n')
334
+ let program = ''
335
+ for (let i = 0; i < raw.length; ++i) {
336
+ const current = raw[i]
337
+ const next = raw[i + 1]
338
+ if (!semiColumnEdgeCases.has(current + next)) program += current
339
+ }
340
+ const help = Drill.Helpers.size
341
+ ? `var ${[...Drill.Helpers.keys()]
342
+ .map((x) => HelpersEntries.get(x))
343
+ .join(',')};\n`
344
+ : ''
345
+ const vars = Drill.Variables.size
346
+ ? `var ${[...Drill.Variables].join(',')};`
347
+ : ''
348
+ const top = `${help}${vars}`
349
+ return `${top}${program}`
350
+ }