fez-lisp 1.3.1 → 1.3.2
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/macros.js +29 -4
- package/lib/baked/std.js +1 -1
- package/package.json +2 -3
- package/src/compiler.js +25 -6
- package/src/interpreter.js +8 -22
- package/src/keywords.js +0 -13
- package/src/macros.js +408 -0
- package/src/utils.js +13 -371
package/package.json
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
"name": "fez-lisp",
|
3
3
|
"description": "Lisp interpreted & compiled to JavaScript",
|
4
4
|
"author": "AT290690",
|
5
|
-
"version": "1.3.
|
5
|
+
"version": "1.3.2",
|
6
6
|
"type": "module",
|
7
7
|
"main": "index.js",
|
8
8
|
"keywords": [
|
@@ -29,6 +29,5 @@
|
|
29
29
|
"license": "MIT",
|
30
30
|
"devDependencies": {
|
31
31
|
"mocha": "^10.2.0"
|
32
|
-
}
|
33
|
-
"dependencies": {}
|
32
|
+
}
|
34
33
|
}
|
package/src/compiler.js
CHANGED
@@ -5,9 +5,9 @@ import {
|
|
5
5
|
KEYWORDS,
|
6
6
|
TYPE,
|
7
7
|
VALUE,
|
8
|
-
WORD
|
9
|
-
SUGGAR
|
8
|
+
WORD
|
10
9
|
} from './keywords.js'
|
10
|
+
import { SUGGAR } from './macros.js'
|
11
11
|
import { leaf, isLeaf, AST } from './parser.js'
|
12
12
|
const deepRename = (name, newName, tree) => {
|
13
13
|
if (!isLeaf(tree))
|
@@ -58,6 +58,20 @@ const keywordToHelper = (name) => {
|
|
58
58
|
return '__lt'
|
59
59
|
case KEYWORDS.LESS_THAN_OR_EQUAL:
|
60
60
|
return '__lteq'
|
61
|
+
case KEYWORDS.BITWISE_AND:
|
62
|
+
return '__bit_and'
|
63
|
+
case KEYWORDS.BITWISE_OR:
|
64
|
+
return '__bit_or'
|
65
|
+
case KEYWORDS.BITWISE_XOR:
|
66
|
+
return '__bit_xor'
|
67
|
+
case KEYWORDS.BITWISE_NOT:
|
68
|
+
return '__bit_not'
|
69
|
+
case KEYWORDS.BITWISE_LEFT_SHIFT:
|
70
|
+
return '__bit_lshift'
|
71
|
+
case KEYWORDS.BITWISE_RIGHT_SHIFT:
|
72
|
+
return '__bit_rshift'
|
73
|
+
case KEYWORDS.BITWISE_UNSIGNED_RIGHT_SHIFT:
|
74
|
+
return '__bit_urshift'
|
61
75
|
default:
|
62
76
|
return name
|
63
77
|
}
|
@@ -78,12 +92,19 @@ const Helpers = {
|
|
78
92
|
__add: `__add=(a,b)=>{return a+b}`,
|
79
93
|
__sub: `__sub=function(a,b){return arguments.length===1?-a:a-b}`,
|
80
94
|
__mult: `__mult=(a,b)=>{return a*b}`,
|
81
|
-
__div: `__div=
|
95
|
+
__div: `__div=(a,b)=>{return a/b}`,
|
82
96
|
__gteq: '__gteq=(a,b)=>+(a>=b)',
|
83
97
|
__gt: '__gt=(a,b)=>+(a>b)',
|
84
98
|
__eq: '__eq=(a,b)=>+(a===b)',
|
85
99
|
__lteq: '__lteq=(a,b)=>+(a<=b)',
|
86
100
|
__lt: '__lt=(a,b)=>+(a<b)',
|
101
|
+
__bit_and: '__bit_and=(a,b)=>a&b',
|
102
|
+
__bit_or: '__bit_or=(a,b)=>a|b',
|
103
|
+
__bit_xor: '__bit_xor=(a,b)=>a^b',
|
104
|
+
__bit_not: '__bit_not=(a)=>~a',
|
105
|
+
__bit_lshift: '__bit_lshift=(a,b)=>a<<b',
|
106
|
+
__bit_rshift: '__bit_rshift=(a,b)=>a>>b',
|
107
|
+
__bit_urshift: '__bit_urshift=(a,b)=>a>>>b',
|
87
108
|
array: 'array=(...args)=>args',
|
88
109
|
not: 'not=(a)=>+!a',
|
89
110
|
and: `and=(a, b)=>+(a&&b)`,
|
@@ -262,9 +283,7 @@ const compile = (tree, Drill) => {
|
|
262
283
|
case KEYWORDS.MULTIPLICATION:
|
263
284
|
return `(${parseArgs(Arguments, Drill, token)});`
|
264
285
|
case KEYWORDS.DIVISION:
|
265
|
-
return Arguments
|
266
|
-
? `(1/${compile(Arguments[0], Drill)});`
|
267
|
-
: `(${parseArgs(Arguments, Drill, token)});`
|
286
|
+
return `(${parseArgs(Arguments, Drill, token)});`
|
268
287
|
case KEYWORDS.ADDITION:
|
269
288
|
return `(${parseArgs(Arguments, Drill, token)});`
|
270
289
|
case KEYWORDS.BITWISE_AND:
|
package/src/interpreter.js
CHANGED
@@ -3,11 +3,11 @@ import { evaluate } from './evaluator.js'
|
|
3
3
|
import { isForbiddenVariableName, stringifyArgs } from './utils.js'
|
4
4
|
export const keywords = {
|
5
5
|
[KEYWORDS.REMAINDER_OF_DIVISION]: (args, env) => {
|
6
|
-
if (args.length
|
6
|
+
if (args.length !== 2)
|
7
7
|
throw new RangeError(
|
8
8
|
`Invalid number of arguments for (${
|
9
9
|
KEYWORDS.REMAINDER_OF_DIVISION
|
10
|
-
}), expected
|
10
|
+
}), expected (= 2) but got ${args.length}. (${
|
11
11
|
KEYWORDS.REMAINDER_OF_DIVISION
|
12
12
|
} ${stringifyArgs(args)})`
|
13
13
|
)
|
@@ -37,24 +37,10 @@ export const keywords = {
|
|
37
37
|
return a % b
|
38
38
|
},
|
39
39
|
[KEYWORDS.DIVISION]: (args, env) => {
|
40
|
-
if (args.length
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
`Arguments of (${KEYWORDS.DIVISION}) is not a (${
|
45
|
-
KEYWORDS.NUMBER_TYPE
|
46
|
-
}) (${KEYWORDS.DIVISION} ${stringifyArgs(args)})`
|
47
|
-
)
|
48
|
-
if (number === 0)
|
49
|
-
throw new TypeError(
|
50
|
-
`Argument of (${
|
51
|
-
KEYWORDS.DIVISION
|
52
|
-
}) can't be a (0) (division by 0 is not allowed) (${
|
53
|
-
KEYWORDS.DIVISION
|
54
|
-
} ${stringifyArgs(args)})`
|
55
|
-
)
|
56
|
-
return 1 / number
|
57
|
-
}
|
40
|
+
if (args.length !== 2)
|
41
|
+
throw new RangeError(
|
42
|
+
`Invalid number of arguments for (${KEYWORDS.DIVISION}), expected (= 2) but got ${args.length}.`
|
43
|
+
)
|
58
44
|
const a = evaluate(args[0], env)
|
59
45
|
if (typeof a !== 'number')
|
60
46
|
throw new TypeError(
|
@@ -139,9 +125,9 @@ export const keywords = {
|
|
139
125
|
return a + b
|
140
126
|
},
|
141
127
|
[KEYWORDS.MULTIPLICATION]: (args, env) => {
|
142
|
-
if (args.length !==
|
128
|
+
if (args.length !== 2)
|
143
129
|
throw new RangeError(
|
144
|
-
`Invalid number of arguments for (${KEYWORDS.MULTIPLICATION}), expected (
|
130
|
+
`Invalid number of arguments for (${KEYWORDS.MULTIPLICATION}), expected (= 2) but got ${args.length}.`
|
145
131
|
)
|
146
132
|
const a = evaluate(args[0], env)
|
147
133
|
if (typeof a !== 'number')
|
package/src/keywords.js
CHANGED
@@ -6,19 +6,6 @@ export const ATOM = 2
|
|
6
6
|
export const TRUE = 1
|
7
7
|
export const FALSE = 0
|
8
8
|
export const PLACEHOLDER = '.'
|
9
|
-
export const SUGGAR = {
|
10
|
-
// Syntactic suggars
|
11
|
-
PIPE: '|>',
|
12
|
-
NOT_EQUAL_1: '!=',
|
13
|
-
NOT_EQUAL_2: '<>',
|
14
|
-
UNLESS: 'unless',
|
15
|
-
LIST_TYPE: 'list',
|
16
|
-
POWER: '**',
|
17
|
-
INTEGER_DEVISION: '//',
|
18
|
-
CONDITION: 'cond',
|
19
|
-
RECURSION: 'recursive',
|
20
|
-
CACHE: 'memoized'
|
21
|
-
}
|
22
9
|
export const KEYWORDS = {
|
23
10
|
NUMBER_TYPE: 'number',
|
24
11
|
ARRAY_TYPE: 'array',
|
package/src/macros.js
ADDED
@@ -0,0 +1,408 @@
|
|
1
|
+
import { isLeaf } from './parser.js'
|
2
|
+
import {
|
3
|
+
EXPONENTIATION,
|
4
|
+
INTEGER_DIVISION,
|
5
|
+
NOT_EQUAL
|
6
|
+
} from '../lib/baked/macros.js'
|
7
|
+
import {
|
8
|
+
APPLY,
|
9
|
+
ATOM,
|
10
|
+
FALSE,
|
11
|
+
KEYWORDS,
|
12
|
+
TRUE,
|
13
|
+
TYPE,
|
14
|
+
VALUE,
|
15
|
+
WORD
|
16
|
+
} from './keywords.js'
|
17
|
+
import { stringifyArgs } from './utils.js'
|
18
|
+
export const SUGGAR = {
|
19
|
+
// Syntactic suggars
|
20
|
+
PIPE: '|>',
|
21
|
+
NOT_EQUAL_1: '!=',
|
22
|
+
NOT_EQUAL_2: '<>',
|
23
|
+
REMAINDER_OF_DIVISION_1: '%',
|
24
|
+
UNLESS: 'unless',
|
25
|
+
LIST_TYPE: 'list',
|
26
|
+
POWER: '**',
|
27
|
+
INTEGER_DEVISION: '//',
|
28
|
+
CONDITION: 'cond',
|
29
|
+
RECURSION: 'recursive',
|
30
|
+
CACHE: 'memoized'
|
31
|
+
}
|
32
|
+
export const deSuggarAst = (ast) => {
|
33
|
+
if (ast.length === 0) throw new SyntaxError(`No expressions to evaluate`)
|
34
|
+
// for (const node of ast)
|
35
|
+
// if (node[0] && node[0][TYPE] === APPLY && node[0][VALUE] === KEYWORDS.BLOCK)
|
36
|
+
// throw new SyntaxError(`Top level (${KEYWORDS.BLOCK}) is not allowed`)
|
37
|
+
let prev = undefined
|
38
|
+
const evaluate = (exp) => {
|
39
|
+
const [first, ...rest] = isLeaf(exp) ? [exp] : exp
|
40
|
+
if (first != undefined) {
|
41
|
+
switch (first[TYPE]) {
|
42
|
+
case WORD:
|
43
|
+
{
|
44
|
+
switch (first[VALUE]) {
|
45
|
+
case SUGGAR.REMAINDER_OF_DIVISION_1:
|
46
|
+
exp[VALUE] = KEYWORDS.REMAINDER_OF_DIVISION
|
47
|
+
break
|
48
|
+
case SUGGAR.NOT_EQUAL_1:
|
49
|
+
case SUGGAR.NOT_EQUAL_2:
|
50
|
+
exp.length = 0
|
51
|
+
exp.push(...NOT_EQUAL)
|
52
|
+
break
|
53
|
+
case SUGGAR.POWER:
|
54
|
+
exp.length = 0
|
55
|
+
exp.push(...EXPONENTIATION)
|
56
|
+
break
|
57
|
+
case SUGGAR.INTEGER_DEVISION:
|
58
|
+
exp.length = 0
|
59
|
+
exp.push(...INTEGER_DIVISION)
|
60
|
+
break
|
61
|
+
}
|
62
|
+
}
|
63
|
+
break
|
64
|
+
case ATOM:
|
65
|
+
break
|
66
|
+
case APPLY:
|
67
|
+
{
|
68
|
+
switch (first[VALUE]) {
|
69
|
+
case KEYWORDS.BLOCK:
|
70
|
+
{
|
71
|
+
if (
|
72
|
+
prev == undefined ||
|
73
|
+
(prev &&
|
74
|
+
prev[TYPE] === APPLY &&
|
75
|
+
prev[VALUE] !== KEYWORDS.ANONYMOUS_FUNCTION)
|
76
|
+
) {
|
77
|
+
exp[0][VALUE] = KEYWORDS.CALL_FUNCTION
|
78
|
+
exp[0][TYPE] = APPLY
|
79
|
+
exp.length = 1
|
80
|
+
exp[1] = [
|
81
|
+
[APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
|
82
|
+
[[APPLY, KEYWORDS.BLOCK], ...rest]
|
83
|
+
]
|
84
|
+
deSuggarAst(exp)
|
85
|
+
}
|
86
|
+
}
|
87
|
+
break
|
88
|
+
case SUGGAR.PIPE:
|
89
|
+
{
|
90
|
+
if (rest.length < 1)
|
91
|
+
throw new RangeError(
|
92
|
+
`Invalid number of arguments to (${
|
93
|
+
SUGGAR.PIPE
|
94
|
+
}) (>= 1 required). (${SUGGAR.PIPE} ${stringifyArgs(
|
95
|
+
rest
|
96
|
+
)})`
|
97
|
+
)
|
98
|
+
let inp = rest[0]
|
99
|
+
exp.length = 0
|
100
|
+
for (let i = 1; i < rest.length; ++i) {
|
101
|
+
if (!rest[i].length || rest[i][0][TYPE] !== APPLY)
|
102
|
+
throw new TypeError(
|
103
|
+
`Argument at position (${i}) of (${
|
104
|
+
SUGGAR.PIPE
|
105
|
+
}) is not an invoked (${
|
106
|
+
KEYWORDS.ANONYMOUS_FUNCTION
|
107
|
+
}). (${SUGGAR.PIPE} ${stringifyArgs(rest)})`
|
108
|
+
)
|
109
|
+
inp = [rest[i].shift(), inp, ...rest[i]]
|
110
|
+
}
|
111
|
+
for (let i = 0; i < inp.length; ++i) exp[i] = inp[i]
|
112
|
+
deSuggarAst(exp)
|
113
|
+
}
|
114
|
+
break
|
115
|
+
case SUGGAR.CONDITION:
|
116
|
+
{
|
117
|
+
if (rest.length < 2)
|
118
|
+
throw new RangeError(
|
119
|
+
`Invalid number of arguments for (${
|
120
|
+
SUGGAR.CONDITION
|
121
|
+
}), expected (> 2 required) but got ${rest.length} (${
|
122
|
+
SUGGAR.CONDITION
|
123
|
+
} ${stringifyArgs(rest)})`
|
124
|
+
)
|
125
|
+
if (rest.length % 2 !== 0)
|
126
|
+
throw new RangeError(
|
127
|
+
`Invalid number of arguments for (${
|
128
|
+
SUGGAR.CONDITION
|
129
|
+
}), expected even number of arguments but got ${
|
130
|
+
rest.length
|
131
|
+
} (${SUGGAR.CONDITION} ${stringifyArgs(rest)})`
|
132
|
+
)
|
133
|
+
exp.length = 0
|
134
|
+
let temp = exp
|
135
|
+
for (let i = 0; i < rest.length; i += 2) {
|
136
|
+
if (i === rest.length - 2) {
|
137
|
+
temp.push([APPLY, KEYWORDS.IF], rest[i], rest.at(-1))
|
138
|
+
} else {
|
139
|
+
temp.push([APPLY, KEYWORDS.IF], rest[i], rest[i + 1], [])
|
140
|
+
temp = temp.at(-1)
|
141
|
+
}
|
142
|
+
}
|
143
|
+
deSuggarAst(exp)
|
144
|
+
}
|
145
|
+
break
|
146
|
+
case SUGGAR.LIST_TYPE:
|
147
|
+
{
|
148
|
+
exp.length = 0
|
149
|
+
let temp = exp
|
150
|
+
for (const item of rest) {
|
151
|
+
temp.push([APPLY, KEYWORDS.ARRAY_TYPE], item, [])
|
152
|
+
temp = temp.at(-1)
|
153
|
+
}
|
154
|
+
temp.push([APPLY, KEYWORDS.ARRAY_TYPE])
|
155
|
+
}
|
156
|
+
deSuggarAst(exp)
|
157
|
+
break
|
158
|
+
case SUGGAR.INTEGER_DEVISION:
|
159
|
+
{
|
160
|
+
if (rest.length !== 2)
|
161
|
+
throw new RangeError(
|
162
|
+
`Invalid number of arguments for (${
|
163
|
+
SUGGAR.INTEGER_DEVISION
|
164
|
+
}), expected (= 2) but got ${rest.length} (${
|
165
|
+
SUGGAR.INTEGER_DEVISION
|
166
|
+
} ${stringifyArgs(rest)})`
|
167
|
+
)
|
168
|
+
else if (rest.some((x) => x[TYPE] === APPLY)) {
|
169
|
+
exp.length = 0
|
170
|
+
exp.push(
|
171
|
+
[0, KEYWORDS.CALL_FUNCTION],
|
172
|
+
INTEGER_DIVISION,
|
173
|
+
...rest
|
174
|
+
)
|
175
|
+
} else {
|
176
|
+
exp.length = 1
|
177
|
+
exp[0] = [APPLY, KEYWORDS.BITWISE_OR]
|
178
|
+
exp.push([[APPLY, KEYWORDS.DIVISION], ...rest])
|
179
|
+
exp.push([ATOM, 0])
|
180
|
+
}
|
181
|
+
}
|
182
|
+
break
|
183
|
+
case SUGGAR.POWER:
|
184
|
+
{
|
185
|
+
if (rest.length !== 2)
|
186
|
+
throw new RangeError(
|
187
|
+
`Invalid number of arguments for (${
|
188
|
+
SUGGAR.POWER
|
189
|
+
}), expected (= 2) but got ${rest.length} (${
|
190
|
+
SUGGAR.POWER
|
191
|
+
} ${stringifyArgs(rest)})`
|
192
|
+
)
|
193
|
+
const isExponentAtom = exp[1][TYPE] === ATOM
|
194
|
+
const isPowerAtom = exp[2][TYPE] === ATOM
|
195
|
+
const isExponentWord = exp[1][TYPE] === WORD
|
196
|
+
if ((isExponentWord || isExponentAtom) && isPowerAtom) {
|
197
|
+
if (isExponentAtom) {
|
198
|
+
exp[0][VALUE] = KEYWORDS.MULTIPLICATION
|
199
|
+
const exponent = exp[1]
|
200
|
+
const power = exp[2][VALUE]
|
201
|
+
exp.length = 1
|
202
|
+
exp.push(exponent, [ATOM, exponent[VALUE] ** (power - 1)])
|
203
|
+
} else if (isExponentWord) {
|
204
|
+
const exponent = exp[1]
|
205
|
+
const power = exp[2]
|
206
|
+
exp.length = 0
|
207
|
+
exp.push(
|
208
|
+
[0, KEYWORDS.CALL_FUNCTION],
|
209
|
+
EXPONENTIATION,
|
210
|
+
exponent,
|
211
|
+
power
|
212
|
+
)
|
213
|
+
}
|
214
|
+
} else {
|
215
|
+
const exponent = exp[1]
|
216
|
+
const power = exp[2]
|
217
|
+
exp.length = 0
|
218
|
+
exp.push(
|
219
|
+
[0, KEYWORDS.CALL_FUNCTION],
|
220
|
+
EXPONENTIATION,
|
221
|
+
exponent,
|
222
|
+
power
|
223
|
+
)
|
224
|
+
}
|
225
|
+
deSuggarAst(exp)
|
226
|
+
}
|
227
|
+
break
|
228
|
+
case KEYWORDS.MULTIPLICATION:
|
229
|
+
if (!rest.length) {
|
230
|
+
exp[0][TYPE] = ATOM
|
231
|
+
exp[0][VALUE] = TRUE
|
232
|
+
} else if (rest.length > 2) {
|
233
|
+
exp.length = 0
|
234
|
+
let temp = exp
|
235
|
+
for (let i = 0; i < rest.length; i += 1) {
|
236
|
+
if (i < rest.length - 1) {
|
237
|
+
temp.push([APPLY, KEYWORDS.MULTIPLICATION], rest[i], [])
|
238
|
+
temp = temp.at(-1)
|
239
|
+
} else {
|
240
|
+
temp.push(...rest[i])
|
241
|
+
}
|
242
|
+
}
|
243
|
+
deSuggarAst(exp)
|
244
|
+
}
|
245
|
+
break
|
246
|
+
case KEYWORDS.ADDITION:
|
247
|
+
if (!rest.length) {
|
248
|
+
exp[0][TYPE] = ATOM
|
249
|
+
exp[0][VALUE] = FALSE
|
250
|
+
} else if (rest.length > 2) {
|
251
|
+
exp.length = 0
|
252
|
+
let temp = exp
|
253
|
+
for (let i = 0; i < rest.length; i += 1) {
|
254
|
+
if (i < rest.length - 1) {
|
255
|
+
temp.push([APPLY, KEYWORDS.ADDITION], rest[i], [])
|
256
|
+
temp = temp.at(-1)
|
257
|
+
} else {
|
258
|
+
temp.push(...rest[i])
|
259
|
+
}
|
260
|
+
}
|
261
|
+
deSuggarAst(exp)
|
262
|
+
}
|
263
|
+
break
|
264
|
+
case KEYWORDS.DIVISION:
|
265
|
+
if (!rest.length) {
|
266
|
+
exp[0][TYPE] = ATOM
|
267
|
+
exp[0][VALUE] = FALSE
|
268
|
+
} else if (rest.length === 1) {
|
269
|
+
exp.length = 1
|
270
|
+
exp.push([ATOM, 1], rest[0])
|
271
|
+
} else if (rest.length > 2) {
|
272
|
+
exp.length = 0
|
273
|
+
let temp = exp
|
274
|
+
for (let i = 0; i < rest.length; i += 1) {
|
275
|
+
if (i < rest.length - 1) {
|
276
|
+
temp.push([APPLY, KEYWORDS.DIVISION], rest[i], [])
|
277
|
+
temp = temp.at(-1)
|
278
|
+
} else {
|
279
|
+
temp.push(...rest[i])
|
280
|
+
}
|
281
|
+
}
|
282
|
+
deSuggarAst(exp)
|
283
|
+
}
|
284
|
+
break
|
285
|
+
case KEYWORDS.AND:
|
286
|
+
if (!rest.length) {
|
287
|
+
exp[0][TYPE] = ATOM
|
288
|
+
exp[0][VALUE] = FALSE
|
289
|
+
} else if (rest.length > 2) {
|
290
|
+
exp.length = 0
|
291
|
+
let temp = exp
|
292
|
+
for (let i = 0; i < rest.length; i += 1) {
|
293
|
+
if (i < rest.length - 1) {
|
294
|
+
temp.push([APPLY, KEYWORDS.AND], rest[i], [])
|
295
|
+
temp = temp.at(-1)
|
296
|
+
} else {
|
297
|
+
temp.push(...rest[i])
|
298
|
+
}
|
299
|
+
}
|
300
|
+
deSuggarAst(exp)
|
301
|
+
}
|
302
|
+
break
|
303
|
+
case KEYWORDS.OR:
|
304
|
+
if (!rest.length) {
|
305
|
+
exp[0][TYPE] = ATOM
|
306
|
+
exp[0][VALUE] = FALSE
|
307
|
+
} else if (rest.length > 2) {
|
308
|
+
exp.length = 0
|
309
|
+
let temp = exp
|
310
|
+
for (let i = 0; i < rest.length; i += 1) {
|
311
|
+
if (i < rest.length - 1) {
|
312
|
+
temp.push([APPLY, KEYWORDS.OR], rest[i], [])
|
313
|
+
temp = temp.at(-1)
|
314
|
+
} else {
|
315
|
+
temp.push(...rest[i])
|
316
|
+
}
|
317
|
+
}
|
318
|
+
deSuggarAst(exp)
|
319
|
+
}
|
320
|
+
break
|
321
|
+
case SUGGAR.UNLESS:
|
322
|
+
{
|
323
|
+
if (rest.length > 3 || rest.length < 2)
|
324
|
+
throw new RangeError(
|
325
|
+
`Invalid number of arguments for (${
|
326
|
+
SUGGAR.UNLESS
|
327
|
+
}), expected (or (= 3) (= 2)) but got ${rest.length} (${
|
328
|
+
SUGGAR.UNLESS
|
329
|
+
} ${stringifyArgs(rest)})`
|
330
|
+
)
|
331
|
+
exp[0][VALUE] = KEYWORDS.IF
|
332
|
+
const temp = exp[2]
|
333
|
+
exp[2] = exp[3] ?? [ATOM, FALSE]
|
334
|
+
exp[3] = temp
|
335
|
+
}
|
336
|
+
deSuggarAst(exp)
|
337
|
+
break
|
338
|
+
case SUGGAR.REMAINDER_OF_DIVISION_1:
|
339
|
+
{
|
340
|
+
if (rest.length !== 2)
|
341
|
+
throw new RangeError(
|
342
|
+
`Invalid number of arguments for (${
|
343
|
+
exp[0][1]
|
344
|
+
}), expected (= 2) but got ${rest.length} (${
|
345
|
+
exp[0][1]
|
346
|
+
} ${stringifyArgs(rest)})`
|
347
|
+
)
|
348
|
+
exp[0][VALUE] = KEYWORDS.REMAINDER_OF_DIVISION
|
349
|
+
deSuggarAst(exp)
|
350
|
+
}
|
351
|
+
break
|
352
|
+
case SUGGAR.NOT_EQUAL_1:
|
353
|
+
case SUGGAR.NOT_EQUAL_2:
|
354
|
+
{
|
355
|
+
if (rest.length !== 2)
|
356
|
+
throw new RangeError(
|
357
|
+
`Invalid number of arguments for (${
|
358
|
+
exp[0][1]
|
359
|
+
}), expected (= 2) but got ${rest.length} (${
|
360
|
+
exp[0][1]
|
361
|
+
} ${stringifyArgs(rest)})`
|
362
|
+
)
|
363
|
+
exp[0][VALUE] = KEYWORDS.NOT
|
364
|
+
exp[1] = [[APPLY, KEYWORDS.EQUAL], exp[1], exp[2]]
|
365
|
+
exp.length = 2
|
366
|
+
deSuggarAst(exp)
|
367
|
+
}
|
368
|
+
break
|
369
|
+
}
|
370
|
+
prev = first
|
371
|
+
}
|
372
|
+
break
|
373
|
+
default:
|
374
|
+
for (const e of exp) evaluate(e)
|
375
|
+
break
|
376
|
+
}
|
377
|
+
for (const r of rest) evaluate(r)
|
378
|
+
}
|
379
|
+
}
|
380
|
+
evaluate(ast)
|
381
|
+
return ast
|
382
|
+
}
|
383
|
+
export const replaceStrings = (source) => {
|
384
|
+
// const quotes = source.match(/"(.*?)"/g)
|
385
|
+
const quotes = source.match(/"(?:.*?(\n|\r))*?.*?"/g)
|
386
|
+
// TODO handle escaping
|
387
|
+
if (quotes)
|
388
|
+
for (const q of quotes)
|
389
|
+
source = source.replaceAll(
|
390
|
+
q,
|
391
|
+
`(${KEYWORDS.ARRAY_TYPE} ${[...q.replaceAll('\r', '')]
|
392
|
+
.slice(1, -1)
|
393
|
+
.map((x) => x.charCodeAt(0))
|
394
|
+
.join(' ')})`
|
395
|
+
)
|
396
|
+
return source
|
397
|
+
}
|
398
|
+
export const replaceQuotes = (source) =>
|
399
|
+
source
|
400
|
+
.replaceAll(/\'\(/g, `(${KEYWORDS.ARRAY_TYPE} `)
|
401
|
+
.replaceAll(/\`\(/g, `(${SUGGAR.LIST_TYPE} `)
|
402
|
+
.replaceAll(/\(\)/g, `(${KEYWORDS.ARRAY_TYPE})`)
|
403
|
+
export const deSuggarSource = (source) => replaceQuotes(replaceStrings(source))
|
404
|
+
export const handleUnbalancedQuotes = (source) => {
|
405
|
+
const diff = (source.match(/\"/g) ?? []).length % 2
|
406
|
+
if (diff !== 0) throw new SyntaxError(`Quotes are unbalanced "`)
|
407
|
+
return source
|
408
|
+
}
|