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/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.9",
5
+ "version": "1.3.10",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "keywords": [
package/src/evaluator.js CHANGED
@@ -33,5 +33,3 @@ export const evaluate = (exp, env) => {
33
33
  )
34
34
  }
35
35
  }
36
- export const run = (tree, env = {}) =>
37
- keywords[KEYWORDS.BLOCK](tree, { ...keywords, ...env })
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[0]
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, run } from './evaluator.js'
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 = Object.create(null)
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 = [...treeShake(parsed, std), ...parsed]
236
+ const ast = wrapInBlock(shake(parsed, std))
229
237
  // if (options.check) typeCheck(ast)
230
238
  if (options.compile) return comp(ast)
231
- return run(ast, env)
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 run(ast, env)
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) => [...treeShake(parsed, std), ...parsed]
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 = prep(source)
293
- return LISP.source([
294
- ...treeShake(
295
- source,
296
- deps.reduce((a, b) => a.concat(b), [])
297
- ),
298
- ...source
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
- export const js = (source, deps) => {
323
- source = prep(source)
324
- const { top, program } = comp([
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
- ...source
330
- ])
331
- return `${top}${program}`
332
- }
313
+ )
314
+ )
315
+ export const astWithStd = (source) => wrapInBlock(shake(prep(source), std))