add helper 'getNodePathFromSourceRange' and it's test
This commit is contained in:
@ -1263,10 +1263,115 @@ function debuggerr(tokens: Token[], indexes: number[], msg = ''): string {
|
|||||||
return debugResult
|
return debugResult
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNodeFromPath(node: Program, path: (string | number)[]) {
|
export function getNodeFromPath(node: Program, path: (string | number)[]) {
|
||||||
let currentNode = node as any
|
let currentNode = node as any
|
||||||
|
let successfulPaths: (string | number)[] = []
|
||||||
for (const pathItem of path) {
|
for (const pathItem of path) {
|
||||||
currentNode = currentNode[pathItem]
|
try {
|
||||||
|
if (typeof currentNode[pathItem] !== 'object')
|
||||||
|
throw new Error('not an object')
|
||||||
|
currentNode = currentNode[pathItem]
|
||||||
|
successfulPaths.push(pathItem)
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error(
|
||||||
|
`Could not find path ${pathItem} in node ${JSON.stringify(
|
||||||
|
currentNode,
|
||||||
|
null,
|
||||||
|
2
|
||||||
|
)}, successful path was ${successfulPaths}`
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return currentNode
|
return currentNode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Path = (string | number)[]
|
||||||
|
|
||||||
|
export function getNodePathFromSourceRange(
|
||||||
|
node: Program,
|
||||||
|
sourceRange: [number, number],
|
||||||
|
previousPath: Path = []
|
||||||
|
): Path {
|
||||||
|
const [start, end] = sourceRange
|
||||||
|
let path: Path = [...previousPath, 'body']
|
||||||
|
const _node = { ...node }
|
||||||
|
// loop over each statement in body getting the index with a for loop
|
||||||
|
for (
|
||||||
|
let statementIndex = 0;
|
||||||
|
statementIndex < _node.body.length;
|
||||||
|
statementIndex++
|
||||||
|
) {
|
||||||
|
const statement = _node.body[statementIndex]
|
||||||
|
if (statement.start <= start && statement.end >= end) {
|
||||||
|
path.push(statementIndex)
|
||||||
|
if (statement.type === 'ExpressionStatement') {
|
||||||
|
const expression = statement.expression
|
||||||
|
if (expression.start <= start && expression.end >= end) {
|
||||||
|
path.push('expression')
|
||||||
|
if (expression.type === 'CallExpression') {
|
||||||
|
const callee = expression.callee
|
||||||
|
if (callee.start <= start && callee.end >= end) {
|
||||||
|
path.push('callee')
|
||||||
|
if (callee.type === 'Identifier') {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (statement.type === 'VariableDeclaration') {
|
||||||
|
const declarations = statement.declarations
|
||||||
|
|
||||||
|
for (let decIndex = 0; decIndex < declarations.length; decIndex++) {
|
||||||
|
const declaration = declarations[decIndex]
|
||||||
|
|
||||||
|
if (declaration.start <= start && declaration.end >= end) {
|
||||||
|
path.push('declarations')
|
||||||
|
path.push(decIndex)
|
||||||
|
const init = declaration.init
|
||||||
|
if (init.start <= start && init.end >= end) {
|
||||||
|
path.push('init')
|
||||||
|
if (init.type === 'SketchExpression') {
|
||||||
|
const body = init.body
|
||||||
|
if (body.start <= start && body.end >= end) {
|
||||||
|
path.push('body')
|
||||||
|
if (body.type === 'BlockStatement') {
|
||||||
|
path = getNodePathFromSourceRange(body, sourceRange, path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (init.type === 'PipeExpression') {
|
||||||
|
const body = init.body
|
||||||
|
for (let pipeIndex = 0; pipeIndex < body.length; pipeIndex++) {
|
||||||
|
const pipe = body[pipeIndex]
|
||||||
|
if (pipe.start <= start && pipe.end >= end) {
|
||||||
|
path.push('body')
|
||||||
|
path.push(pipeIndex)
|
||||||
|
if (pipe.type === 'SketchExpression') {
|
||||||
|
const body = pipe.body
|
||||||
|
if (body.start <= start && body.end >= end) {
|
||||||
|
path.push('body')
|
||||||
|
if (body.type === 'BlockStatement') {
|
||||||
|
path = getNodePathFromSourceRange(
|
||||||
|
body,
|
||||||
|
sourceRange,
|
||||||
|
path
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (init.type === 'CallExpression') {
|
||||||
|
const callee = init.callee
|
||||||
|
if (callee.start <= start && callee.end >= end) {
|
||||||
|
path.push('callee')
|
||||||
|
if (callee.type === 'Identifier') {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
27
src/lang/getNodePathFromSourceRange.test.ts
Normal file
27
src/lang/getNodePathFromSourceRange.test.ts
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
import { getNodePathFromSourceRange } from './abstractSyntaxTree'
|
||||||
|
import { lexer } from './tokeniser'
|
||||||
|
import { abstractSyntaxTree, getNodeFromPath } from './abstractSyntaxTree'
|
||||||
|
|
||||||
|
describe('testing getNodePathFromSourceRange', () => {
|
||||||
|
it('test it gets the right path for a `lineTo` CallExpression within a SketchExpression', () => {
|
||||||
|
const code = `
|
||||||
|
const myVar = 5
|
||||||
|
sketch sk3 {
|
||||||
|
lineTo(1, 2)
|
||||||
|
path yo = lineTo(3, 4)
|
||||||
|
close()
|
||||||
|
}
|
||||||
|
`
|
||||||
|
const subStr = 'lineTo(3, 4)'
|
||||||
|
const lineToSubstringIndex = code.indexOf(subStr)
|
||||||
|
const sourceRange: [number, number] = [lineToSubstringIndex, lineToSubstringIndex + subStr.length]
|
||||||
|
|
||||||
|
const ast = abstractSyntaxTree(lexer(code))
|
||||||
|
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
|
||||||
|
const node = getNodeFromPath(ast, nodePath)
|
||||||
|
|
||||||
|
expect([node.start, node.end]).toEqual(sourceRange)
|
||||||
|
expect(node.type).toBe('CallExpression')
|
||||||
|
|
||||||
|
})
|
||||||
|
})
|
Reference in New Issue
Block a user