fez-lisp 1.3.9 → 1.3.10
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/README.md +1 -64
- package/lib/baked/std.js +1 -1
- package/package.json +1 -1
- package/src/evaluator.js +0 -2
- package/src/parser.js +138 -138
- package/src/utils.js +29 -46
package/package.json
CHANGED
package/src/evaluator.js
CHANGED
package/src/parser.js
CHANGED
@@ -1,138 +1,138 @@
|
|
1
|
-
import { APPLY, ATOM, TYPE, WORD, VALUE } from './keywords.js'
|
2
|
-
export const leaf = (type, value) => [type, value]
|
3
|
-
export const isLeaf = ([car]) => car === APPLY || car === ATOM || car === WORD
|
4
|
-
export const LISP = {
|
5
|
-
parse: (source) => {
|
6
|
-
const tree = []
|
7
|
-
const stack = [tree]
|
8
|
-
let head = tree
|
9
|
-
let acc = ''
|
10
|
-
for (let i = 0; i < source.length; ++i) {
|
11
|
-
const cursor = source[i]
|
12
|
-
if (cursor === '(') {
|
13
|
-
const temp = []
|
14
|
-
head.push(temp)
|
15
|
-
stack.push(head)
|
16
|
-
head = temp
|
17
|
-
} else if (cursor === ')' || cursor === ' ') {
|
18
|
-
let token = acc
|
19
|
-
acc = ''
|
20
|
-
if (token) {
|
21
|
-
if (!head.length) head.push(leaf(APPLY, token))
|
22
|
-
else if (token.match(/^-?[0-9]\d*(\.\d+)?$/))
|
23
|
-
head.push(leaf(ATOM, Number(token)))
|
24
|
-
else head.push(leaf(WORD, token))
|
25
|
-
}
|
26
|
-
if (cursor === ')') head = stack.pop()
|
27
|
-
} else acc += cursor
|
28
|
-
}
|
29
|
-
return tree
|
30
|
-
},
|
31
|
-
stringify: (array) => {
|
32
|
-
if (array == undefined) return '()'
|
33
|
-
else if (typeof array === 'object')
|
34
|
-
if (Array.isArray(array))
|
35
|
-
return array.length
|
36
|
-
? `(array ${array.map(LISP.stringify).join(' ')})`
|
37
|
-
: '()'
|
38
|
-
else
|
39
|
-
return `(array ${array
|
40
|
-
.map(([key, value]) => `("${key}" ${LISP.stringify(value)})`)
|
41
|
-
.join(' ')})`
|
42
|
-
else if (typeof array === 'function') return '()'
|
43
|
-
else if (typeof array === 'boolean') return +array
|
44
|
-
else return array
|
45
|
-
},
|
46
|
-
source: (ast) => {
|
47
|
-
const dfs = (exp) => {
|
48
|
-
let out = ''
|
49
|
-
const [first, ...rest] = isLeaf(exp) ? [exp] : exp
|
50
|
-
if (first == undefined) return (out += '()')
|
51
|
-
switch (first[TYPE]) {
|
52
|
-
case WORD:
|
53
|
-
out += first[VALUE]
|
54
|
-
break
|
55
|
-
case ATOM:
|
56
|
-
out += first[VALUE]
|
57
|
-
break
|
58
|
-
case APPLY:
|
59
|
-
out += `(${first[VALUE]} ${rest.map(dfs).join(' ')})`
|
60
|
-
break
|
61
|
-
}
|
62
|
-
return out
|
63
|
-
}
|
64
|
-
return ast.map(dfs).join(' ')
|
65
|
-
}
|
66
|
-
}
|
67
|
-
export const AST = {
|
68
|
-
parse: (source) => {
|
69
|
-
const tree = []
|
70
|
-
const stack = [tree]
|
71
|
-
let head = tree
|
72
|
-
let acc = ''
|
73
|
-
for (let i = 0; i < source.length; ++i) {
|
74
|
-
const cursor = source[i]
|
75
|
-
if (cursor === '"') {
|
76
|
-
acc += '"'
|
77
|
-
++i
|
78
|
-
while (source[i] !== '"') {
|
79
|
-
acc += source[i]
|
80
|
-
++i
|
81
|
-
}
|
82
|
-
}
|
83
|
-
if (cursor === '[') {
|
84
|
-
const temp = []
|
85
|
-
head.push(temp)
|
86
|
-
stack.push(head)
|
87
|
-
head = temp
|
88
|
-
} else if (cursor === ']' || cursor === ',') {
|
89
|
-
let token = acc
|
90
|
-
acc = ''
|
91
|
-
if (token) {
|
92
|
-
if (!head.length) head.push(Number(token))
|
93
|
-
else if (token[0] === '"' && token[token.length - 1] === '"')
|
94
|
-
head.push(token.substring(1, token.length - 1))
|
95
|
-
else head.push(Number(token))
|
96
|
-
}
|
97
|
-
if (cursor === ']') head = stack.pop()
|
98
|
-
} else acc += cursor
|
99
|
-
}
|
100
|
-
return tree
|
101
|
-
},
|
102
|
-
stringify: (ast) =>
|
103
|
-
typeof ast === 'object'
|
104
|
-
? `[${ast.map(AST.stringify).join(',')}]`
|
105
|
-
: typeof ast === 'string'
|
106
|
-
? `"${ast}"`
|
107
|
-
: ast,
|
108
|
-
struct: (ast) => {
|
109
|
-
const dfs = (exp) => {
|
110
|
-
let out = ''
|
111
|
-
const [first, ...rest] = isLeaf(exp) ? [exp] : exp
|
112
|
-
if (first == undefined)
|
113
|
-
return (out +=
|
114
|
-
'(Expression::Apply(vec![Expression::Word("array".to_string())]))')
|
115
|
-
switch (first[TYPE]) {
|
116
|
-
case WORD:
|
117
|
-
out += `Expression::Word("${first[VALUE]}".to_string())`
|
118
|
-
break
|
119
|
-
case ATOM:
|
120
|
-
out += `Expression::Atom(${
|
121
|
-
Number.isInteger(first[VALUE])
|
122
|
-
? `${first[VALUE]}.0`
|
123
|
-
: first[VALUE].toString()
|
124
|
-
})`
|
125
|
-
break
|
126
|
-
case APPLY:
|
127
|
-
out += `Expression::Apply(vec![Expression::Word("${
|
128
|
-
first[VALUE]
|
129
|
-
}".to_string()),${rest.map(dfs).join(',')}])`
|
130
|
-
break
|
131
|
-
}
|
132
|
-
return out
|
133
|
-
}
|
134
|
-
return `Expression::Apply(vec![Expression::Word("do".to_string()),${ast
|
135
|
-
.map(dfs)
|
136
|
-
.join(',')}])`
|
137
|
-
}
|
138
|
-
}
|
1
|
+
import { APPLY, ATOM, TYPE, WORD, VALUE } from './keywords.js'
|
2
|
+
export const leaf = (type, value) => [type, value]
|
3
|
+
export const isLeaf = ([car]) => car === APPLY || car === ATOM || car === WORD
|
4
|
+
export const LISP = {
|
5
|
+
parse: (source) => {
|
6
|
+
const tree = []
|
7
|
+
const stack = [tree]
|
8
|
+
let head = tree
|
9
|
+
let acc = ''
|
10
|
+
for (let i = 0; i < source.length; ++i) {
|
11
|
+
const cursor = source[i]
|
12
|
+
if (cursor === '(') {
|
13
|
+
const temp = []
|
14
|
+
head.push(temp)
|
15
|
+
stack.push(head)
|
16
|
+
head = temp
|
17
|
+
} else if (cursor === ')' || cursor === ' ') {
|
18
|
+
let token = acc
|
19
|
+
acc = ''
|
20
|
+
if (token) {
|
21
|
+
if (!head.length) head.push(leaf(APPLY, token))
|
22
|
+
else if (token.match(/^-?[0-9]\d*(\.\d+)?$/))
|
23
|
+
head.push(leaf(ATOM, Number(token)))
|
24
|
+
else head.push(leaf(WORD, token))
|
25
|
+
}
|
26
|
+
if (cursor === ')') head = stack.pop()
|
27
|
+
} else acc += cursor
|
28
|
+
}
|
29
|
+
return tree
|
30
|
+
},
|
31
|
+
stringify: (array) => {
|
32
|
+
if (array == undefined) return '()'
|
33
|
+
else if (typeof array === 'object')
|
34
|
+
if (Array.isArray(array))
|
35
|
+
return array.length
|
36
|
+
? `(array ${array.map(LISP.stringify).join(' ')})`
|
37
|
+
: '()'
|
38
|
+
else
|
39
|
+
return `(array ${array
|
40
|
+
.map(([key, value]) => `("${key}" ${LISP.stringify(value)})`)
|
41
|
+
.join(' ')})`
|
42
|
+
else if (typeof array === 'function') return '()'
|
43
|
+
else if (typeof array === 'boolean') return +array
|
44
|
+
else return array
|
45
|
+
},
|
46
|
+
source: (ast) => {
|
47
|
+
const dfs = (exp) => {
|
48
|
+
let out = ''
|
49
|
+
const [first, ...rest] = isLeaf(exp) ? [exp] : exp
|
50
|
+
if (first == undefined) return (out += '()')
|
51
|
+
switch (first[TYPE]) {
|
52
|
+
case WORD:
|
53
|
+
out += first[VALUE]
|
54
|
+
break
|
55
|
+
case ATOM:
|
56
|
+
out += first[VALUE]
|
57
|
+
break
|
58
|
+
case APPLY:
|
59
|
+
out += `(${first[VALUE]} ${rest.map(dfs).join(' ')})`
|
60
|
+
break
|
61
|
+
}
|
62
|
+
return out
|
63
|
+
}
|
64
|
+
return ast.map(dfs).join(' ')
|
65
|
+
}
|
66
|
+
}
|
67
|
+
export const AST = {
|
68
|
+
parse: (source) => {
|
69
|
+
const tree = []
|
70
|
+
const stack = [tree]
|
71
|
+
let head = tree
|
72
|
+
let acc = ''
|
73
|
+
for (let i = 0; i < source.length; ++i) {
|
74
|
+
const cursor = source[i]
|
75
|
+
if (cursor === '"') {
|
76
|
+
acc += '"'
|
77
|
+
++i
|
78
|
+
while (source[i] !== '"') {
|
79
|
+
acc += source[i]
|
80
|
+
++i
|
81
|
+
}
|
82
|
+
}
|
83
|
+
if (cursor === '[') {
|
84
|
+
const temp = []
|
85
|
+
head.push(temp)
|
86
|
+
stack.push(head)
|
87
|
+
head = temp
|
88
|
+
} else if (cursor === ']' || cursor === ',') {
|
89
|
+
let token = acc
|
90
|
+
acc = ''
|
91
|
+
if (token) {
|
92
|
+
if (!head.length) head.push(Number(token))
|
93
|
+
else if (token[0] === '"' && token[token.length - 1] === '"')
|
94
|
+
head.push(token.substring(1, token.length - 1))
|
95
|
+
else head.push(Number(token))
|
96
|
+
}
|
97
|
+
if (cursor === ']') head = stack.pop()
|
98
|
+
} else acc += cursor
|
99
|
+
}
|
100
|
+
return tree
|
101
|
+
},
|
102
|
+
stringify: (ast) =>
|
103
|
+
typeof ast === 'object'
|
104
|
+
? `[${ast.map(AST.stringify).join(',')}]`
|
105
|
+
: typeof ast === 'string'
|
106
|
+
? `"${ast}"`
|
107
|
+
: ast,
|
108
|
+
struct: (ast) => {
|
109
|
+
const dfs = (exp) => {
|
110
|
+
let out = ''
|
111
|
+
const [first, ...rest] = isLeaf(exp) ? [exp] : exp
|
112
|
+
if (first == undefined)
|
113
|
+
return (out +=
|
114
|
+
'(Expression::Apply(vec![Expression::Word("array".to_string())]))')
|
115
|
+
switch (first[TYPE]) {
|
116
|
+
case WORD:
|
117
|
+
out += `Expression::Word("${first[VALUE]}".to_string())`
|
118
|
+
break
|
119
|
+
case ATOM:
|
120
|
+
out += `Expression::Atom(${
|
121
|
+
Number.isInteger(first[VALUE])
|
122
|
+
? `${first[VALUE]}.0`
|
123
|
+
: first[VALUE].toString()
|
124
|
+
})`
|
125
|
+
break
|
126
|
+
case APPLY:
|
127
|
+
out += `Expression::Apply(vec![Expression::Word("${
|
128
|
+
first[VALUE]
|
129
|
+
}".to_string()),${rest.map(dfs).join(',')}])`
|
130
|
+
break
|
131
|
+
}
|
132
|
+
return out
|
133
|
+
}
|
134
|
+
return `Expression::Apply(vec![Expression::Word("do".to_string()),${ast
|
135
|
+
.map(dfs)
|
136
|
+
.join(',')}])`
|
137
|
+
}
|
138
|
+
}
|
package/src/utils.js
CHANGED
@@ -1,13 +1,14 @@
|
|
1
1
|
import std from '../lib/baked/std.js'
|
2
2
|
import { comp, OPTIMIZATIONS } from './compiler.js'
|
3
3
|
import { APPLY, ATOM, KEYWORDS, TYPE, VALUE, WORD } from './keywords.js'
|
4
|
-
import { evaluate
|
4
|
+
import { evaluate } from './evaluator.js'
|
5
5
|
import { AST, isLeaf, LISP } from './parser.js'
|
6
6
|
import {
|
7
7
|
deSuggarAst,
|
8
8
|
deSuggarSource,
|
9
9
|
handleUnbalancedQuotes
|
10
10
|
} from './macros.js'
|
11
|
+
import { keywords } from './interpreter.js'
|
11
12
|
export const logError = (error) =>
|
12
13
|
console.log('\x1b[31m', `\n${error}\n`, '\x1b[0m')
|
13
14
|
export const logSuccess = (output) => console.log(output, '\x1b[0m')
|
@@ -212,10 +213,17 @@ export const dfs = (tree, callback) => {
|
|
212
213
|
if (!isLeaf(tree)) for (const leaf of tree) dfs(leaf)
|
213
214
|
else callback(tree)
|
214
215
|
}
|
216
|
+
export const wrapInBlock = (ast) => [
|
217
|
+
[APPLY, KEYWORDS.CALL_FUNCTION],
|
218
|
+
[
|
219
|
+
[APPLY, KEYWORDS.ANONYMOUS_FUNCTION],
|
220
|
+
[[APPLY, KEYWORDS.BLOCK], ...ast]
|
221
|
+
]
|
222
|
+
]
|
215
223
|
export const interpret = (ast, keywords) =>
|
216
224
|
ast.reduce((_, x) => evaluate(x, keywords), 0)
|
217
225
|
export const fez = (source, options = {}) => {
|
218
|
-
const env =
|
226
|
+
const env = { ...keywords }
|
219
227
|
try {
|
220
228
|
if (typeof source === 'string') {
|
221
229
|
source = deSuggarSource(source)
|
@@ -225,16 +233,16 @@ export const fez = (source, options = {}) => {
|
|
225
233
|
const code = !options.mutation ? removeMutation(valid) : valid
|
226
234
|
if (!code.length && options.throw) throw new Error('Nothing to parse!')
|
227
235
|
const parsed = deSuggarAst(LISP.parse(code))
|
228
|
-
const ast =
|
236
|
+
const ast = wrapInBlock(shake(parsed, std))
|
229
237
|
// if (options.check) typeCheck(ast)
|
230
238
|
if (options.compile) return comp(ast)
|
231
|
-
return
|
239
|
+
return evaluate(ast, env)
|
232
240
|
} else if (Array.isArray(source)) {
|
233
241
|
const ast = !options.mutation
|
234
242
|
? AST.parse(AST.stringify(source).replace(new RegExp(/!/g), 'ǃ'))
|
235
243
|
: source
|
236
244
|
if (options.compile) return comp(ast)
|
237
|
-
return
|
245
|
+
return evaluate(ast, env)
|
238
246
|
} else {
|
239
247
|
throw new Error('Source has to be either a lisp source code or an AST')
|
240
248
|
}
|
@@ -279,7 +287,7 @@ export const decompress = (raw) => {
|
|
279
287
|
return result
|
280
288
|
}
|
281
289
|
// shake(LISP.parse(removeNoCode(source)), std)
|
282
|
-
export const shake = (parsed, std) =>
|
290
|
+
export const shake = (parsed, std) => treeShake(parsed, std).concat(parsed)
|
283
291
|
export const tree = (source, std) =>
|
284
292
|
std
|
285
293
|
? shake(LISP.parse(deSuggarSource(removeNoCode(source))), std)
|
@@ -288,45 +296,20 @@ export const minify = (source) =>
|
|
288
296
|
LISP.source(deSuggarAst(LISP.parse(deSuggarSource(removeNoCode(source)))))
|
289
297
|
export const prep = (source) =>
|
290
298
|
deSuggarAst(LISP.parse(removeNoCode(deSuggarSource(source))))
|
291
|
-
export const src = (source, deps) =>
|
292
|
-
source
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
])
|
300
|
-
}
|
301
|
-
export const ast = (source, deps) => {
|
302
|
-
source = prep(source)
|
303
|
-
return [
|
304
|
-
...treeShake(
|
305
|
-
source,
|
306
|
-
deps.reduce((a, b) => a.concat(b), [])
|
307
|
-
),
|
308
|
-
...source
|
309
|
-
]
|
310
|
-
}
|
311
|
-
export const astWithStd = (source) => {
|
312
|
-
const parsed = prep(source)
|
313
|
-
return [...treeShake(parsed, std), ...parsed]
|
314
|
-
}
|
315
|
-
export const dependencies = (source, deps) => {
|
316
|
-
source = prep(source)
|
317
|
-
return shakedList(
|
318
|
-
source,
|
319
|
-
deps.reduce((a, b) => a.concat(b), [])
|
299
|
+
export const src = (source, deps) =>
|
300
|
+
LISP.source(
|
301
|
+
wrapInBlock(
|
302
|
+
shake(
|
303
|
+
prep(source),
|
304
|
+
deps.reduce((a, b) => a.concat(b), [])
|
305
|
+
)
|
306
|
+
)
|
320
307
|
)
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
...treeShake(
|
326
|
-
source,
|
308
|
+
export const ast = (source, deps) =>
|
309
|
+
wrapInBlock(
|
310
|
+
shake(
|
311
|
+
prep(source),
|
327
312
|
deps.reduce((a, b) => a.concat(b), [])
|
328
|
-
)
|
329
|
-
|
330
|
-
|
331
|
-
return `${top}${program}`
|
332
|
-
}
|
313
|
+
)
|
314
|
+
)
|
315
|
+
export const astWithStd = (source) => wrapInBlock(shake(prep(source), std))
|