fez-lisp 1.4.8 → 1.4.9
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 +52 -50
- package/src/evaluator.js +10 -5
- package/src/parser.js +28 -15
package/package.json
CHANGED
package/src/compiler.js
CHANGED
@@ -141,32 +141,37 @@ const semiColumnEdgeCases = new Set([
|
|
141
141
|
';]',
|
142
142
|
';^'
|
143
143
|
])
|
144
|
-
const parse = (
|
145
|
-
const parseArgs = (
|
146
|
-
parse(
|
144
|
+
const parse = (tail, Drill) => tail.map((x) => comp(x, Drill))
|
145
|
+
const parseArgs = (tail, Drill, separator = ',') =>
|
146
|
+
parse(tail, Drill).join(separator)
|
147
147
|
const comp = (tree, Drill) => {
|
148
148
|
if (!tree) return ''
|
149
|
-
|
150
|
-
if (
|
151
|
-
|
152
|
-
|
149
|
+
let head, tail
|
150
|
+
if (isLeaf(tree)) head = tree
|
151
|
+
else {
|
152
|
+
head = tree[0]
|
153
|
+
if (head == undefined) return '[];'
|
154
|
+
|
155
|
+
tail = tree.slice(1)
|
156
|
+
}
|
157
|
+
const token = head[VALUE]
|
158
|
+
if (head[TYPE] === APPLY) {
|
153
159
|
switch (token) {
|
154
160
|
case KEYWORDS.BLOCK: {
|
155
|
-
if (
|
156
|
-
return `(${
|
157
|
-
(comp(x, Drill) ?? '').toString().trim()
|
158
|
-
)
|
161
|
+
if (tail.length > 1) {
|
162
|
+
return `(${tail
|
163
|
+
.map((x) => (comp(x, Drill) ?? '').toString().trim())
|
159
164
|
.filter((x) => x !== undefined)
|
160
165
|
.join(',')});`
|
161
166
|
} else {
|
162
|
-
const res = comp(
|
167
|
+
const res = comp(tail[0], Drill)
|
163
168
|
return res !== undefined ? res.toString().trim() : ''
|
164
169
|
}
|
165
170
|
}
|
166
171
|
case KEYWORDS.CALL_FUNCTION: {
|
167
|
-
const
|
168
|
-
const rest =
|
169
|
-
const apply = comp(
|
172
|
+
const head = tail.pop()
|
173
|
+
const rest = tail
|
174
|
+
const apply = comp(head, Drill)
|
170
175
|
return `${
|
171
176
|
apply[apply.length - 1] === ';'
|
172
177
|
? apply.substring(0, apply.length - 1)
|
@@ -174,7 +179,7 @@ const comp = (tree, Drill) => {
|
|
174
179
|
}(${parseArgs(rest, Drill)})`
|
175
180
|
}
|
176
181
|
case KEYWORDS.DEFINE_VARIABLE: {
|
177
|
-
const n =
|
182
|
+
const n = tail[0][VALUE]
|
178
183
|
const prefix = n.split(':')[0]
|
179
184
|
if (prefix === OPTIMIZATIONS.RECURSION) {
|
180
185
|
const name = lispToJavaScriptVariableName(n)
|
@@ -185,7 +190,7 @@ const comp = (tree, Drill) => {
|
|
185
190
|
Drill.Variables.add(name)
|
186
191
|
Drill.Variables.add(newName)
|
187
192
|
Drill.Helpers.add('__tco')
|
188
|
-
const functionArgs =
|
193
|
+
const functionArgs = tail.at(-1).slice(1)
|
189
194
|
const body = functionArgs.pop()
|
190
195
|
const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
|
191
196
|
deepRename(n, `()=>${newName}`, body)
|
@@ -204,7 +209,7 @@ const comp = (tree, Drill) => {
|
|
204
209
|
const name = lispToJavaScriptVariableName(n)
|
205
210
|
const newName = name.substring(OPTIMIZATIONS.CACHE.length + 1)
|
206
211
|
Drill.Variables.add(name)
|
207
|
-
const functionArgs =
|
212
|
+
const functionArgs = tail.at(-1).slice(1)
|
208
213
|
const body = functionArgs.pop()
|
209
214
|
deepRename(n, newName, body)
|
210
215
|
const FunctionDrill = { Variables: new Set(), Helpers: Drill.Helpers }
|
@@ -221,29 +226,26 @@ const comp = (tree, Drill) => {
|
|
221
226
|
} else {
|
222
227
|
const name = lispToJavaScriptVariableName(n)
|
223
228
|
Drill.Variables.add(name)
|
224
|
-
return `${name}=${comp(
|
229
|
+
return `${name}=${comp(tail[1], Drill)};`
|
225
230
|
}
|
226
231
|
}
|
227
232
|
case KEYWORDS.IS_ATOM:
|
228
233
|
Drill.Helpers.add('atom_predicate')
|
229
|
-
return `atom_predicate(${comp(
|
234
|
+
return `atom_predicate(${comp(tail[0], Drill)});`
|
230
235
|
case KEYWORDS.IS_LAMBDA:
|
231
236
|
Drill.Helpers.add('lambda_predicate')
|
232
|
-
return `lambda_predicate(${comp(
|
237
|
+
return `lambda_predicate(${comp(tail[0], Drill)});`
|
233
238
|
case KEYWORDS.CREATE_ARRAY:
|
234
|
-
return `[${parseArgs(
|
239
|
+
return `[${parseArgs(tail, Drill)}];`
|
235
240
|
case KEYWORDS.ARRAY_LENGTH:
|
236
241
|
Drill.Helpers.add('length')
|
237
|
-
return `length(${comp(
|
242
|
+
return `length(${comp(tail[0], Drill)})`
|
238
243
|
case KEYWORDS.GET_ARRAY:
|
239
244
|
Drill.Helpers.add('get')
|
240
|
-
return `get(${comp(
|
241
|
-
Arguments[1],
|
242
|
-
Drill
|
243
|
-
)});`
|
245
|
+
return `get(${comp(tail[0], Drill)}, ${comp(tail[1], Drill)});`
|
244
246
|
case KEYWORDS.ANONYMOUS_FUNCTION: {
|
245
|
-
const functionArgs =
|
246
|
-
const body =
|
247
|
+
const functionArgs = tail
|
248
|
+
const body = tail.pop()
|
247
249
|
const InnerDrills = { Variables: new Set(), Helpers: Drill.Helpers }
|
248
250
|
const evaluatedBody = comp(body, InnerDrills)
|
249
251
|
const vars = InnerDrills.Variables.size
|
@@ -263,61 +265,61 @@ const comp = (tree, Drill) => {
|
|
263
265
|
.trim()}});`
|
264
266
|
}
|
265
267
|
case KEYWORDS.AND:
|
266
|
-
return `((${parseArgs(
|
268
|
+
return `((${parseArgs(tail, Drill, '&&')}) ? 1 : 0);`
|
267
269
|
case KEYWORDS.OR:
|
268
|
-
return `((${parseArgs(
|
270
|
+
return `((${parseArgs(tail, Drill, '||')}) ? 1 : 0);`
|
269
271
|
case KEYWORDS.EQUAL:
|
270
|
-
return `+(${parseArgs(
|
272
|
+
return `+(${parseArgs(tail, Drill, '===')});`
|
271
273
|
case KEYWORDS.GREATHER_THAN_OR_EQUAL:
|
272
274
|
case KEYWORDS.LESS_THAN_OR_EQUAL:
|
273
275
|
case KEYWORDS.GREATHER_THAN:
|
274
276
|
case KEYWORDS.LESS_THAN:
|
275
|
-
return `+(${parseArgs(
|
277
|
+
return `+(${parseArgs(tail, Drill, token)});`
|
276
278
|
case KEYWORDS.SUBTRACTION:
|
277
|
-
return
|
278
|
-
? `(-${comp(
|
279
|
-
: `(${parse(
|
279
|
+
return tail.length === 1
|
280
|
+
? `(-${comp(tail[0], Drill)});`
|
281
|
+
: `(${parse(tail, Drill)
|
280
282
|
// Add space so it doesn't consider it 2--1 but 2- -1
|
281
283
|
.map((x) => (typeof x === 'number' && x < 0 ? ` ${x}` : x))
|
282
284
|
.join(token)});`
|
283
285
|
case KEYWORDS.MULTIPLICATION:
|
284
|
-
return `(${parseArgs(
|
286
|
+
return `(${parseArgs(tail, Drill, token)});`
|
285
287
|
case KEYWORDS.DIVISION:
|
286
|
-
return `(${parseArgs(
|
288
|
+
return `(${parseArgs(tail, Drill, token)});`
|
287
289
|
case KEYWORDS.ADDITION:
|
288
|
-
return `(${parseArgs(
|
290
|
+
return `(${parseArgs(tail, Drill, token)});`
|
289
291
|
case KEYWORDS.BITWISE_AND:
|
290
292
|
case KEYWORDS.BITWISE_OR:
|
291
293
|
case KEYWORDS.BITWISE_XOR:
|
292
294
|
case KEYWORDS.BITWISE_LEFT_SHIFT:
|
293
295
|
case KEYWORDS.BITWISE_RIGHT_SHIFT:
|
294
296
|
case KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT:
|
295
|
-
return `(${parseArgs(
|
297
|
+
return `(${parseArgs(tail, Drill, token)});`
|
296
298
|
case KEYWORDS.REMAINDER_OF_DIVISION:
|
297
|
-
return `(${comp(
|
299
|
+
return `(${comp(tail[0], Drill)}%${comp(tail[1], Drill)});`
|
298
300
|
case KEYWORDS.BIT_TYPE:
|
299
|
-
return `(${comp(
|
301
|
+
return `(${comp(tail[0], Drill)}>>>0).toString(2)`
|
300
302
|
case KEYWORDS.BITWISE_NOT:
|
301
|
-
return `~(${comp(
|
303
|
+
return `~(${comp(tail[0], Drill)})`
|
302
304
|
case KEYWORDS.NOT:
|
303
|
-
return `(+!${comp(
|
305
|
+
return `(+!${comp(tail[0], Drill)})`
|
304
306
|
case KEYWORDS.IF: {
|
305
|
-
return `(${comp(
|
306
|
-
|
307
|
+
return `(${comp(tail[0], Drill)}?${comp(tail[1], Drill)}:${
|
308
|
+
tail.length === 3 ? comp(tail[2], Drill) : 0
|
307
309
|
});`
|
308
310
|
}
|
309
311
|
case KEYWORDS.ERROR: {
|
310
312
|
Drill.Helpers.add('__error')
|
311
|
-
return `__error(${compile(
|
313
|
+
return `__error(${compile(tail[0], Drill)})`
|
312
314
|
}
|
313
315
|
default: {
|
314
316
|
const camelCased = lispToJavaScriptVariableName(token)
|
315
317
|
if (camelCased in Helpers) Drill.Helpers.add(camelCased)
|
316
|
-
return `${camelCased}(${parseArgs(
|
318
|
+
return `${camelCased}(${parseArgs(tail, Drill)});`
|
317
319
|
}
|
318
320
|
}
|
319
|
-
} else if (
|
320
|
-
else if (
|
321
|
+
} else if (head[TYPE] === ATOM) return head[VALUE]
|
322
|
+
else if (head[TYPE] === WORD) {
|
321
323
|
const camelCased = lispToJavaScriptVariableName(token)
|
322
324
|
if (camelCased in Helpers) Drill.Helpers.add(camelCased)
|
323
325
|
return camelCased
|
package/src/evaluator.js
CHANGED
@@ -12,10 +12,15 @@ import {
|
|
12
12
|
import { isLeaf } from './parser.js'
|
13
13
|
import { stringifyArgs } from './utils.js'
|
14
14
|
export const evaluate = (exp, env = keywords) => {
|
15
|
-
|
16
|
-
if (
|
17
|
-
|
18
|
-
|
15
|
+
let head, tail
|
16
|
+
if (isLeaf(exp)) head = exp
|
17
|
+
else {
|
18
|
+
head = exp[0]
|
19
|
+
if (head == undefined) return []
|
20
|
+
tail = exp.slice(1)
|
21
|
+
}
|
22
|
+
const value = head[VALUE]
|
23
|
+
switch (head[TYPE]) {
|
19
24
|
case WORD: {
|
20
25
|
const word = env[value]
|
21
26
|
if (word == undefined)
|
@@ -33,7 +38,7 @@ export const evaluate = (exp, env = keywords) => {
|
|
33
38
|
`${value} is not a (${KEYWORDS.ANONYMOUS_FUNCTION})`
|
34
39
|
)
|
35
40
|
const isSpecial = SPECIAL_FORMS_SET.has(value)
|
36
|
-
const result = apply(
|
41
|
+
const result = apply(tail, env, value)
|
37
42
|
if (!isSpecial && Array.isArray(env[DEBUG.CALLSTACK]))
|
38
43
|
env[DEBUG.CALLSTACK].pop()
|
39
44
|
return result
|
package/src/parser.js
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import { APPLY, ATOM, TYPE, WORD, VALUE } from './keywords.js'
|
2
2
|
export const leaf = (type, value) => [type, value]
|
3
3
|
export const isLeaf = ([car]) => car === APPLY || car === ATOM || car === WORD
|
4
|
+
export const toPair = (exp) => []
|
4
5
|
export const LISP = {
|
5
6
|
parse: (source) => {
|
6
7
|
const tree = []
|
@@ -46,17 +47,23 @@ export const LISP = {
|
|
46
47
|
source: (ast) => {
|
47
48
|
const dfs = (exp) => {
|
48
49
|
let out = ''
|
49
|
-
|
50
|
-
if (
|
51
|
-
|
50
|
+
let head, tail
|
51
|
+
if (isLeaf(exp)) head = exp
|
52
|
+
else {
|
53
|
+
head = exp[0]
|
54
|
+
if (head == undefined) return []
|
55
|
+
tail = exp.slice(1)
|
56
|
+
}
|
57
|
+
if (head == undefined) return (out += '()')
|
58
|
+
switch (head[TYPE]) {
|
52
59
|
case WORD:
|
53
|
-
out +=
|
60
|
+
out += head[VALUE]
|
54
61
|
break
|
55
62
|
case ATOM:
|
56
|
-
out +=
|
63
|
+
out += head[VALUE]
|
57
64
|
break
|
58
65
|
case APPLY:
|
59
|
-
out += `(${
|
66
|
+
out += `(${head[VALUE]} ${tail.map(dfs).join(' ')})`
|
60
67
|
break
|
61
68
|
}
|
62
69
|
return out
|
@@ -108,25 +115,31 @@ export const AST = {
|
|
108
115
|
struct: (ast) => {
|
109
116
|
const dfs = (exp) => {
|
110
117
|
let out = ''
|
111
|
-
|
112
|
-
if (
|
118
|
+
let head, tail
|
119
|
+
if (isLeaf(exp)) head = exp
|
120
|
+
else {
|
121
|
+
head = exp[0]
|
122
|
+
if (head == undefined) return []
|
123
|
+
tail = exp.slice(1)
|
124
|
+
}
|
125
|
+
if (head == undefined)
|
113
126
|
return (out +=
|
114
127
|
'(Expression::Apply(vec![Expression::Word("array".to_string())]))')
|
115
|
-
switch (
|
128
|
+
switch (head[TYPE]) {
|
116
129
|
case WORD:
|
117
|
-
out += `Expression::Word("${
|
130
|
+
out += `Expression::Word("${head[VALUE]}".to_string())`
|
118
131
|
break
|
119
132
|
case ATOM:
|
120
133
|
out += `Expression::Atom(${
|
121
|
-
Number.isInteger(
|
122
|
-
? `${
|
123
|
-
:
|
134
|
+
Number.isInteger(head[VALUE])
|
135
|
+
? `${head[VALUE]}.0`
|
136
|
+
: head[VALUE].toString()
|
124
137
|
})`
|
125
138
|
break
|
126
139
|
case APPLY:
|
127
140
|
out += `Expression::Apply(vec![Expression::Word("${
|
128
|
-
|
129
|
-
}".to_string()),${
|
141
|
+
head[VALUE]
|
142
|
+
}".to_string()),${tail.map(dfs).join(',')}])`
|
130
143
|
break
|
131
144
|
}
|
132
145
|
return out
|