fez-lisp 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- package/LICENSE +21 -0
- package/README.md +111 -0
- package/cli/index.js +162 -0
- package/index.js +8 -0
- package/js.svg +14 -0
- package/lib/baked/std.js +1 -0
- package/lib/src/std.lisp +275 -0
- package/logo.svg +8 -0
- package/package.json +33 -0
- package/src/compiler.js +361 -0
- package/src/enums.js +79 -0
- package/src/interpreter.js +29 -0
- package/src/parser.js +45 -0
- package/src/tokeniser.js +1036 -0
- package/src/utils.js +258 -0
package/lib/src/std.lisp
ADDED
@@ -0,0 +1,275 @@
|
|
1
|
+
(let E 2.718281828459045)
|
2
|
+
(let PI 3.141592653589793)
|
3
|
+
|
4
|
+
(let* iteration (lambda array callback
|
5
|
+
(when (length array)
|
6
|
+
(do (callback (car array))
|
7
|
+
(iteration (cdr array) callback)))))
|
8
|
+
|
9
|
+
(let scan (lambda array callback (do
|
10
|
+
(let* iterate (lambda array out
|
11
|
+
(if (length array)
|
12
|
+
(iterate (cdr array)
|
13
|
+
(merge out (Array (callback (car array)))))
|
14
|
+
out)))
|
15
|
+
(iterate array ()))))
|
16
|
+
|
17
|
+
(let select (lambda array callback (do
|
18
|
+
(let* iterate (lambda array out
|
19
|
+
(if (length array)
|
20
|
+
(iterate (cdr array)
|
21
|
+
(if (callback (car array))
|
22
|
+
(merge out (Array (car array)))
|
23
|
+
out))
|
24
|
+
out)))
|
25
|
+
(iterate array ()))))
|
26
|
+
|
27
|
+
(let exclude (lambda array callback (do
|
28
|
+
(let* iterate (lambda array out
|
29
|
+
(if (length array)
|
30
|
+
(iterate (cdr array)
|
31
|
+
(if (not (callback (car array)))
|
32
|
+
(merge out (Array (car array)))
|
33
|
+
out))
|
34
|
+
out)))
|
35
|
+
(iterate array ()))))
|
36
|
+
|
37
|
+
(let fold (lambda array callback initial (do
|
38
|
+
(let* iterate (lambda array out
|
39
|
+
(if (length array)
|
40
|
+
(iterate (cdr array) (callback out (car array)))
|
41
|
+
out)))
|
42
|
+
(iterate array initial))))
|
43
|
+
|
44
|
+
(let every? (lambda array callback (do
|
45
|
+
(let* iterate (lambda array
|
46
|
+
(if (and (length array) (callback (car array)))
|
47
|
+
(iterate (cdr array))
|
48
|
+
(not (length array)))))
|
49
|
+
(iterate array))))
|
50
|
+
|
51
|
+
(let some? (lambda array callback (do
|
52
|
+
(let* iterate (lambda array
|
53
|
+
(if (and (length array) (not (callback (car array))))
|
54
|
+
(iterate (cdr array))
|
55
|
+
(type (length array) Boolean))))
|
56
|
+
(iterate array))))
|
57
|
+
|
58
|
+
(let find (lambda array callback (do
|
59
|
+
(let* iterate (lambda array
|
60
|
+
(when (length array)
|
61
|
+
(if (callback (car array)) (car array) (iterate (cdr array))))))
|
62
|
+
(iterate array))))
|
63
|
+
|
64
|
+
(let has? (lambda array callback (do
|
65
|
+
(let* iterate (lambda array
|
66
|
+
(when (length array)
|
67
|
+
(if (callback (car array)) 1 (iterate (cdr array))))))
|
68
|
+
(iterate array))))
|
69
|
+
|
70
|
+
(let reverse (lambda array (do
|
71
|
+
(let* iterate (lambda array out
|
72
|
+
(if (length array)
|
73
|
+
(iterate (cdr array)
|
74
|
+
(merge (Array (car array)) out))
|
75
|
+
out)))
|
76
|
+
(iterate array ()))))
|
77
|
+
|
78
|
+
(let range (lambda start end (do
|
79
|
+
(let* iterate (lambda out count
|
80
|
+
(if (<= count end) (iterate (merge out (Array count)) (+ count 1)) out)))
|
81
|
+
(iterate () start))))
|
82
|
+
|
83
|
+
(let sequence (lambda array (do
|
84
|
+
(let end (length array))
|
85
|
+
(let* iterate (lambda out count
|
86
|
+
(if (< (length out) end) (iterate (merge out (Array count)) (+ count 1)) out)))
|
87
|
+
(iterate () 0))))
|
88
|
+
|
89
|
+
(let sequence-n (lambda n (do
|
90
|
+
(let* iterate (lambda out count
|
91
|
+
(if (< (length out) n) (iterate (merge out (Array count)) (+ count 1)) out)))
|
92
|
+
(iterate () 0))))
|
93
|
+
|
94
|
+
(let unique (lambda array (go
|
95
|
+
(let sorted (sort array (safety lambda a b (> a b))))
|
96
|
+
(zip (sequence sorted))
|
97
|
+
(select (lambda x
|
98
|
+
(or (not (let index (car (cdr x))))
|
99
|
+
(not (= (get sorted (- index 1)) (get sorted index))))))
|
100
|
+
(scan car))))
|
101
|
+
|
102
|
+
(let for-range (lambda start end callback (do
|
103
|
+
(let* iterate (lambda i
|
104
|
+
(when (< i end)
|
105
|
+
(do
|
106
|
+
(callback i)
|
107
|
+
(iterate (+ i 1))))))
|
108
|
+
(iterate start))))
|
109
|
+
|
110
|
+
(let list-range (lambda start end (do
|
111
|
+
(let range (lambda list start end
|
112
|
+
(if (< start end)
|
113
|
+
(Array (merge list (range (Array (Array (+ start 1))) (+ start 1) end)))
|
114
|
+
list)))
|
115
|
+
(car (car (range () start end))))))
|
116
|
+
|
117
|
+
(let traverse (lambda x callback
|
118
|
+
(if (Atom? x)
|
119
|
+
(callback x)
|
120
|
+
(iterate x (lambda y (traverse y callback))))))
|
121
|
+
|
122
|
+
(let summation (lambda array (fold array (safety lambda a b (+ a b)) 0)))
|
123
|
+
(let product (lambda array (fold array (safety lambda a b (* a b)) 1)))
|
124
|
+
(let maximum (lambda array (fold array (safety lambda a b (if (> a b) a b)) (car array))))
|
125
|
+
(let minimum (lambda array (fold array (safety lambda a b (if (< a b) a b)) (car array))))
|
126
|
+
(let max (lambda a b (if (> a b) a b)))
|
127
|
+
(let min (lambda a b (if (< a b) a b)))
|
128
|
+
(let count-of (lambda array callback (go array (select callback) (length))))
|
129
|
+
(let increment (safety lambda i (+ i 1)))
|
130
|
+
(let floor (safety lambda n (| n 0)))
|
131
|
+
(let round (safety lambda n (| (+ n 0.5) 0)))
|
132
|
+
(let empty? (safety lambda array (not (length array))))
|
133
|
+
(let array-in-bounds? (safety lambda array index (and (< index (length array)) (>= index 0))))
|
134
|
+
|
135
|
+
(let string->array (safety lambda string (type string Array)))
|
136
|
+
(let array->string (lambda array (fold array (safety lambda a x (concatenate a (type x String))) "")))
|
137
|
+
(let string->number (safety lambda string (type string Number)))
|
138
|
+
(let number->string (safety lambda number (type number String)))
|
139
|
+
(let strings->numbers (lambda array (scan array (safety lambda x (type x Number)))))
|
140
|
+
(let numbers->strings (lambda array (scan array (safety lambda x (type x String)))))
|
141
|
+
(let string->charcodes (lambda string (go string (type Array) (scan (lambda x (type x Char-Code))))))
|
142
|
+
(let chars->charcodes (lambda array (go array (scan (lambda x (type x Char-Code))))))
|
143
|
+
(let charcodes->chars (lambda array (go array (scan (lambda x (type x Char))))))
|
144
|
+
(let charcodes->string (lambda array (go array (scan (lambda x (type x Char))) (array->string))))
|
145
|
+
|
146
|
+
(let power (lambda base exp
|
147
|
+
(if (< exp 0)
|
148
|
+
(if (= base 0)
|
149
|
+
(throw "Attempting to divide by 0 in (power)")
|
150
|
+
(/ (* base (power base (- (* exp -1) 1)))))
|
151
|
+
(cond
|
152
|
+
(= exp 0) 1
|
153
|
+
(= exp 1) base
|
154
|
+
(*) (* base (power base (- exp 1)))))))
|
155
|
+
|
156
|
+
(let greatest-common-divisor (lambda a b (do
|
157
|
+
(let* gcd (lambda a b
|
158
|
+
(if (= b 0) a (gcd b (mod a b)))) (gcd a b)))))
|
159
|
+
|
160
|
+
(let least-common-divisor (lambda a b (* a b (/ (greatest-common-divisor a b)))))
|
161
|
+
|
162
|
+
(let sqrt (lambda x (do
|
163
|
+
(let is-good-enough (lambda g x (< (abs (- (square g) x)) 0.01))
|
164
|
+
improve-guess (lambda g x (average g (* x (/ g)))))
|
165
|
+
(let* sqrt-iter (lambda g x
|
166
|
+
(if (is-good-enough g x) g
|
167
|
+
(sqrt-iter (improve-guess g x) x))))
|
168
|
+
(sqrt-iter 1.0 x))))
|
169
|
+
(let circumference (lambda radius (* PI (* radius 2))))
|
170
|
+
(let hypotenuse (lambda a b (sqrt (+ (* a a) (* b b)))))
|
171
|
+
(let abs (safety lambda n (- (^ n (>> n 31)) (>> n 31))))
|
172
|
+
(let nth-digit (lambda digit n (| (mod (/ digit (power 10 (- n 1))) 10) 0.5)))
|
173
|
+
(let normalize (safety lambda value min max (* (- value min) (/ (- max min)))))
|
174
|
+
(let linear-interpolation (safety lambda a b n (+ (* (- 1 n) a) (* n b))))
|
175
|
+
(let gauss-sum (safety lambda n (* n (+ n 1) 0.5)))
|
176
|
+
(let gauss-sum-sequance (safety lambda a b (* (+ a b) (+ (- b a) 1) 0.5)))
|
177
|
+
(let clamp (safety lambda x limit (if (> x limit) limit x)))
|
178
|
+
(let odd? (safety lambda x (= (mod x 2) 1)))
|
179
|
+
(let even? (safety lambda x (= (mod x 2) 0)))
|
180
|
+
(let sign (safety lambda n (if (< n 0) -1 1)))
|
181
|
+
(let radians (lambda deg (* deg PI (/ 180))))
|
182
|
+
(let average (safety lambda x y (* (+ x y) 0.5)))
|
183
|
+
(let euclidean-mod (safety lambda a b (mod (+ (mod a b) b) b)))
|
184
|
+
(let euclidean-distance (lambda x1 y1 x2 y2 (do
|
185
|
+
(let a (- x1 x2))
|
186
|
+
(let b (- y1 y2))
|
187
|
+
(sqrt (+ (* a a) (* b b))))))
|
188
|
+
(let manhattan-distance (lambda x1 y1 x2 y2 (+ (abs (- x2 x1)) (abs (- y2 y1)))))
|
189
|
+
(let positive? (safety lambda num (> num 0)))
|
190
|
+
(let negative? (safety lambda num (< num 0)))
|
191
|
+
(let zero? (safety lambda num (= num 0)))
|
192
|
+
(let divisible? (safety lambda a b (= (mod a b) 0)))
|
193
|
+
(let prime? (lambda n
|
194
|
+
(cond
|
195
|
+
(= n 1) 0
|
196
|
+
(< n 0) 0
|
197
|
+
(*) (do
|
198
|
+
(let* iter (lambda i end (do
|
199
|
+
(let it-is (not (= (mod n i) 0)))
|
200
|
+
(if (and (<= i end) it-is) (iter (+ i 1) end) it-is))))
|
201
|
+
(or (= n 2) (iter 2 (sqrt n)))))))
|
202
|
+
|
203
|
+
(let slice (safety lambda array start end (do
|
204
|
+
(let bounds (- end start))
|
205
|
+
(let* iterate (lambda i out
|
206
|
+
(if (< i bounds)
|
207
|
+
(iterate (+ i 1) (merge out (Array (get array (+ start i)))))
|
208
|
+
out)))
|
209
|
+
(iterate 0 ()))))
|
210
|
+
|
211
|
+
(let binary-search
|
212
|
+
(lambda array target (do
|
213
|
+
(let* search
|
214
|
+
(lambda arr target start end (do
|
215
|
+
(when (<= start end) (do
|
216
|
+
(let index (floor (* (+ start end) 0.5)))
|
217
|
+
(let current (get arr index))
|
218
|
+
(if (= target current) target
|
219
|
+
(if (> current target)
|
220
|
+
(search arr target start (- index 1))
|
221
|
+
(search arr target (+ index 1) end))))))))
|
222
|
+
(search array target 0 (length array)))))
|
223
|
+
|
224
|
+
(let zip (safety lambda A B (do
|
225
|
+
(let* iterate (lambda a b output
|
226
|
+
(if (and (length a) (length b)) (iterate (cdr a) (cdr b) (merge output (Array (Array (car a) (car b))))) output)))
|
227
|
+
(iterate A B ()))))
|
228
|
+
|
229
|
+
(let cartesian-product (lambda a b (fold a (lambda p x (merge p (scan b (lambda y (Array x y))))) ())))
|
230
|
+
|
231
|
+
(let equal? (lambda a b
|
232
|
+
(or (and (Atom? a) (Atom? b) (= a b))
|
233
|
+
(and (Array? a)
|
234
|
+
(= (length a) (length b))
|
235
|
+
(not (some? (sequence a) (lambda i (not (equal? (get a i) (get b i))))))))))
|
236
|
+
|
237
|
+
(let split (lambda string delim (do
|
238
|
+
(let input (type (concatenate string delim) Array))
|
239
|
+
(let marks
|
240
|
+
(go
|
241
|
+
input
|
242
|
+
(zip (sequence input))
|
243
|
+
(scan (lambda x (if (= (car x) delim) (car (cdr x)) (car x))))))
|
244
|
+
(let first (find marks (lambda x (Number? x))))
|
245
|
+
(go
|
246
|
+
marks
|
247
|
+
(fold (lambda a b
|
248
|
+
(if (Number? b)
|
249
|
+
(merge a (Array (slice input (- b first) b)))
|
250
|
+
a)) ())
|
251
|
+
(scan (lambda x (array->string x)))))))
|
252
|
+
|
253
|
+
(let join (lambda array delim (fold (zip array (sequence array)) (lambda a b (if (> (car (cdr b)) 0) (concatenate a delim (type (car b) String)) (type (car b) String))) "")))
|
254
|
+
|
255
|
+
(let flat (lambda array (do
|
256
|
+
(let flatten (lambda item
|
257
|
+
(if (and (Array? item) (length item))
|
258
|
+
(fold item (lambda a b (merge a (flatten b))) ())
|
259
|
+
(Array item))))
|
260
|
+
(flatten array))))
|
261
|
+
|
262
|
+
(let sort (lambda arr callback (do
|
263
|
+
(if (<= (length arr) 1) arr (do
|
264
|
+
(let pivot (car arr))
|
265
|
+
(let* iterate (lambda i bounds a b (do
|
266
|
+
(let current (get arr i))
|
267
|
+
(let predicate (callback current pivot))
|
268
|
+
(let left (if (= predicate 0) (merge a (Array current)) a))
|
269
|
+
(let right (if (= predicate 1) (merge b (Array current)) b))
|
270
|
+
(if (< i bounds) (iterate (+ i 1) bounds left right)
|
271
|
+
(Array left right)))))
|
272
|
+
(let sorted (iterate 1 (- (length arr) 1) () ()))
|
273
|
+
(let left (car sorted))
|
274
|
+
(let right (car (cdr sorted)))
|
275
|
+
(merge (sort left callback) (Array pivot) (sort right callback)))))))
|
package/logo.svg
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
<svg width="144" height="128" viewBox="0 0 144 128" fill="none" xmlns="http://www.w3.org/2000/svg">
|
2
|
+
<path d="M128 0H0V128H128V0Z" fill="#B44637"/>
|
3
|
+
<path d="M128 0H0V128H128V0Z" fill="#B44637"/>
|
4
|
+
<path d="M128 0H96V128H128V0Z" fill="#8E2F22"/>
|
5
|
+
<path d="M128 0H96V128H128V0Z" fill="#8E2F22"/>
|
6
|
+
<path d="M112 0H128V64H112V0Z" fill="#D6C096"/>
|
7
|
+
<path d="M128 64H144V80H128V64Z" fill="#D6C096"/>
|
8
|
+
</svg>
|
package/package.json
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
{
|
2
|
+
"name": "fez-lisp",
|
3
|
+
"description": "Immutable Lisp interpreted & compiled to JavaScript",
|
4
|
+
"author": "AT290690",
|
5
|
+
"version": "1.0.0",
|
6
|
+
"type": "module",
|
7
|
+
"main": "index.js",
|
8
|
+
"keywords": [
|
9
|
+
"lisp",
|
10
|
+
"expressions",
|
11
|
+
"programming-language",
|
12
|
+
"interpreter",
|
13
|
+
"compiler"
|
14
|
+
],
|
15
|
+
"repository": {
|
16
|
+
"type": "git",
|
17
|
+
"url": "git+https://github.com/AT-290690/fez.git"
|
18
|
+
},
|
19
|
+
"prettier": {
|
20
|
+
"semi": false,
|
21
|
+
"singleQuote": true
|
22
|
+
},
|
23
|
+
"scripts": {
|
24
|
+
"build": "node ./lib/builder.js",
|
25
|
+
"fez": "node main.js",
|
26
|
+
"test": "mocha"
|
27
|
+
},
|
28
|
+
"license": "MIT",
|
29
|
+
"devDependencies": {
|
30
|
+
"mocha": "^10.2.0"
|
31
|
+
},
|
32
|
+
"dependencies": {}
|
33
|
+
}
|
package/src/compiler.js
ADDED
@@ -0,0 +1,361 @@
|
|
1
|
+
import {
|
2
|
+
APPLY,
|
3
|
+
ATOM,
|
4
|
+
PLACEHOLDER,
|
5
|
+
KEYWORDS,
|
6
|
+
TYPE,
|
7
|
+
VALUE,
|
8
|
+
WORD,
|
9
|
+
} from './enums.js'
|
10
|
+
import { deepRename, lispToJavaScriptVariableName } from './utils.js'
|
11
|
+
const Helpers = {
|
12
|
+
log: `var logEffect=(msg)=>{console.log(msg);return msg}`,
|
13
|
+
_merge: `_merge=(...arrays)=>arrays.reduce((a,b)=>a.concat(b),[])`,
|
14
|
+
tco: `tco=fn=>(...args)=>{
|
15
|
+
let result=fn(...args)
|
16
|
+
while(typeof result==='function')result=result()
|
17
|
+
return result
|
18
|
+
}`,
|
19
|
+
atom: `_isAtom=(value)=>typeof value==='number'||typeof value==='string'`,
|
20
|
+
error: `_error=(error)=>{
|
21
|
+
throw new Error(error)
|
22
|
+
}`,
|
23
|
+
serialise: `_serialise=(result)=>{
|
24
|
+
return typeof result==='function'?'(λ)':Array.isArray(result)?JSON.stringify(result,(_,value)=>{
|
25
|
+
switch(typeof value){
|
26
|
+
case 'number':
|
27
|
+
return Number(value)
|
28
|
+
case 'function':
|
29
|
+
return 'λ'
|
30
|
+
case 'undefined':
|
31
|
+
case 'symbol':
|
32
|
+
return 0
|
33
|
+
case 'boolean':
|
34
|
+
return +value
|
35
|
+
default:
|
36
|
+
return value
|
37
|
+
}
|
38
|
+
})
|
39
|
+
.replace(new RegExp(/\\[/g),"(Array ")
|
40
|
+
.replace(new RegExp(/\\]/g),')')
|
41
|
+
.replace(new RegExp(/\\,/g),' ')
|
42
|
+
.replace(new RegExp(/"λ"/g),'λ')
|
43
|
+
:typeof result==='string'
|
44
|
+
?'"'+result+'"'
|
45
|
+
:result==undefined
|
46
|
+
?'(void)'
|
47
|
+
:result
|
48
|
+
}`,
|
49
|
+
cast: `_cast=(type,value)=>{
|
50
|
+
switch (type) {
|
51
|
+
case 'Number':
|
52
|
+
return Number(value)
|
53
|
+
case 'String':
|
54
|
+
return value.toString()
|
55
|
+
case 'Array':
|
56
|
+
return typeof value==='number'?[...Number(value).toString()].map(Number):[...value]
|
57
|
+
case 'Bit':
|
58
|
+
return parseInt(value,2)
|
59
|
+
case 'Boolean':
|
60
|
+
return +!!value
|
61
|
+
case 'Function':
|
62
|
+
return ()=>value
|
63
|
+
case 'Char-Code':
|
64
|
+
return String.fromCharCode(value)
|
65
|
+
case 'Char':
|
66
|
+
return value.charCodeAt(0)
|
67
|
+
default:
|
68
|
+
return 0
|
69
|
+
}
|
70
|
+
}`,
|
71
|
+
}
|
72
|
+
const handleBoolean = (source) => `+${source}`
|
73
|
+
const semiColumnEdgeCases = new Set([
|
74
|
+
';)',
|
75
|
+
';-',
|
76
|
+
';+',
|
77
|
+
';*',
|
78
|
+
';%',
|
79
|
+
';&',
|
80
|
+
';/',
|
81
|
+
';:',
|
82
|
+
';.',
|
83
|
+
';=',
|
84
|
+
';<',
|
85
|
+
';>',
|
86
|
+
';|',
|
87
|
+
';,',
|
88
|
+
';?',
|
89
|
+
',,',
|
90
|
+
';;',
|
91
|
+
';]',
|
92
|
+
])
|
93
|
+
|
94
|
+
const parse = (Arguments, Variables) =>
|
95
|
+
Arguments.map((x) => compile(x, Variables))
|
96
|
+
const parseArgs = (Arguments, Variables, separator = ',') =>
|
97
|
+
parse(Arguments, Variables).join(separator)
|
98
|
+
const compile = (tree, Variables) => {
|
99
|
+
if (!tree) return ''
|
100
|
+
const [first, ...Arguments] = Array.isArray(tree) ? tree : [tree]
|
101
|
+
if (first == undefined) return '[];'
|
102
|
+
const token = first[VALUE]
|
103
|
+
if (first[TYPE] === APPLY) {
|
104
|
+
switch (token) {
|
105
|
+
case KEYWORDS.BLOCK: {
|
106
|
+
if (Arguments.length > 1) {
|
107
|
+
return `(${Arguments.map((x) =>
|
108
|
+
(compile(x, Variables) ?? '').toString().trimStart()
|
109
|
+
)
|
110
|
+
.filter(Boolean)
|
111
|
+
.join(',')});`
|
112
|
+
} else {
|
113
|
+
const res = compile(Arguments[0], Variables)
|
114
|
+
return res !== undefined ? res.toString().trim() : ''
|
115
|
+
}
|
116
|
+
}
|
117
|
+
case KEYWORDS.CALL_FUNCTION: {
|
118
|
+
const [first, ...rest] = Arguments
|
119
|
+
const apply = compile(first, Variables)
|
120
|
+
return `${
|
121
|
+
apply[apply.length - 1] === ';'
|
122
|
+
? apply.substring(0, apply.length - 1)
|
123
|
+
: apply
|
124
|
+
}(${parseArgs(rest, Variables)})`
|
125
|
+
}
|
126
|
+
case KEYWORDS.DEFINE_VARIABLE: {
|
127
|
+
let name,
|
128
|
+
out = '(('
|
129
|
+
if (Arguments[0][TYPE] === WORD) {
|
130
|
+
name = lispToJavaScriptVariableName(Arguments[0][VALUE])
|
131
|
+
Variables.add(name)
|
132
|
+
}
|
133
|
+
out += `${name}=${compile(Arguments[1], Variables)}`
|
134
|
+
out += `),${name});`
|
135
|
+
return out
|
136
|
+
}
|
137
|
+
case KEYWORDS.IS_STRING:
|
138
|
+
return handleBoolean(
|
139
|
+
`(typeof(${compile(Arguments[0], Variables)})==='string');`
|
140
|
+
)
|
141
|
+
case KEYWORDS.IS_NUMBER:
|
142
|
+
return handleBoolean(
|
143
|
+
`(typeof(${compile(Arguments[0], Variables)})==='number');`
|
144
|
+
)
|
145
|
+
case KEYWORDS.IS_FUNCTION:
|
146
|
+
return `(typeof(${compile(Arguments[0], Variables)})==='function');`
|
147
|
+
case KEYWORDS.IS_ARRAY:
|
148
|
+
return `(Array.isArray(${compile(Arguments[0], Variables)}));`
|
149
|
+
case KEYWORDS.NUMBER_TYPE:
|
150
|
+
return '0'
|
151
|
+
case KEYWORDS.INTEGER_TYPE:
|
152
|
+
return '0n'
|
153
|
+
case KEYWORDS.BOOLEAN_TYPE:
|
154
|
+
return '1'
|
155
|
+
case KEYWORDS.STRING_TYPE:
|
156
|
+
return '""'
|
157
|
+
case KEYWORDS.ARRAY_TYPE:
|
158
|
+
return Arguments.length === 2 &&
|
159
|
+
Arguments[1][TYPE] === WORD &&
|
160
|
+
Arguments[1][VALUE] === 'length'
|
161
|
+
? `(new Array(${compile(Arguments[0], Variables)}).fill(0))`
|
162
|
+
: `[${parseArgs(Arguments, Variables)}];`
|
163
|
+
case KEYWORDS.FUNCTION_TYPE:
|
164
|
+
return '(()=>{});'
|
165
|
+
case KEYWORDS.ARRAY_OR_STRING_LENGTH:
|
166
|
+
return `(${compile(Arguments[0], Variables)}).length`
|
167
|
+
case KEYWORDS.IS_ATOM:
|
168
|
+
return handleBoolean(`_isAtom(${compile(Arguments[0], Variables)});`)
|
169
|
+
case KEYWORDS.FIRST_ARRAY:
|
170
|
+
return `${compile(Arguments[0], Variables)}.at(0);`
|
171
|
+
case KEYWORDS.REST_ARRAY:
|
172
|
+
return `${compile(Arguments[0], Variables)}.slice(1);`
|
173
|
+
case KEYWORDS.GET_ARRAY:
|
174
|
+
return `${compile(Arguments[0], Variables)}.at(${compile(
|
175
|
+
Arguments[1],
|
176
|
+
Variables
|
177
|
+
)});`
|
178
|
+
case KEYWORDS.MERGE:
|
179
|
+
return `_merge(${parseArgs(Arguments, Variables)});`
|
180
|
+
case KEYWORDS.ANONYMOUS_FUNCTION: {
|
181
|
+
const functionArgs = Arguments
|
182
|
+
const body = Arguments.pop()
|
183
|
+
const Variables = new Set()
|
184
|
+
const evaluatedBody = compile(body, Variables)
|
185
|
+
const vars = Variables.size ? `var ${[...Variables].join(',')};` : ''
|
186
|
+
return `((${parseArgs(
|
187
|
+
functionArgs.map((node, index) =>
|
188
|
+
node[VALUE] === PLACEHOLDER
|
189
|
+
? { [TYPE]: node[TYPE], [VALUE]: `_${index}` }
|
190
|
+
: { [TYPE]: node[TYPE], [VALUE]: node[VALUE] }
|
191
|
+
),
|
192
|
+
Variables
|
193
|
+
)})=>{${vars}return ${evaluatedBody.toString().trimStart()}});`
|
194
|
+
}
|
195
|
+
case KEYWORDS.TAIL_CALLS_OPTIMISED_RECURSIVE_FUNCTION: {
|
196
|
+
let name,
|
197
|
+
newName,
|
198
|
+
out = '(('
|
199
|
+
const arg = Arguments[0]
|
200
|
+
name = lispToJavaScriptVariableName(arg[VALUE])
|
201
|
+
newName = `rec_${performance.now().toString().replace('.', 7)}`
|
202
|
+
Variables.add(name)
|
203
|
+
Variables.add(newName)
|
204
|
+
const functionArgs = Arguments[1].slice(1)
|
205
|
+
const body = functionArgs.pop()
|
206
|
+
const FunctionVariables = new Set()
|
207
|
+
deepRename(arg[VALUE], newName, body)
|
208
|
+
const evaluatedBody = compile(body, FunctionVariables)
|
209
|
+
const vars = FunctionVariables.size
|
210
|
+
? `var ${[...FunctionVariables].join(',')};`
|
211
|
+
: ''
|
212
|
+
out += `${name}=(tco(${newName}=(${parseArgs(
|
213
|
+
functionArgs,
|
214
|
+
Variables
|
215
|
+
)})=>{${vars}return ${evaluatedBody.toString().trimStart()}};`
|
216
|
+
out += `, ${newName}))), ${name});`
|
217
|
+
return out
|
218
|
+
}
|
219
|
+
case KEYWORDS.AND:
|
220
|
+
return `(${parseArgs(Arguments, Variables, '&&')});`
|
221
|
+
case KEYWORDS.OR:
|
222
|
+
return `((${parseArgs(Arguments, Variables, '||')}) || 0);`
|
223
|
+
case KEYWORDS.CONCATENATION:
|
224
|
+
return '(' + parseArgs(Arguments, Variables, '+') + ');'
|
225
|
+
case KEYWORDS.EQUAL:
|
226
|
+
return handleBoolean(`(${parseArgs(Arguments, Variables, '===')});`)
|
227
|
+
case KEYWORDS.GREATHER_THAN_OR_EQUAL:
|
228
|
+
case KEYWORDS.LESS_THAN_OR_EQUAL:
|
229
|
+
case KEYWORDS.GREATHER_THAN:
|
230
|
+
case KEYWORDS.LESS_THAN:
|
231
|
+
return handleBoolean(`(${parseArgs(Arguments, Variables, token)});`)
|
232
|
+
case KEYWORDS.SUBTRACTION:
|
233
|
+
return Arguments.length === 1
|
234
|
+
? `(-${compile(Arguments[0], Variables)});`
|
235
|
+
: `(${parse(Arguments, Variables)
|
236
|
+
// Add space so it doesn't consider it 2--1 but 2- -1
|
237
|
+
.map((x) => (typeof x === 'number' && x < 0 ? ` ${x}` : x))
|
238
|
+
.join(token)});`
|
239
|
+
case KEYWORDS.MULTIPLICATION:
|
240
|
+
return Arguments.length
|
241
|
+
? `(${parseArgs(Arguments, Variables, token)});`
|
242
|
+
: `(1);`
|
243
|
+
case KEYWORDS.DIVISION:
|
244
|
+
return Arguments.length
|
245
|
+
? Arguments.length === 1
|
246
|
+
? `(1/${compile(Arguments[0], Variables)});`
|
247
|
+
: `(${parseArgs(Arguments, Variables, token)});`
|
248
|
+
: `(0);`
|
249
|
+
case KEYWORDS.ADDITION:
|
250
|
+
case KEYWORDS.BITWISE_AND:
|
251
|
+
case KEYWORDS.BITWISE_OR:
|
252
|
+
case KEYWORDS.BITWISE_XOR:
|
253
|
+
case KEYWORDS.BITWISE_LEFT_SHIFT:
|
254
|
+
case KEYWORDS.BITWISE_RIGHT_SHIFT:
|
255
|
+
case KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT:
|
256
|
+
return `(${parseArgs(Arguments, Variables, token)});`
|
257
|
+
case KEYWORDS.REMAINDER_OF_DIVISION:
|
258
|
+
return `(${compile(Arguments[0], Variables)}%${compile(
|
259
|
+
Arguments[1],
|
260
|
+
Variables
|
261
|
+
)});`
|
262
|
+
case KEYWORDS.BIT_TYPE:
|
263
|
+
return `(${compile(Arguments[0], Variables)}>>>0).toString(2)`
|
264
|
+
case KEYWORDS.BITWISE_NOT:
|
265
|
+
return `~(${compile(Arguments[0], Variables)})`
|
266
|
+
case KEYWORDS.NOT:
|
267
|
+
return `(${handleBoolean(`!${compile(Arguments[0], Variables)}`)})`
|
268
|
+
case KEYWORDS.IF: {
|
269
|
+
return `(${compile(Arguments[0], Variables)}?${compile(
|
270
|
+
Arguments[1],
|
271
|
+
Variables
|
272
|
+
)}:${compile(Arguments[2], Variables)});`
|
273
|
+
}
|
274
|
+
case KEYWORDS.WHEN: {
|
275
|
+
return `(${compile(Arguments[0], Variables)}?${compile(
|
276
|
+
Arguments[1],
|
277
|
+
Variables
|
278
|
+
)}:0);`
|
279
|
+
}
|
280
|
+
case KEYWORDS.UNLESS: {
|
281
|
+
return `(${compile(Arguments[0], Variables)}?${compile(
|
282
|
+
Arguments[2],
|
283
|
+
Variables
|
284
|
+
)}:${compile(Arguments[1], Variables)});`
|
285
|
+
}
|
286
|
+
case KEYWORDS.OTHERWISE: {
|
287
|
+
return `(${compile(Arguments[0], Variables)}?0:${compile(
|
288
|
+
Arguments[1],
|
289
|
+
Variables
|
290
|
+
)});`
|
291
|
+
}
|
292
|
+
case KEYWORDS.CONDITION: {
|
293
|
+
let out = '('
|
294
|
+
for (let i = 0; i < Arguments.length; i += 2)
|
295
|
+
out += `${compile(Arguments[i], Variables)}?${compile(
|
296
|
+
Arguments[i + 1],
|
297
|
+
Variables
|
298
|
+
)}:`
|
299
|
+
out += '0);'
|
300
|
+
return out
|
301
|
+
}
|
302
|
+
case KEYWORDS.CAST_TYPE:
|
303
|
+
return `_cast("${Arguments[1][VALUE]}", ${compile(
|
304
|
+
Arguments[0],
|
305
|
+
Variables
|
306
|
+
)})`
|
307
|
+
case KEYWORDS.PIPE: {
|
308
|
+
let inp = Arguments[0]
|
309
|
+
for (let i = 1; i < Arguments.length; ++i)
|
310
|
+
inp = [Arguments[i].shift(), inp, ...Arguments[i]]
|
311
|
+
return compile(inp, Variables)
|
312
|
+
}
|
313
|
+
case KEYWORDS.THROW_ERROR: {
|
314
|
+
return `_error(${compile(Arguments[0], Variables)})`
|
315
|
+
}
|
316
|
+
case KEYWORDS.IMMUTABLE_FUNCTION: {
|
317
|
+
const [first, ...rest] = Arguments
|
318
|
+
return compile(
|
319
|
+
[{ [TYPE]: APPLY, [VALUE]: first[VALUE] }, ...rest],
|
320
|
+
Variables
|
321
|
+
)
|
322
|
+
}
|
323
|
+
case KEYWORDS.SERIALISE:
|
324
|
+
return `_serialise(${compile(Arguments[0], Variables)});`
|
325
|
+
case KEYWORDS.NOT_COMPILED_BLOCK:
|
326
|
+
case KEYWORDS.ATOM:
|
327
|
+
case KEYWORDS.TEST_CASE:
|
328
|
+
case KEYWORDS.TEST_BED:
|
329
|
+
return ''
|
330
|
+
default: {
|
331
|
+
const camelCased = lispToJavaScriptVariableName(token)
|
332
|
+
return `${camelCased}(${parseArgs(Arguments, Variables)});`
|
333
|
+
}
|
334
|
+
}
|
335
|
+
} else if (first[TYPE] === ATOM)
|
336
|
+
return typeof first[VALUE] === 'string'
|
337
|
+
? `\`${first[VALUE]}\``
|
338
|
+
: first[VALUE]
|
339
|
+
else if (first[TYPE] === WORD) return lispToJavaScriptVariableName(token)
|
340
|
+
}
|
341
|
+
|
342
|
+
const HelperSources = Object.values(Helpers).join(',')
|
343
|
+
|
344
|
+
export const comp = (ast) => {
|
345
|
+
const Variables = new Set()
|
346
|
+
const raw = ast
|
347
|
+
.map((tree) => compile(tree, Variables))
|
348
|
+
.filter(Boolean)
|
349
|
+
.join('\n')
|
350
|
+
let program = ''
|
351
|
+
for (let i = 0; i < raw.length; ++i) {
|
352
|
+
const current = raw[i]
|
353
|
+
const next = raw[i + 1]
|
354
|
+
if (!semiColumnEdgeCases.has(current + next)) program += current
|
355
|
+
}
|
356
|
+
const top = `${HelperSources};\n${
|
357
|
+
Variables.size ? `var ${[...Variables].join(',')};` : ''
|
358
|
+
}`
|
359
|
+
|
360
|
+
return { top, program }
|
361
|
+
}
|