Improve traverse (#2782)

improve travers
This commit is contained in:
Kurt Hutten
2024-06-25 12:46:40 +10:00
committed by GitHub
parent 496398de52
commit 24516cdb2d
2 changed files with 136 additions and 34 deletions

View File

@ -1,4 +1,4 @@
import { parse, recast, initPromise } from './wasm' import { parse, recast, initPromise, PathToNode } from './wasm'
import { import {
findAllPreviousVariables, findAllPreviousVariables,
isNodeSafeToReplace, isNodeSafeToReplace,
@ -9,6 +9,7 @@ import {
findUsesOfTagInPipe, findUsesOfTagInPipe,
hasSketchPipeBeenExtruded, hasSketchPipeBeenExtruded,
hasExtrudableGeometry, hasExtrudableGeometry,
traverse,
} from './queryAst' } from './queryAst'
import { enginelessExecutor } from '../lib/testHelpers' import { enginelessExecutor } from '../lib/testHelpers'
import { import {
@ -538,3 +539,53 @@ const extrude001 = extrude(10, sketch001)
expect(extrudable).toBeFalsy() expect(extrudable).toBeFalsy()
}) })
}) })
describe.only('Testing traverse and pathToNode', () => {
it.each([
['basic', '2.73'],
[
'very nested, array, object, callExpression, array, memberExpression',
'.yo',
],
])('testing %s', async (testName, literalOfInterest) => {
const code = `const myVar = 5
const sketch001 = startSketchOn('XZ')
|> startProfileAt([3.29, 7.86], %)
|> line([2.48, 2.44], %)
|> line([-3.86, -2.73], %)
|> line([-17.67, 0.85], %)
|> close(%)
const bing = { yo: 55 }
const myNestedVar = [
{
prop: line([bing.yo, 21], sketch001)
}
]
`
const ast = parse(code)
if (err(ast)) throw ast
let pathToNode: PathToNode = []
traverse(ast, {
enter: (node, path) => {
if (
node.type === 'Literal' &&
String(node.value) === literalOfInterest
) {
pathToNode = path
} else if (
node.type === 'Identifier' &&
literalOfInterest.includes(node.name)
) {
pathToNode = path
}
},
})
const literalIndex = code.indexOf(literalOfInterest)
const pathToNode2 = getNodePathFromSourceRange(ast, [
literalIndex + 2,
literalIndex + 2,
])
expect(pathToNode).toEqual(pathToNode2)
})
})

View File

@ -270,6 +270,18 @@ function moreNodePathFromSourceRange(
} }
} }
} }
if (_node.type === 'MemberExpression' && isInRange) {
const { object, property } = _node
if (object.start <= start && object.end >= end) {
path.push(['object', 'MemberExpression'])
return moreNodePathFromSourceRange(object, sourceRange, path)
}
if (property.start <= start && property.end >= end) {
path.push(['property', 'MemberExpression'])
return moreNodePathFromSourceRange(property, sourceRange, path)
}
return path
}
if (_node.type === 'PipeSubstitution' && isInRange) return path if (_node.type === 'PipeSubstitution' && isInRange) return path
console.error('not implemented: ' + node.type) console.error('not implemented: ' + node.type)
return path return path
@ -307,48 +319,87 @@ type KCLNode =
| ReturnStatement | ReturnStatement
export function traverse( export function traverse(
node: KCLNode, node: KCLNode | Program,
option: { option: {
enter?: (node: KCLNode) => void enter?: (node: KCLNode, pathToNode: PathToNode) => void
leave?: (node: KCLNode) => void leave?: (node: KCLNode) => void
} },
pathToNode: PathToNode = []
) { ) {
option?.enter?.(node) const _node = node as KCLNode
const _traverse = (node: KCLNode) => traverse(node, option) option?.enter?.(_node, pathToNode)
const _traverse = (node: KCLNode, pathToNode: PathToNode) =>
traverse(node, option, pathToNode)
if (node.type === 'VariableDeclaration') { if (_node.type === 'VariableDeclaration') {
node.declarations.forEach(_traverse) _node.declarations.forEach((declaration, index) =>
} else if (node.type === 'VariableDeclarator') { _traverse(declaration, [
_traverse(node.init) ...pathToNode,
} else if (node.type === 'PipeExpression') { ['declarations', 'VariableDeclaration'],
node.body.forEach(_traverse) [index, 'index'],
} else if (node.type === 'CallExpression') { ])
_traverse(node.callee) )
node.arguments.forEach(_traverse) } else if (_node.type === 'VariableDeclarator') {
} else if (node.type === 'BinaryExpression') { _traverse(_node.init, [...pathToNode, ['init', '']])
_traverse(node.left) } else if (_node.type === 'PipeExpression') {
_traverse(node.right) _node.body.forEach((expression, index) =>
} else if (node.type === 'Identifier') { _traverse(expression, [
...pathToNode,
['body', 'PipeExpression'],
[index, 'index'],
])
)
} else if (_node.type === 'CallExpression') {
_traverse(_node.callee, [...pathToNode, ['callee', 'CallExpression']])
_node.arguments.forEach((arg, index) =>
_traverse(arg, [
...pathToNode,
['arguments', 'CallExpression'],
[index, 'index'],
])
)
} else if (_node.type === 'BinaryExpression') {
_traverse(_node.left, [...pathToNode, ['left', 'BinaryExpression']])
_traverse(_node.right, [...pathToNode, ['right', 'BinaryExpression']])
} else if (_node.type === 'Identifier') {
// do nothing // do nothing
} else if (node.type === 'Literal') { } else if (_node.type === 'Literal') {
// do nothing // do nothing
} else if (node.type === 'ArrayExpression') { } else if (_node.type === 'ArrayExpression') {
node.elements.forEach(_traverse) _node.elements.forEach((el, index) =>
} else if (node.type === 'ObjectExpression') { _traverse(el, [
node.properties.forEach(({ key, value }) => { ...pathToNode,
_traverse(key) ['elements', 'ArrayExpression'],
_traverse(value) [index, 'index'],
])
)
} else if (_node.type === 'ObjectExpression') {
_node.properties.forEach(({ key, value }, index) => {
_traverse(key, [
...pathToNode,
['properties', 'ObjectExpression'],
[index, 'index'],
['key', 'Property'],
])
_traverse(value, [
...pathToNode,
['properties', 'ObjectExpression'],
[index, 'index'],
['value', 'Property'],
])
}) })
} else if (node.type === 'UnaryExpression') { } else if (_node.type === 'UnaryExpression') {
_traverse(node.argument) _traverse(_node.argument, [...pathToNode, ['argument', 'UnaryExpression']])
} else if (node.type === 'MemberExpression') { } else if (_node.type === 'MemberExpression') {
// hmm this smell // hmm this smell
_traverse(node.object) _traverse(_node.object, [...pathToNode, ['object', 'MemberExpression']])
_traverse(node.property) _traverse(_node.property, [...pathToNode, ['property', 'MemberExpression']])
} else if ('body' in node && Array.isArray(node.body)) { } else if ('body' in _node && Array.isArray(_node.body)) {
node.body.forEach(_traverse) _node.body.forEach((expression, index) =>
_traverse(expression, [...pathToNode, ['body', ''], [index, 'index']])
)
} }
option?.leave?.(node) option?.leave?.(_node)
} }
export interface PrevVariable<T> { export interface PrevVariable<T> {