fez-lisp 1.3.0 → 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 +169 -0
- package/lib/baked/std.js +1 -1
- package/package.json +2 -3
- package/src/compiler.js +27 -24
- package/src/interpreter.js +32 -56
- package/src/keywords.js +0 -13
- package/src/macros.js +408 -0
- package/src/utils.js +13 -515
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,32 +92,23 @@ 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
|
-
and: `and=(
|
90
|
-
|
91
|
-
for(let i=0; i<args.length-1;++i){
|
92
|
-
circuit=args[i]
|
93
|
-
if(circuit) continue
|
94
|
-
else return 0
|
95
|
-
}
|
96
|
-
return args.at(-1) ? 1 : 0
|
97
|
-
}`,
|
98
|
-
or: `or=(...args)=>{
|
99
|
-
let circuit;
|
100
|
-
for(let i=0;i<args.length-1;++i) {
|
101
|
-
circuit = args[i]
|
102
|
-
if(circuit) return 1
|
103
|
-
else continue
|
104
|
-
}
|
105
|
-
return args.at(-1) ? 1 : 0
|
106
|
-
}`,
|
110
|
+
and: `and=(a, b)=>+(a&&b)`,
|
111
|
+
or: `or=(a, b)=>+(a||b)`,
|
107
112
|
log_effect: `log_effect=(msg)=>{console.log(msg);return msg}`,
|
108
113
|
log_char_effect: `log_char_effect=(msg)=>{console.log(String.fromCharCode(msg));return msg}`,
|
109
114
|
log_string_effect: `log_string_effect=(msg)=>{console.log(msg.map(x=>String.fromCharCode(x)).join(''));return msg}`,
|
@@ -278,9 +283,7 @@ const compile = (tree, Drill) => {
|
|
278
283
|
case KEYWORDS.MULTIPLICATION:
|
279
284
|
return `(${parseArgs(Arguments, Drill, token)});`
|
280
285
|
case KEYWORDS.DIVISION:
|
281
|
-
return Arguments
|
282
|
-
? `(1/${compile(Arguments[0], Drill)});`
|
283
|
-
: `(${parseArgs(Arguments, Drill, token)});`
|
286
|
+
return `(${parseArgs(Arguments, Drill, token)});`
|
284
287
|
case KEYWORDS.ADDITION:
|
285
288
|
return `(${parseArgs(Arguments, Drill, token)});`
|
286
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')
|
@@ -432,28 +418,22 @@ export const keywords = {
|
|
432
418
|
KEYWORDS.AND
|
433
419
|
} ${stringifyArgs(args)})`
|
434
420
|
)
|
435
|
-
|
436
|
-
|
437
|
-
circuit = evaluate(args[i], env)
|
438
|
-
if (circuit !== FALSE && circuit !== TRUE)
|
439
|
-
throw new TypeError(
|
440
|
-
`Condition of (${
|
441
|
-
KEYWORDS.AND
|
442
|
-
}) must be ${TRUE} or ${FALSE} but got (${
|
443
|
-
KEYWORDS.AND
|
444
|
-
} ${stringifyArgs(args)})`
|
445
|
-
)
|
446
|
-
if (circuit) continue
|
447
|
-
else return 0
|
448
|
-
}
|
449
|
-
const end = evaluate(args.at(-1), env)
|
450
|
-
if (end !== FALSE && end !== TRUE)
|
421
|
+
const a = evaluate(args[0], env)
|
422
|
+
if (a !== FALSE && a !== TRUE)
|
451
423
|
throw new TypeError(
|
452
|
-
`Condition of (${KEYWORDS.
|
453
|
-
KEYWORDS.
|
424
|
+
`Condition of (${KEYWORDS.OR}) must be ${TRUE} or ${FALSE} but got (${
|
425
|
+
KEYWORDS.OR
|
426
|
+
} ${stringifyArgs(args)})`
|
427
|
+
)
|
428
|
+
if (!a) return 0
|
429
|
+
const b = evaluate(args[1], env)
|
430
|
+
if (b !== FALSE && b !== TRUE)
|
431
|
+
throw new TypeError(
|
432
|
+
`Condition of (${KEYWORDS.OR}) must be ${TRUE} or ${FALSE} but got (${
|
433
|
+
KEYWORDS.OR
|
454
434
|
} ${stringifyArgs(args)})`
|
455
435
|
)
|
456
|
-
return
|
436
|
+
return b
|
457
437
|
},
|
458
438
|
[KEYWORDS.OR]: (args, env) => {
|
459
439
|
if (args.length !== 2)
|
@@ -462,26 +442,22 @@ export const keywords = {
|
|
462
442
|
KEYWORDS.OR
|
463
443
|
} ${stringifyArgs(args)})`
|
464
444
|
)
|
465
|
-
|
466
|
-
|
467
|
-
|
468
|
-
|
469
|
-
|
470
|
-
|
471
|
-
|
472
|
-
|
473
|
-
|
474
|
-
|
475
|
-
else continue
|
476
|
-
}
|
477
|
-
const end = evaluate(args.at(-1), env)
|
478
|
-
if (end !== FALSE && end !== TRUE)
|
445
|
+
const a = evaluate(args[0], env)
|
446
|
+
if (a !== FALSE && a !== TRUE)
|
447
|
+
throw new TypeError(
|
448
|
+
`Condition of (${KEYWORDS.OR}) must be ${TRUE} or ${FALSE} but got (${
|
449
|
+
KEYWORDS.OR
|
450
|
+
} ${stringifyArgs(args)})`
|
451
|
+
)
|
452
|
+
if (a) return 1
|
453
|
+
const b = evaluate(args[1], env)
|
454
|
+
if (b !== FALSE && b !== TRUE)
|
479
455
|
throw new TypeError(
|
480
456
|
`Condition of (${KEYWORDS.OR}) must be ${TRUE} or ${FALSE} but got (${
|
481
457
|
KEYWORDS.OR
|
482
458
|
} ${stringifyArgs(args)})`
|
483
459
|
)
|
484
|
-
return
|
460
|
+
return b
|
485
461
|
},
|
486
462
|
[KEYWORDS.CALL_FUNCTION]: (args, env) => {
|
487
463
|
if (!args.length)
|
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
|
+
}
|