fez-lisp 1.0.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/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
|
+
}
|