fez-lisp 1.3.8 → 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 -65
- package/lib/baked/macros.js +1 -40
- package/lib/baked/std.js +1 -1
- package/package.json +1 -1
- package/src/compiler.js +1 -6
- package/src/evaluator.js +0 -2
- package/src/interpreter.js +0 -17
- package/src/keywords.js +1 -3
- package/src/parser.js +138 -138
- package/src/utils.js +29 -46
package/package.json
CHANGED
package/src/compiler.js
CHANGED
@@ -121,8 +121,7 @@ const Helpers = {
|
|
121
121
|
__tco: `__tco=fn=>(...args)=>{let result=fn(...args);while(typeof result==='function')result=result();return result}`,
|
122
122
|
atom_predicate: `atom_predicate=(number)=>+(typeof number==='number')`,
|
123
123
|
lambda_predicate: `lambda_predicate=(fm)=>+(typeof fn==='function')`,
|
124
|
-
set_effect: `set_effect=function(array,index,value){if(arguments.length===1){array.pop()}else{array[index] = value};return array}
|
125
|
-
__error: `__error=(error)=>{throw new Error(error.map((x)=>String.fromCharCode(x)).join(''))}`
|
124
|
+
set_effect: `set_effect=function(array,index,value){if(arguments.length===1){array.pop()}else{array[index] = value};return array}`
|
126
125
|
}
|
127
126
|
const semiColumnEdgeCases = new Set([
|
128
127
|
';)',
|
@@ -313,10 +312,6 @@ const compile = (tree, Drill) => {
|
|
313
312
|
Drill
|
314
313
|
)}:${Arguments.length === 3 ? compile(Arguments[2], Drill) : 0});`
|
315
314
|
}
|
316
|
-
case KEYWORDS.THROW: {
|
317
|
-
Drill.Helpers.add('__error')
|
318
|
-
return `__error(${compile(Arguments[0], Drill)})`
|
319
|
-
}
|
320
315
|
default: {
|
321
316
|
const camelCased = lispToJavaScriptVariableName(token)
|
322
317
|
if (camelCased in Helpers) Drill.Helpers.add(camelCased)
|
package/src/evaluator.js
CHANGED
package/src/interpreter.js
CHANGED
@@ -702,23 +702,6 @@ export const keywords = {
|
|
702
702
|
)
|
703
703
|
return +(typeof evaluate(args[0], env) === 'function')
|
704
704
|
},
|
705
|
-
// Not sure about these
|
706
|
-
[KEYWORDS.THROW]: (args, env) => {
|
707
|
-
if (args.length !== 1)
|
708
|
-
throw new RangeError(
|
709
|
-
`Invalid number of arguments to (${KEYWORDS.THROW}) (= 1 required) (${
|
710
|
-
KEYWORDS.THROW
|
711
|
-
} ${stringifyArgs(args)})`
|
712
|
-
)
|
713
|
-
const expression = evaluate(args[0], env)
|
714
|
-
if (!Array.isArray(expression))
|
715
|
-
throw new TypeError(
|
716
|
-
`Argument of (${KEYWORDS.THROW}) must be an (${
|
717
|
-
RUNTIME_TYPES.ARRAY
|
718
|
-
}) but got (${expression}) (${KEYWORDS.THROW} ${stringifyArgs(args)})`
|
719
|
-
)
|
720
|
-
throw new Error(expression.map((x) => String.fromCharCode(x)).join(''))
|
721
|
-
},
|
722
705
|
[KEYWORDS.LOG]: (args, env) => {
|
723
706
|
if (args.length !== 1)
|
724
707
|
throw new RangeError(
|
package/src/keywords.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))
|