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/lib/baked/std.js +1 -1
- package/package.json +1 -1
- package/src/compiler.js +7 -8
- package/src/interpreter.js +20 -14
- package/src/keywords.js +1 -1
- package/src/utils.js +10 -27
- package/src/ocaml.js +0 -373
package/package.json
CHANGED
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
|
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
|
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, '||')})
|
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:
|
package/src/interpreter.js
CHANGED
@@ -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
|
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
|
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 (
|
904
|
+
if (args.length !== 1)
|
910
905
|
throw new RangeError(
|
911
|
-
`Invalid number of arguments to (${KEYWORDS.LOG}) (
|
906
|
+
`Invalid number of arguments to (${KEYWORDS.LOG}) (= 1 required) (${
|
912
907
|
KEYWORDS.LOG
|
913
908
|
} ${stringifyArgs(args)})`
|
914
909
|
)
|
915
|
-
const
|
916
|
-
console.log(
|
917
|
-
return
|
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
|
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
|
-
`(
|
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.
|
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 =
|
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
|
-
|
184
|
-
|
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
|
-
|
201
|
-
|
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
|
-
}
|