fez-lisp 1.0.38 → 1.0.40
Sign up to get free protection for your applications and to get access to all the features.
- 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 +49 -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,59 @@ 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
|
-
|
37
|
-
|
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
|
47
|
+
? `(array ${ast.map(LISP.stringify).join(' ')})`
|
48
|
+
: '()'
|
49
|
+
else
|
50
|
+
return `(array ${ast
|
51
|
+
.map(([key, value]) => `("${key}" ${LISP.stringify(value)})`)
|
52
|
+
.join(' ')})`
|
53
|
+
else if (typeof ast === 'string') return `"${ast}"`
|
54
|
+
else if (typeof ast === 'function') return '()'
|
55
|
+
else if (typeof ast === 'boolean') return +ast
|
56
|
+
else return ast
|
38
57
|
}
|
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
58
|
}
|
55
59
|
export const AST = {
|
56
60
|
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(
|