fez-lisp 1.0.38 → 1.0.39
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 -1
- package/index.js +2 -2
- package/package.json +1 -1
- package/src/formatter.js +2 -2
- package/src/parser.js +47 -45
- package/src/tokeniser.js +6 -4
- package/src/utils.js +6 -19
package/README.md
CHANGED
package/index.js
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
import { evaluate } from './src/interpreter.js'
|
2
|
-
import {
|
2
|
+
import { LISP, AST } from './src/parser.js'
|
3
3
|
import { fez, tree } from './src/utils.js'
|
4
4
|
import std from './lib/baked/std.js'
|
5
5
|
import { keywords } from './src/tokeniser.js'
|
6
6
|
import { WORD, APPLY, ATOM, VALUE, TYPE } from './src/enums.js'
|
7
7
|
const types = { WORD, APPLY, ATOM, VALUE, TYPE }
|
8
|
-
export { fez,
|
8
|
+
export { fez, keywords, evaluate, std, types, tree, LISP, AST }
|
package/package.json
CHANGED
package/src/formatter.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
import { APPLY, ATOM, KEYWORDS, TYPE, VALUE, WORD } from './enums.js'
|
2
|
-
import { isLeaf,
|
2
|
+
import { isLeaf, LISP } from './parser.js'
|
3
3
|
import { removeNoCode } from './utils.js'
|
4
4
|
const tops = []
|
5
5
|
const indent = (level) => ' '.repeat(level)
|
@@ -70,7 +70,7 @@ export const formatWithPreservedComments = (source) => {
|
|
70
70
|
)
|
71
71
|
.join('\n')
|
72
72
|
|
73
|
-
return format(parse(removeNoCode(value)))
|
73
|
+
return format(LISP.parse(removeNoCode(value)))
|
74
74
|
.split('\n')
|
75
75
|
.map((x) => {
|
76
76
|
if (x.includes(commentIdentifier)) {
|
package/src/parser.js
CHANGED
@@ -2,55 +2,57 @@ import { APPLY, ATOM, WORD } from './enums.js'
|
|
2
2
|
import { escape } from './utils.js'
|
3
3
|
export const leaf = (type, value) => [type, value]
|
4
4
|
export const isLeaf = ([car]) => car === APPLY || car === ATOM || car === WORD
|
5
|
-
export const
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
while (source[i] !== '"') {
|
16
|
-
if (source[i] === '\\') acc += escape(source[++i])
|
17
|
-
else acc += source[i]
|
5
|
+
export const LISP = {
|
6
|
+
parse: (source) => {
|
7
|
+
const tree = []
|
8
|
+
let head = tree,
|
9
|
+
stack = [tree],
|
10
|
+
acc = ''
|
11
|
+
for (let i = 0; i < source.length; ++i) {
|
12
|
+
const cursor = source[i]
|
13
|
+
if (cursor === '"') {
|
14
|
+
acc += '"'
|
18
15
|
++i
|
16
|
+
while (source[i] !== '"') {
|
17
|
+
if (source[i] === '\\') acc += escape(source[++i])
|
18
|
+
else acc += source[i]
|
19
|
+
++i
|
20
|
+
}
|
19
21
|
}
|
22
|
+
if (cursor === '(') {
|
23
|
+
head.push([])
|
24
|
+
stack.push(head)
|
25
|
+
head = head.at(-1)
|
26
|
+
} else if (cursor === ')' || cursor === ' ') {
|
27
|
+
let token = acc
|
28
|
+
acc = ''
|
29
|
+
if (token) {
|
30
|
+
if (!head.length) head.push(leaf(APPLY, token))
|
31
|
+
else if (token.match(/^"([^"]*)"/))
|
32
|
+
head.push(leaf(ATOM, token.substring(1, token.length - 1)))
|
33
|
+
else if (token.match(/^-?[0-9]\d*(\.\d+)?$/))
|
34
|
+
head.push(leaf(ATOM, Number(token)))
|
35
|
+
else head.push(leaf(WORD, token))
|
36
|
+
}
|
37
|
+
if (cursor === ')') head = stack.pop()
|
38
|
+
} else acc += cursor
|
20
39
|
}
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
if (cursor === ')') head = stack.pop()
|
37
|
-
} else acc += cursor
|
40
|
+
return tree
|
41
|
+
},
|
42
|
+
stringify: (ast) => {
|
43
|
+
if (ast == undefined) return '()'
|
44
|
+
else if (typeof ast === 'object')
|
45
|
+
if (Array.isArray(ast))
|
46
|
+
return ast.length ? `(array ${ast.map(stringify).join(' ')})` : '()'
|
47
|
+
else
|
48
|
+
return `(array ${ast
|
49
|
+
.map(([key, value]) => `("${key}" ${stringify(value)})`)
|
50
|
+
.join(' ')})`
|
51
|
+
else if (typeof ast === 'string') return `"${ast}"`
|
52
|
+
else if (typeof ast === 'function') return '()'
|
53
|
+
else if (typeof ast === 'boolean') return +ast
|
54
|
+
else return ast
|
38
55
|
}
|
39
|
-
return tree
|
40
|
-
}
|
41
|
-
export const stringify = (ast) => {
|
42
|
-
if (ast == undefined) return '()'
|
43
|
-
else if (typeof ast === 'object')
|
44
|
-
if (Array.isArray(ast))
|
45
|
-
return ast.length ? `(array ${ast.map(stringify).join(' ')})` : '()'
|
46
|
-
else
|
47
|
-
return `(array ${ast
|
48
|
-
.map(([key, value]) => `("${key}" ${stringify(value)})`)
|
49
|
-
.join(' ')})`
|
50
|
-
else if (typeof ast === 'string') return `"${ast}"`
|
51
|
-
else if (typeof ast === 'function') return '()'
|
52
|
-
else if (typeof ast === 'boolean') return +ast
|
53
|
-
else return ast
|
54
56
|
}
|
55
57
|
export const AST = {
|
56
58
|
parse: (source) => {
|
package/src/tokeniser.js
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import std from '../lib/baked/std.js'
|
2
2
|
import { TYPE, VALUE, WORD, KEYWORDS, APPLY } from './enums.js'
|
3
3
|
import { evaluate, isAtom } from './interpreter.js'
|
4
|
-
import {
|
4
|
+
import { LISP } from './parser.js'
|
5
5
|
import {
|
6
6
|
isEqual,
|
7
7
|
isEqualTypes,
|
@@ -990,14 +990,16 @@ const keywords = {
|
|
990
990
|
? console.log(
|
991
991
|
'\x1b[31m',
|
992
992
|
`${describe} Failed:\n`,
|
993
|
-
`${rest[0]} => ${stringify(rest[1])} != ${stringify(
|
993
|
+
`${rest[0]} => ${LISP.stringify(rest[1])} != ${LISP.stringify(
|
994
|
+
rest[2]
|
995
|
+
)}`,
|
994
996
|
'\n',
|
995
997
|
'\x1b[0m'
|
996
998
|
)
|
997
999
|
: console.log(
|
998
1000
|
'\x1b[32m',
|
999
1001
|
`${describe} Passed:\n`,
|
1000
|
-
`${rest[0]} => ${stringify(rest[1])}`,
|
1002
|
+
`${rest[0]} => ${LISP.stringify(rest[1])}`,
|
1001
1003
|
'\n',
|
1002
1004
|
'\x1b[0m'
|
1003
1005
|
)
|
@@ -1017,7 +1019,7 @@ const keywords = {
|
|
1017
1019
|
`Invalid number of arguments for (${KEYWORDS.SERIALISE})`
|
1018
1020
|
)
|
1019
1021
|
const data = evaluate(args[0], env)
|
1020
|
-
return stringify(data)
|
1022
|
+
return LISP.stringify(data)
|
1021
1023
|
},
|
1022
1024
|
[KEYWORDS.SET_ARRAY]: (args, env) => {
|
1023
1025
|
if (args.length !== 2 && args.length !== 3)
|
package/src/utils.js
CHANGED
@@ -2,7 +2,7 @@ import std from '../lib/baked/std.js'
|
|
2
2
|
import { comp } from './compiler.js'
|
3
3
|
import { APPLY, KEYWORDS, TYPE, VALUE, WORD } from './enums.js'
|
4
4
|
import { run } from './interpreter.js'
|
5
|
-
import { AST, isLeaf,
|
5
|
+
import { AST, isLeaf, LISP } from './parser.js'
|
6
6
|
export const logError = (error) => console.log('\x1b[31m', error, '\x1b[0m')
|
7
7
|
export const logSuccess = (output) => console.log(output, '\x1b[0m')
|
8
8
|
export const removeNoCode = (source) =>
|
@@ -142,21 +142,6 @@ export const treeShake = (ast, libs) => {
|
|
142
142
|
// Filter out libraries that are not in the visited set
|
143
143
|
return libs.filter((x) => visited.has(x.at(1)[VALUE]))
|
144
144
|
}
|
145
|
-
|
146
|
-
export const runFromCompiled = (source) => {
|
147
|
-
const tree = parse(
|
148
|
-
handleUnbalancedQuotes(handleUnbalancedParens(removeNoCode(source)))
|
149
|
-
)
|
150
|
-
const compiled = comp(tree)
|
151
|
-
const JavaScript = `${compiled.top}${compiled.program}`
|
152
|
-
return eval(JavaScript)
|
153
|
-
}
|
154
|
-
export const runFromInterpreted = (source, env = {}) => {
|
155
|
-
const tree = parse(
|
156
|
-
handleUnbalancedQuotes(handleUnbalancedParens(removeNoCode(source)))
|
157
|
-
)
|
158
|
-
run(tree, env)
|
159
|
-
}
|
160
145
|
export const dfs = (tree, callback) => {
|
161
146
|
if (!isLeaf(tree)) for (const leaf of tree) dfs(leaf)
|
162
147
|
else callback(tree)
|
@@ -174,7 +159,7 @@ export const fez = (source, options = {}) => {
|
|
174
159
|
else code = removeNoCode(source)
|
175
160
|
if (!options.mutation) code = removeMutation(code)
|
176
161
|
if (!code.length && options.throw) throw new Error('Nothing to parse!')
|
177
|
-
const parsed = parse(code)
|
162
|
+
const parsed = LISP.parse(code)
|
178
163
|
if (parsed.length === 0 && options.throw)
|
179
164
|
throw new Error(
|
180
165
|
'Top level expressions need to be wrapped in a (do) block'
|
@@ -292,10 +277,12 @@ export const decompress = (raw) => {
|
|
292
277
|
for (const tok of runes) result += tok
|
293
278
|
return result
|
294
279
|
}
|
295
|
-
// shake(parse(removeNoCode(source)), std)
|
280
|
+
// shake(LISP.parse(removeNoCode(source)), std)
|
296
281
|
export const shake = (parsed, std) => [...treeShake(parsed, std), ...parsed]
|
297
282
|
export const tree = (source, std) =>
|
298
|
-
std
|
283
|
+
std
|
284
|
+
? shake(LISP.parse(removeNoCode(source)), std)
|
285
|
+
: LISP.parse(removeNoCode(source))
|
299
286
|
export const lispToJavaScriptVariableName = (name) =>
|
300
287
|
toCamelCase(
|
301
288
|
arrowFromTo(
|