fez-lisp 1.1.14 → 1.1.15

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.
Files changed (3) hide show
  1. package/index.js +1 -0
  2. package/package.json +1 -1
  3. package/src/utils.js +66 -61
package/index.js CHANGED
@@ -16,6 +16,7 @@ export {
16
16
  src,
17
17
  js,
18
18
  prep,
19
+ dependencies,
19
20
  LISP,
20
21
  AST
21
22
  }
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.1.14",
5
+ "version": "1.1.15",
6
6
  "type": "module",
7
7
  "main": "index.js",
8
8
  "keywords": [
package/src/utils.js CHANGED
@@ -133,70 +133,68 @@ export const handleUnbalancedQuotes = (source) => {
133
133
  return source
134
134
  }
135
135
  export const removeMutation = (source) => source.replace(new RegExp(/!/g), 'ǃ')
136
- export const treeShake = (ast, libs) => {
137
- const deps = libs.reduce((a, x) => a.set(x.at(1)[VALUE], x), new Map())
138
- const visited = new Set()
139
- const dfs = (tree, ignore = new Set()) => {
140
- const type = tree[TYPE]
141
- const value = tree[VALUE]
142
- if (!isLeaf(tree)) {
143
- const [car, ...rest] = tree
144
- if (car == undefined) return
136
+ const isDefinition = (x) =>
137
+ x[TYPE] === APPLY &&
138
+ (x[VALUE] === KEYWORDS.DEFINE_VARIABLE ||
139
+ x[VALUE] === KEYWORDS.TAIL_CALLS_OPTIMISED_RECURSIVE_FUNCTION)
140
+ const toDeps = (libs) =>
141
+ libs.reduce(
142
+ (a, x, i) => a.set(x.at(1)[VALUE], { value: x, index: i }),
143
+ new Map()
144
+ )
145
+ const deepShake = (tree, deps, visited = new Set(), ignored = new Set()) => {
146
+ const type = tree[TYPE]
147
+ const value = tree[VALUE]
148
+ if (!isLeaf(tree)) {
149
+ const [car, ...rest] = tree
150
+ if (car == undefined) return
151
+ if (isDefinition(car)) {
145
152
  if (
146
- car[TYPE] === APPLY &&
147
- (car[VALUE] === KEYWORDS.DEFINE_VARIABLE ||
148
- car[VALUE] === KEYWORDS.TAIL_CALLS_OPTIMISED_RECURSIVE_FUNCTION)
153
+ !isLeaf(rest.at(-1)) &&
154
+ rest
155
+ .at(-1)
156
+ .some(
157
+ (x) => x[TYPE] === APPLY && x[VALUE] === KEYWORDS.ANONYMOUS_FUNCTION
158
+ )
149
159
  ) {
150
- if (
151
- !isLeaf(rest.at(-1)) &&
152
- rest
153
- .at(-1)
154
- .some(
155
- (x) =>
156
- x[TYPE] === APPLY && x[VALUE] === KEYWORDS.ANONYMOUS_FUNCTION
157
- )
158
- ) {
159
- const args = rest
160
- .at(-1)
161
- .filter(
162
- (x) =>
163
- !(
164
- x[TYPE] === APPLY &&
165
- (x[VALUE] === KEYWORDS.ANONYMOUS_FUNCTION ||
166
- x[VALUE] === KEYWORDS.IMMUTABLE_FUNCTION)
167
- )
168
- )
169
- const body = args.pop()
170
- const params = new Set(args.map((x) => x[VALUE]))
171
- dfs(body, params)
172
- } else rest.forEach((x) => dfs(x, ignore))
173
- } else tree.forEach((x) => dfs(x, ignore))
174
- } else if (
175
- (type === APPLY || type === WORD) &&
176
- deps.has(value) &&
177
- !visited.has(value) &&
178
- !ignore.has(value)
179
- ) {
180
- visited.add(value)
181
- // Recursively explore the dependencies of the current node
182
- dfs(deps.get(value), ignore)
183
- }
160
+ const args = rest.at(-1).filter((x) => !isDefinition(x))
161
+ const body = args.pop()
162
+ const params = new Set(args.map((x) => x[VALUE]))
163
+ deepShake(body, deps, visited, params)
164
+ } else rest.forEach((x) => deepShake(x, deps, visited, ignored))
165
+ } else tree.forEach((x) => deepShake(x, deps, visited, ignored))
166
+ } else if (
167
+ (type === APPLY || type === WORD) &&
168
+ deps.has(value) &&
169
+ !visited.has(value) &&
170
+ !ignored.has(value)
171
+ ) {
172
+ visited.add(value)
173
+ deepShake(deps.get(value).value, deps, visited, ignored)
184
174
  }
185
- dfs(
186
- ast,
187
- new Set(
188
- ast
189
- .filter(
190
- ([x]) =>
191
- x[TYPE] === APPLY &&
192
- (x[VALUE] === KEYWORDS.DEFINE_VARIABLE ||
193
- x[VALUE] === KEYWORDS.TAIL_CALLS_OPTIMISED_RECURSIVE_FUNCTION)
194
- )
195
- .map(([_, x]) => x[VALUE])
196
- )
197
- )
198
- // Filter out libraries that are not in the visited set
199
- return [...visited].reverse().map((x) => deps.get(x))
175
+ }
176
+ const extractDeps = (visited, deps) =>
177
+ [...visited]
178
+ .map((x) => deps.get(x))
179
+ .sort((a, b) => a.index - b.index)
180
+ .map((x) => x.value)
181
+ const toIgnore = (ast) =>
182
+ ast.filter(([x]) => isDefinition(x)).map(([_, x]) => x[VALUE])
183
+ export const treeShake = (ast, libs) => {
184
+ const deps = toDeps(libs)
185
+ const visited = new Set()
186
+ const ignored = new Set(toIgnore(ast))
187
+ deepShake(ast, deps, visited, ignored)
188
+ return extractDeps(visited, deps)
189
+ }
190
+ export const shakedList = (ast, libs) => {
191
+ const deps = toDeps(libs)
192
+ const visited = new Set()
193
+ const ignored = new Set(toIgnore(ast))
194
+ deepShake(ast, deps, visited, ignored)
195
+ const out = []
196
+ for (const [key] of deps) if (visited.has(key)) out.push(key)
197
+ return out
200
198
  }
201
199
  export const dfs = (tree, callback) => {
202
200
  if (!isLeaf(tree)) for (const leaf of tree) dfs(leaf)
@@ -323,6 +321,13 @@ export const ast = (source, deps) => {
323
321
  ...source
324
322
  ]
325
323
  }
324
+ export const dependencies = (source, deps) => {
325
+ source = prep(source)
326
+ return shakedList(
327
+ source,
328
+ deps.reduce((a, b) => a.concat(b), [])
329
+ )
330
+ }
326
331
  export const js = (source, deps) => {
327
332
  source = prep(source)
328
333
  const { top, program } = comp([