focus on extrude literal when extruding sketch

This commit is contained in:
Kurt Hutten Irev-Dev
2023-01-13 17:58:37 +11:00
parent bd9dab8a29
commit 9ec332d681
6 changed files with 163 additions and 94 deletions

View File

@ -62,8 +62,11 @@ export const Toolbar = () => {
onClick={() => {
if (!ast) return
const pathToNode = getNodePathFromSourceRange(ast, selectionRange)
const { modifiedAst } = extrudeSketch(ast, pathToNode)
updateAst(modifiedAst)
const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
ast,
pathToNode
)
updateAst(modifiedAst, pathToExtrudeArg)
}}
className="border m-1 px-1 rounded"
>
@ -73,8 +76,12 @@ export const Toolbar = () => {
onClick={() => {
if (!ast) return
const pathToNode = getNodePathFromSourceRange(ast, selectionRange)
const { modifiedAst } = extrudeSketch(ast, pathToNode, false)
updateAst(modifiedAst)
const { modifiedAst, pathToExtrudeArg } = extrudeSketch(
ast,
pathToNode,
false
)
updateAst(modifiedAst, pathToExtrudeArg)
}}
className="border m-1 px-1 rounded"
>

View File

@ -55,7 +55,10 @@ function MovingSphere({
const { originalXY } = useMemo(() => {
if (ast) {
const thePath = getNodePathFromSourceRange(ast, sourceRange)
const callExpression = getNodeFromPath(ast, thePath) as CallExpression
const { node: callExpression } = getNodeFromPath<CallExpression>(
ast,
thePath
)
const [xArg, yArg] = callExpression?.arguments || []
const x = xArg?.type === 'Literal' ? xArg.value : -1
const y = yArg?.type === 'Literal' ? yArg.value : -1

View File

@ -1,82 +1,82 @@
import { PathToNode } from './executor'
import { Token } from './tokeniser'
type syntaxType =
| 'Program'
| 'ExpressionStatement'
| 'BinaryExpression'
| 'NumberLiteral'
| 'StringLiteral'
| 'CallExpression'
| 'Identifier'
| 'BlockStatement'
| 'IfStatement'
| 'WhileStatement'
| 'FunctionDeclaration'
| 'ReturnStatement'
| 'VariableDeclaration'
| 'VariableDeclarator'
| 'AssignmentExpression'
| 'UnaryExpression'
| 'MemberExpression'
| 'ArrayExpression'
| 'ObjectExpression'
| 'ObjectProperty'
| 'Property'
| 'LogicalExpression'
| 'ConditionalExpression'
| 'ForStatement'
| 'ForInStatement'
| 'ForOfStatement'
| 'BreakStatement'
| 'ContinueStatement'
| 'SwitchStatement'
| 'SwitchCase'
| 'ThrowStatement'
| 'TryStatement'
| 'CatchClause'
| 'ClassDeclaration'
| 'ClassBody'
| 'MethodDefinition'
| 'NewExpression'
| 'ThisExpression'
| 'UpdateExpression'
// | "ArrowFunctionExpression"
| 'FunctionExpression'
| 'SketchExpression'
| 'PipeExpression'
| 'PipeSubstitution'
| 'YieldExpression'
| 'AwaitExpression'
| 'ImportDeclaration'
| 'ImportSpecifier'
| 'ImportDefaultSpecifier'
| 'ImportNamespaceSpecifier'
| 'ExportNamedDeclaration'
| 'ExportDefaultDeclaration'
| 'ExportAllDeclaration'
| 'ExportSpecifier'
| 'TaggedTemplateExpression'
| 'TemplateLiteral'
| 'TemplateElement'
| 'SpreadElement'
| 'RestElement'
| 'SequenceExpression'
| 'DebuggerStatement'
| 'LabeledStatement'
| 'DoWhileStatement'
| 'WithStatement'
| 'EmptyStatement'
| 'Literal'
| 'ArrayPattern'
| 'ObjectPattern'
| 'AssignmentPattern'
| 'MetaProperty'
| 'Super'
| 'Import'
| 'RegExpLiteral'
| 'BooleanLiteral'
| 'NullLiteral'
| 'TypeAnnotation'
// | 'NumberLiteral'
// | 'StringLiteral'
// | 'IfStatement'
// | 'WhileStatement'
// | 'FunctionDeclaration'
// | 'AssignmentExpression'
// | 'UnaryExpression'
// | 'Property'
// | 'LogicalExpression'
// | 'ConditionalExpression'
// | 'ForStatement'
// | 'ForInStatement'
// | 'ForOfStatement'
// | 'BreakStatement'
// | 'ContinueStatement'
// | 'SwitchStatement'
// | 'SwitchCase'
// | 'ThrowStatement'
// | 'TryStatement'
// | 'CatchClause'
// | 'ClassDeclaration'
// | 'ClassBody'
// | 'MethodDefinition'
// | 'NewExpression'
// | 'ThisExpression'
// | 'UpdateExpression'
// | 'YieldExpression'
// | 'AwaitExpression'
// | 'ImportDeclaration'
// | 'ImportSpecifier'
// | 'ImportDefaultSpecifier'
// | 'ImportNamespaceSpecifier'
// | 'ExportNamedDeclaration'
// | 'ExportDefaultDeclaration'
// | 'ExportAllDeclaration'
// | 'ExportSpecifier'
// | 'TaggedTemplateExpression'
// | 'TemplateLiteral'
// | 'TemplateElement'
// | 'SpreadElement'
// | 'RestElement'
// | 'SequenceExpression'
// | 'DebuggerStatement'
// | 'LabeledStatement'
// | 'DoWhileStatement'
// | 'WithStatement'
// | 'EmptyStatement'
// | 'ArrayPattern'
// | 'ObjectPattern'
// | 'AssignmentPattern'
// | 'MetaProperty'
// | 'Super'
// | 'Import'
// | 'RegExpLiteral'
// | 'BooleanLiteral'
// | 'NullLiteral'
// | 'TypeAnnotation'
export interface Program {
type: syntaxType
@ -1345,27 +1345,37 @@ function debuggerr(tokens: Token[], indexes: number[], msg = ''): string {
return debugResult
}
export function getNodeFromPath(
export function getNodeFromPath<T>(
node: Program,
path: (string | number)[],
stopAt: string = '',
returnEarly = false
) {
): {
node: T
path: PathToNode
} {
let currentNode = node as any
let stopAtNode = null
let successfulPaths: (string | number)[] = []
let successfulPaths: PathToNode = []
let pathsExplored: PathToNode = []
for (const pathItem of path) {
try {
if (typeof currentNode[pathItem] !== 'object')
throw new Error('not an object')
currentNode = currentNode[pathItem]
successfulPaths.push(pathItem)
if (!stopAtNode) {
pathsExplored.push(pathItem)
}
if (currentNode.type === stopAt) {
// it will match the deepest node of the type
// instead of returning at the first match
stopAtNode = currentNode
if (returnEarly) {
return stopAtNode
return {
node: stopAtNode,
path: pathsExplored,
}
}
}
} catch (e) {
@ -1378,7 +1388,10 @@ export function getNodeFromPath(
)
}
}
return stopAtNode || currentNode
return {
node: stopAtNode || currentNode,
path: pathsExplored,
}
}
type Path = (string | number)[]

View File

@ -21,7 +21,7 @@ describe('testing getNodePathFromSourceRange', () => {
const ast = abstractSyntaxTree(lexer(code))
const nodePath = getNodePathFromSourceRange(ast, sourceRange)
const node = getNodeFromPath(ast, nodePath)
const { node } = getNodeFromPath<any>(ast, nodePath)
expect([node.start, node.end]).toEqual(sourceRange)
expect(node.type).toBe('CallExpression')

View File

@ -202,11 +202,11 @@ export function addLine(
): { modifiedAst: Program; pathToNode: (string | number)[] } {
const _node = { ...node }
const dumbyStartend = { start: 0, end: 0 }
const sketchExpression = getNodeFromPath(
const { node: sketchExpression } = getNodeFromPath<SketchExpression>(
_node,
pathToNode,
'SketchExpression'
) as SketchExpression
)
const line: ExpressionStatement = {
type: 'ExpressionStatement',
...dumbyStartend,
@ -251,7 +251,10 @@ export function changeArguments(
const _node = { ...node }
const dumbyStartend = { start: 0, end: 0 }
// const thePath = getNodePathFromSourceRange(_node, sourceRange)
const callExpression = getNodeFromPath(_node, pathToNode) as CallExpression
const { node: callExpression } = getNodeFromPath<CallExpression>(
_node,
pathToNode
)
const newXArg: CallExpression['arguments'][number] =
callExpression.arguments[0].type === 'Literal'
? {
@ -285,24 +288,29 @@ export function extrudeSketch(
node: Program,
pathToNode: (string | number)[],
shouldPipe = true
): { modifiedAst: Program; pathToNode: (string | number)[] } {
): {
modifiedAst: Program
pathToNode: PathToNode
pathToExtrudeArg: PathToNode
} {
const _node = { ...node }
const dumbyStartend = { start: 0, end: 0 }
const sketchExpression = getNodeFromPath(
const { node: sketchExpression } = getNodeFromPath<SketchExpression>(
_node,
pathToNode,
'SketchExpression'
) as SketchExpression
)
// determine if sketchExpression is in a pipeExpression or not
const pipeExpression = getNodeFromPath(_node, pathToNode, 'PipeExpression')
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
const variableDeclorator = getNodeFromPath(
const { node: pipeExpression } = getNodeFromPath<PipeExpression>(
_node,
pathToNode,
'VariableDeclarator'
) as VariableDeclarator
'PipeExpression'
)
const isInPipeExpression = pipeExpression.type === 'PipeExpression'
const { node: variableDeclorator, path: pathToDecleration } =
getNodeFromPath<VariableDeclarator>(_node, pathToNode, 'VariableDeclarator')
const extrudeCall: CallExpression = {
type: 'CallExpression',
@ -346,10 +354,19 @@ export function extrudeSketch(
}
variableDeclorator.init = pipeChain
const pathToExtrudeArg = [
...pathToDecleration,
'init',
'body',
pipeChain.body.length - 1,
'arguments',
0,
]
return {
modifiedAst: _node,
pathToNode,
pathToExtrudeArg,
}
}
const name = findUniqueName(node, 'part')
@ -372,9 +389,19 @@ export function extrudeSketch(
}
const showCallIndex = getShowIndex(_node)
_node.body.splice(showCallIndex, 0, VariableDeclaration)
const pathToExtrudeArg = [
'body',
showCallIndex,
'declarations',
0,
'init',
'arguments',
0,
]
return {
modifiedAst: addToShow(_node, name),
pathToNode: [...pathToNode.slice(0, -1), showCallIndex],
pathToExtrudeArg,
}
}
@ -385,22 +412,27 @@ export function sketchOnExtrudedFace(
const _node = { ...node }
const dumbyStartend = { start: 0, end: 0 }
const newSketchName = findUniqueName(node, 'part')
const oldSketchName = getNodeFromPath(
const oldSketchName = getNodeFromPath<VariableDeclarator>(
_node,
pathToNode,
'VariableDeclarator',
true
).id.name
const expression = getNodeFromPath(_node, pathToNode, 'CallExpression') as
| VariableDeclarator
| CallExpression
).node.id.name
const { node: expression } = getNodeFromPath<
VariableDeclarator | CallExpression
>(_node, pathToNode, 'CallExpression')
const pathName =
expression.type === 'VariableDeclarator'
? expression.id.name
: findUniqueName(node, 'path', 2)
if (expression.type === 'CallExpression') {
const block = getNodeFromPath(_node, pathToNode, 'BlockStatement')
const { node: block } = getNodeFromPath<BlockStatement>(
_node,
pathToNode,
'BlockStatement'
)
const expressionIndex = getLastIndex(pathToNode)
if (expression.callee.name !== 'lineTo')
throw new Error('expected a lineTo call')

View File

@ -1,6 +1,10 @@
import create from 'zustand'
import { addLineHighlight, EditorView } from './editor/highlightextension'
import { Program, abstractSyntaxTree } from './lang/abstractSyntaxTree'
import {
Program,
abstractSyntaxTree,
getNodeFromPath,
} from './lang/abstractSyntaxTree'
import { ProgramMemory, Position, PathToNode, Rotation } from './lang/executor'
import { recast } from './lang/recast'
import { lexer } from './lang/tokeniser'
@ -48,7 +52,7 @@ interface StoreState {
setEditorView: (editorView: EditorView) => void
highlightRange: [number, number]
setHighlightRange: (range: Range) => void
setCursor: (cursor: number) => void
setCursor: (start: number, end?: number) => void
selectionRange: [number, number]
setSelectionRange: (range: Range) => void
guiMode: GuiModes
@ -60,7 +64,7 @@ interface StoreState {
resetLogs: () => void
ast: Program | null
setAst: (ast: Program | null) => void
updateAst: (ast: Program) => void
updateAst: (ast: Program, focusPath?: PathToNode) => void
code: string
setCode: (code: string) => void
formatCode: () => void
@ -86,11 +90,11 @@ export const useStore = create<StoreState>()((set, get) => ({
editorView.dispatch({ effects: addLineHighlight.of(highlightRange) })
}
},
setCursor: (cursor: number) => {
setCursor: (start: number, end: number = start) => {
const editorView = get().editorView
if (!editorView) return
editorView.dispatch({
selection: { anchor: cursor, head: cursor },
selection: { anchor: start, head: end },
})
},
selectionRange: [0, 0],
@ -118,9 +122,19 @@ export const useStore = create<StoreState>()((set, get) => ({
setAst: (ast) => {
set({ ast })
},
updateAst: (ast) => {
updateAst: (ast, focusPath) => {
const newCode = recast(ast)
set({ ast, code: newCode })
const astWithUpdatedSource = abstractSyntaxTree(lexer(newCode))
set({ ast: astWithUpdatedSource, code: newCode })
if (focusPath) {
const { node } = getNodeFromPath<any>(astWithUpdatedSource, focusPath)
const { start, end } = node
if (!start || !end) return
setTimeout(() => {
get().setCursor(start, end)
})
}
},
code: '',
setCode: (code) => {