Change setupSketch to use artifacts output from executor
This commit is contained in:
@ -44,6 +44,8 @@ import {
|
||||
VariableDeclaration,
|
||||
VariableDeclarator,
|
||||
sketchGroupFromKclValue,
|
||||
ExecState,
|
||||
sketchGroupFromArtifactId,
|
||||
} from 'lang/wasm'
|
||||
import {
|
||||
engineCommandManager,
|
||||
@ -97,6 +99,7 @@ import { CSS2DObject } from 'three/examples/jsm/renderers/CSS2DRenderer'
|
||||
import {
|
||||
ArtifactGraph,
|
||||
ArtifactId,
|
||||
getPathFromSelection,
|
||||
getPlaneOrFaceFromSelection,
|
||||
} from 'lang/std/artifactGraph'
|
||||
import { SegmentInputs } from 'lang/std/stdTypes'
|
||||
@ -397,17 +400,37 @@ export class SceneEntities {
|
||||
const { truncatedAst, programMemoryOverride, variableDeclarationName } =
|
||||
prepared
|
||||
|
||||
const { programMemory } = await executeAst({
|
||||
const { execState } = await executeAst({
|
||||
ast: truncatedAst,
|
||||
useFakeExecutor: true,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
programMemoryOverride,
|
||||
})
|
||||
const sketchGroup = sketchGroupFromPathToNode({
|
||||
pathToNode: sketchPathToNode,
|
||||
ast: maybeModdedAst,
|
||||
programMemory,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
let sketchGroup: SketchGroup | null | Error = null
|
||||
if (selectionRanges) {
|
||||
console.warn('setupSketch looking for sketch from selection')
|
||||
sketchGroup = sketchGroupFromSelection({
|
||||
selections: selectionRanges,
|
||||
execState: kclManager.execState,
|
||||
// execState,
|
||||
artifactGraph: this.engineCommandManager.artifactGraph,
|
||||
})
|
||||
if (sketchGroup) {
|
||||
console.warn('setupSketch found sketch from selection')
|
||||
}
|
||||
}
|
||||
if (!sketchGroup) {
|
||||
console.warn(
|
||||
'setupSketch sketch not found from selection; falling back to program memory'
|
||||
)
|
||||
// Fall back to the sketch group from the program memory.
|
||||
sketchGroup = sketchGroupFromPathToNode({
|
||||
pathToNode: sketchPathToNode,
|
||||
ast: maybeModdedAst,
|
||||
programMemory,
|
||||
})
|
||||
}
|
||||
if (err(sketchGroup)) return Promise.reject(sketchGroup)
|
||||
if (!sketchGroup) return Promise.reject('sketchGroup not found')
|
||||
|
||||
@ -810,12 +833,13 @@ export class SceneEntities {
|
||||
updateRectangleSketch(sketchInit, x, y, tags[0])
|
||||
}
|
||||
|
||||
const { programMemory } = await executeAst({
|
||||
const { execState } = await executeAst({
|
||||
ast: truncatedAst,
|
||||
useFakeExecutor: true,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
programMemoryOverride,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
this.sceneProgramMemory = programMemory
|
||||
const sketchGroup = sketchGroupFromKclValue(
|
||||
programMemory.get(variableDeclarationName),
|
||||
@ -864,12 +888,13 @@ export class SceneEntities {
|
||||
await kclManager.executeAstMock(_ast)
|
||||
sceneInfra.modelingSend({ type: 'Finish rectangle' })
|
||||
|
||||
const { programMemory } = await executeAst({
|
||||
const { execState } = await executeAst({
|
||||
ast: _ast,
|
||||
useFakeExecutor: true,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
programMemoryOverride,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
|
||||
// Prepare to update the THREEjs scene
|
||||
this.sceneProgramMemory = programMemory
|
||||
@ -988,12 +1013,13 @@ export class SceneEntities {
|
||||
modded = moddedResult.modifiedAst
|
||||
}
|
||||
|
||||
const { programMemory } = await executeAst({
|
||||
const { execState } = await executeAst({
|
||||
ast: modded,
|
||||
useFakeExecutor: true,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
programMemoryOverride,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
this.sceneProgramMemory = programMemory
|
||||
const sketchGroup = sketchGroupFromKclValue(
|
||||
programMemory.get(variableDeclarationName),
|
||||
@ -1347,12 +1373,13 @@ export class SceneEntities {
|
||||
// don't want to mod the user's code yet as they have't committed to the change yet
|
||||
// plus this would be the truncated ast being recast, it would be wrong
|
||||
codeManager.updateCodeEditor(code)
|
||||
const { programMemory } = await executeAst({
|
||||
const { execState } = await executeAst({
|
||||
ast: truncatedAst,
|
||||
useFakeExecutor: true,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
programMemoryOverride,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
this.sceneProgramMemory = programMemory
|
||||
|
||||
const maybeSketchGroup = programMemory.get(variableDeclarationName)
|
||||
@ -1855,6 +1882,43 @@ export async function planeOrFaceFromSelection({
|
||||
return null
|
||||
}
|
||||
|
||||
export function sketchGroupFromSelection({
|
||||
selections,
|
||||
artifactGraph,
|
||||
execState,
|
||||
}: {
|
||||
selections: Selections
|
||||
artifactGraph: ArtifactGraph
|
||||
execState: ExecState
|
||||
}): SketchGroup | null {
|
||||
if (selections.codeBasedSelections.length !== 1) {
|
||||
// Give up if there isn't exactly one selection.
|
||||
console.warn(
|
||||
'sketchGroupFromSelection no single selection',
|
||||
selections.codeBasedSelections.length,
|
||||
selections
|
||||
)
|
||||
return null
|
||||
}
|
||||
const selection = selections.codeBasedSelections[0]
|
||||
const artifactId = selection.artifactId
|
||||
if (!artifactId) {
|
||||
console.warn(
|
||||
'sketchGroupFromSelection artifact ID not found',
|
||||
selections.codeBasedSelections.length,
|
||||
selections
|
||||
)
|
||||
}
|
||||
if (!artifactId) return null
|
||||
const path = getPathFromSelection(artifactId, artifactGraph)
|
||||
if (!path) {
|
||||
console.warn('sketchGroupFromSelection path not found', artifactId)
|
||||
return null
|
||||
}
|
||||
const sketch = sketchGroupFromArtifactId(execState, path.id)
|
||||
return sketch
|
||||
}
|
||||
|
||||
export function sketchGroupFromPathToNode({
|
||||
pathToNode,
|
||||
ast,
|
||||
@ -1949,6 +2013,9 @@ export async function getSketchOrientationDetails(
|
||||
|
||||
// We couldn't find the plane or face, so try to look at the AST, and find it
|
||||
// through there.
|
||||
console.warn(
|
||||
'getSketchOrientationDetails falling back to sketchGroupFromPathToNode'
|
||||
)
|
||||
const sketchGroup = sketchGroupFromPathToNode({
|
||||
pathToNode: sketchPathToNode,
|
||||
ast: kclManager.ast,
|
||||
|
||||
@ -157,7 +157,7 @@ export function useCalc({
|
||||
engineCommandManager,
|
||||
useFakeExecutor: true,
|
||||
programMemoryOverride: kclManager.programMemory.clone(),
|
||||
}).then(({ programMemory }) => {
|
||||
}).then(({ execState }) => {
|
||||
const resultDeclaration = ast.body.find(
|
||||
(a) =>
|
||||
a.type === 'VariableDeclaration' &&
|
||||
@ -166,7 +166,7 @@ export function useCalc({
|
||||
const init =
|
||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||
resultDeclaration?.declarations?.[0]?.init
|
||||
const result = programMemory?.get('__result__')?.value
|
||||
const result = execState.memory?.get('__result__')?.value
|
||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||
init && setValueNode(init)
|
||||
})
|
||||
|
||||
@ -29,8 +29,8 @@ describe('processMemory', () => {
|
||||
|> lineTo([2.15, 4.32], %)
|
||||
// |> rx(90, %)`
|
||||
const ast = parse(code)
|
||||
const programMemory = await enginelessExecutor(ast, ProgramMemory.empty())
|
||||
const output = processMemory(programMemory)
|
||||
const execState = await enginelessExecutor(ast, ProgramMemory.empty())
|
||||
const output = processMemory(execState.memory)
|
||||
expect(output.myVar).toEqual(5)
|
||||
expect(output.otherVar).toEqual(3)
|
||||
expect(output).toEqual({
|
||||
|
||||
@ -8,6 +8,8 @@ import { EXECUTE_AST_INTERRUPT_ERROR_MESSAGE } from 'lib/constants'
|
||||
|
||||
import {
|
||||
CallExpression,
|
||||
emptyExecState,
|
||||
ExecState,
|
||||
initPromise,
|
||||
parse,
|
||||
PathToNode,
|
||||
@ -19,6 +21,8 @@ import {
|
||||
import { getNodeFromPath } from './queryAst'
|
||||
import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
|
||||
import { Diagnostic } from '@codemirror/lint'
|
||||
import { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId'
|
||||
import { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
|
||||
interface ExecuteArgs {
|
||||
ast?: Program
|
||||
@ -43,6 +47,7 @@ export class KclManager {
|
||||
digest: null,
|
||||
}
|
||||
private _programMemory: ProgramMemory = ProgramMemory.empty()
|
||||
private _execState: ExecState = emptyExecState()
|
||||
private _logs: string[] = []
|
||||
private _lints: Diagnostic[] = []
|
||||
private _kclErrors: KCLError[] = []
|
||||
@ -71,11 +76,21 @@ export class KclManager {
|
||||
get programMemory() {
|
||||
return this._programMemory
|
||||
}
|
||||
set programMemory(programMemory) {
|
||||
// This is private because callers should be setting the entire execState.
|
||||
private set programMemory(programMemory) {
|
||||
this._programMemory = programMemory
|
||||
this._programMemoryCallBack(programMemory)
|
||||
}
|
||||
|
||||
set execState(execState) {
|
||||
this._execState = execState
|
||||
this.programMemory = execState.memory
|
||||
}
|
||||
|
||||
get execState() {
|
||||
return this._execState
|
||||
}
|
||||
|
||||
get logs() {
|
||||
return this._logs
|
||||
}
|
||||
@ -252,7 +267,7 @@ export class KclManager {
|
||||
// Make sure we clear before starting again. End session will do this.
|
||||
this.engineCommandManager?.endSession()
|
||||
await this.ensureWasmInit()
|
||||
const { logs, errors, programMemory, isInterrupted } = await executeAst({
|
||||
const { logs, errors, execState, isInterrupted } = await executeAst({
|
||||
ast,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
})
|
||||
@ -263,7 +278,7 @@ export class KclManager {
|
||||
this.lints = await lintAst({ ast: ast })
|
||||
|
||||
sceneInfra.modelingSend({ type: 'code edit during sketch' })
|
||||
defaultSelectionFilter(programMemory, this.engineCommandManager)
|
||||
defaultSelectionFilter(execState.memory, this.engineCommandManager)
|
||||
|
||||
if (args.zoomToFit) {
|
||||
let zoomObjectId: string | undefined = ''
|
||||
@ -296,7 +311,7 @@ export class KclManager {
|
||||
this.logs = logs
|
||||
// Do not add the errors since the program was interrupted and the error is not a real KCL error
|
||||
this.addKclErrors(isInterrupted ? [] : errors)
|
||||
this.programMemory = programMemory
|
||||
this.execState = execState
|
||||
this.ast = { ...ast }
|
||||
this._executeCallback()
|
||||
this.engineCommandManager.addCommandLog({
|
||||
@ -333,7 +348,7 @@ export class KclManager {
|
||||
await codeManager.writeToFile()
|
||||
this._ast = { ...newAst }
|
||||
|
||||
const { logs, errors, programMemory } = await executeAst({
|
||||
const { logs, errors, execState } = await executeAst({
|
||||
ast: newAst,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
useFakeExecutor: true,
|
||||
@ -341,7 +356,8 @@ export class KclManager {
|
||||
|
||||
this._logs = logs
|
||||
this._kclErrors = errors
|
||||
this._programMemory = programMemory
|
||||
this._execState = execState
|
||||
this._programMemory = execState.memory
|
||||
if (updates !== 'artifactRanges') return
|
||||
|
||||
// TODO the below seems like a work around, I wish there's a comment explaining exactly what
|
||||
|
||||
@ -445,6 +445,6 @@ async function exe(
|
||||
) {
|
||||
const ast = parse(code)
|
||||
|
||||
const result = await enginelessExecutor(ast, programMemory)
|
||||
return result
|
||||
const execState = await enginelessExecutor(ast, programMemory)
|
||||
return execState.memory
|
||||
}
|
||||
|
||||
@ -4,6 +4,8 @@ import {
|
||||
ProgramMemory,
|
||||
programMemoryInit,
|
||||
kclLint,
|
||||
emptyExecState,
|
||||
ExecState,
|
||||
} from 'lang/wasm'
|
||||
import { enginelessExecutor } from 'lib/testHelpers'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
@ -56,7 +58,7 @@ export async function executeAst({
|
||||
}): Promise<{
|
||||
logs: string[]
|
||||
errors: KCLError[]
|
||||
programMemory: ProgramMemory
|
||||
execState: ExecState
|
||||
isInterrupted: boolean
|
||||
}> {
|
||||
try {
|
||||
@ -65,7 +67,7 @@ export async function executeAst({
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.startNewSession()
|
||||
}
|
||||
const programMemory = await (useFakeExecutor
|
||||
const execState = await (useFakeExecutor
|
||||
? enginelessExecutor(ast, programMemoryOverride || programMemoryInit())
|
||||
: _executor(ast, programMemoryInit(), engineCommandManager, false))
|
||||
|
||||
@ -73,7 +75,7 @@ export async function executeAst({
|
||||
return {
|
||||
logs: [],
|
||||
errors: [],
|
||||
programMemory,
|
||||
execState,
|
||||
isInterrupted: false,
|
||||
}
|
||||
} catch (e: any) {
|
||||
@ -89,7 +91,7 @@ export async function executeAst({
|
||||
return {
|
||||
errors: [e],
|
||||
logs: [],
|
||||
programMemory: ProgramMemory.empty(),
|
||||
execState: emptyExecState(),
|
||||
isInterrupted,
|
||||
}
|
||||
} else {
|
||||
@ -97,7 +99,7 @@ export async function executeAst({
|
||||
return {
|
||||
logs: [e],
|
||||
errors: [],
|
||||
programMemory: ProgramMemory.empty(),
|
||||
execState: emptyExecState(),
|
||||
isInterrupted,
|
||||
}
|
||||
}
|
||||
|
||||
@ -220,11 +220,11 @@ const yo2 = hmm([identifierGuy + 5])`
|
||||
it('should move a binary expression into a new variable', async () => {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const startIndex = code.indexOf('100 + 100') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
ast,
|
||||
programMemory,
|
||||
execState.memory,
|
||||
[startIndex, startIndex],
|
||||
'newVar'
|
||||
)
|
||||
@ -235,11 +235,11 @@ const yo2 = hmm([identifierGuy + 5])`
|
||||
it('should move a value into a new variable', async () => {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const startIndex = code.indexOf('2.8') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
ast,
|
||||
programMemory,
|
||||
execState.memory,
|
||||
[startIndex, startIndex],
|
||||
'newVar'
|
||||
)
|
||||
@ -250,11 +250,11 @@ const yo2 = hmm([identifierGuy + 5])`
|
||||
it('should move a callExpression into a new variable', async () => {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const startIndex = code.indexOf('def(')
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
ast,
|
||||
programMemory,
|
||||
execState.memory,
|
||||
[startIndex, startIndex],
|
||||
'newVar'
|
||||
)
|
||||
@ -265,11 +265,11 @@ const yo2 = hmm([identifierGuy + 5])`
|
||||
it('should move a binary expression with call expression into a new variable', async () => {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const startIndex = code.indexOf('jkl(') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
ast,
|
||||
programMemory,
|
||||
execState.memory,
|
||||
[startIndex, startIndex],
|
||||
'newVar'
|
||||
)
|
||||
@ -280,11 +280,11 @@ const yo2 = hmm([identifierGuy + 5])`
|
||||
it('should move a identifier into a new variable', async () => {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const startIndex = code.indexOf('identifierGuy +') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
ast,
|
||||
programMemory,
|
||||
execState.memory,
|
||||
[startIndex, startIndex],
|
||||
'newVar'
|
||||
)
|
||||
@ -465,7 +465,7 @@ describe('Testing deleteSegmentFromPipeExpression', () => {
|
||||
|> line([306.21, 198.87], %)`
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const lineOfInterest = 'line([306.21, 198.85], %, $a)'
|
||||
const range: [number, number] = [
|
||||
code.indexOf(lineOfInterest),
|
||||
@ -475,7 +475,7 @@ describe('Testing deleteSegmentFromPipeExpression', () => {
|
||||
const modifiedAst = deleteSegmentFromPipeExpression(
|
||||
[],
|
||||
ast,
|
||||
programMemory,
|
||||
execState.memory,
|
||||
code,
|
||||
pathToNode
|
||||
)
|
||||
@ -543,7 +543,7 @@ ${!replace1 ? ` |> ${line}\n` : ''} |> angledLine([-65, ${
|
||||
const code = makeCode(line)
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const lineOfInterest = line
|
||||
const range: [number, number] = [
|
||||
code.indexOf(lineOfInterest),
|
||||
@ -554,7 +554,7 @@ ${!replace1 ? ` |> ${line}\n` : ''} |> angledLine([-65, ${
|
||||
const modifiedAst = deleteSegmentFromPipeExpression(
|
||||
dependentSegments,
|
||||
ast,
|
||||
programMemory,
|
||||
execState.memory,
|
||||
code,
|
||||
pathToNode
|
||||
)
|
||||
@ -632,7 +632,7 @@ describe('Testing removeSingleConstraintInfo', () => {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const lineOfInterest = expectedFinish.split('(')[0] + '('
|
||||
const range: [number, number] = [
|
||||
code.indexOf(lineOfInterest) + 1,
|
||||
@ -661,7 +661,7 @@ describe('Testing removeSingleConstraintInfo', () => {
|
||||
pathToNode,
|
||||
argPosition,
|
||||
ast,
|
||||
programMemory
|
||||
execState.memory
|
||||
)
|
||||
if (!mod) return new Error('mod is undefined')
|
||||
const recastCode = recast(mod.modifiedAst)
|
||||
@ -686,7 +686,7 @@ describe('Testing removeSingleConstraintInfo', () => {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const lineOfInterest = expectedFinish.split('(')[0] + '('
|
||||
const range: [number, number] = [
|
||||
code.indexOf(lineOfInterest) + 1,
|
||||
@ -711,7 +711,7 @@ describe('Testing removeSingleConstraintInfo', () => {
|
||||
pathToNode,
|
||||
argPosition,
|
||||
ast,
|
||||
programMemory
|
||||
execState.memory
|
||||
)
|
||||
if (!mod) return new Error('mod is undefined')
|
||||
const recastCode = recast(mod.modifiedAst)
|
||||
@ -882,7 +882,7 @@ const sketch002 = startSketchOn({
|
||||
// const lineOfInterest = 'line([-2.94, 2.7], %)'
|
||||
const ast = parse(codeBefore)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
|
||||
// deleteFromSelection
|
||||
const range: [number, number] = [
|
||||
@ -895,7 +895,7 @@ const sketch002 = startSketchOn({
|
||||
range,
|
||||
type,
|
||||
},
|
||||
programMemory,
|
||||
execState.memory,
|
||||
async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
return {
|
||||
|
||||
@ -45,11 +45,11 @@ const variableBelowShouldNotBeIncluded = 3
|
||||
const rangeStart = code.indexOf('// selection-range-7ish-before-this') - 7
|
||||
const ast = parse(code)
|
||||
if (err(ast)) throw ast
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
|
||||
const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
|
||||
ast,
|
||||
programMemory,
|
||||
execState.memory,
|
||||
[rangeStart, rangeStart]
|
||||
)
|
||||
expect(variables).toEqual([
|
||||
@ -351,11 +351,11 @@ const part001 = startSketchAt([-1.41, 3.46])
|
||||
const ast = parse(exampleCode)
|
||||
if (err(ast)) throw ast
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const result = hasExtrudeSketchGroup({
|
||||
ast,
|
||||
selection: { type: 'default', range: [100, 101] },
|
||||
programMemory,
|
||||
programMemory: execState.memory,
|
||||
})
|
||||
expect(result).toEqual(true)
|
||||
})
|
||||
@ -370,11 +370,11 @@ const part001 = startSketchAt([-1.41, 3.46])
|
||||
const ast = parse(exampleCode)
|
||||
if (err(ast)) throw ast
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const result = hasExtrudeSketchGroup({
|
||||
ast,
|
||||
selection: { type: 'default', range: [100, 101] },
|
||||
programMemory,
|
||||
programMemory: execState.memory,
|
||||
})
|
||||
expect(result).toEqual(true)
|
||||
})
|
||||
@ -383,11 +383,11 @@ const part001 = startSketchAt([-1.41, 3.46])
|
||||
const ast = parse(exampleCode)
|
||||
if (err(ast)) throw ast
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const result = hasExtrudeSketchGroup({
|
||||
ast,
|
||||
selection: { type: 'default', range: [10, 11] },
|
||||
programMemory,
|
||||
programMemory: execState.memory,
|
||||
})
|
||||
expect(result).toEqual(false)
|
||||
})
|
||||
|
||||
@ -849,3 +849,26 @@ export function getPlaneOrFaceFromSelection(
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the path from a selection.
|
||||
*/
|
||||
export function getPathFromSelection(
|
||||
id: ArtifactId,
|
||||
artifactGraph: ArtifactGraph
|
||||
): PathArtifact | null {
|
||||
const selection = artifactGraph.get(id)
|
||||
if (!selection) return null
|
||||
if (selection.type === 'solid2D') {
|
||||
const path = artifactGraph.get(selection.pathId)
|
||||
if (path?.type !== 'path') return null
|
||||
return path
|
||||
} else if (selection.type === 'wall' || selection.type === 'cap') {
|
||||
const sweep = artifactGraph.get(selection.sweepId)
|
||||
if (sweep?.type !== 'sweep') return null
|
||||
const path = artifactGraph.get(sweep.pathId)
|
||||
if (path?.type !== 'path') return null
|
||||
return path
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@ -117,11 +117,11 @@ describe('testing changeSketchArguments', () => {
|
||||
const ast = parse(code)
|
||||
if (err(ast)) return ast
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const sourceStart = code.indexOf(lineToChange)
|
||||
const changeSketchArgsRetVal = changeSketchArguments(
|
||||
ast,
|
||||
programMemory,
|
||||
execState.memory,
|
||||
{
|
||||
type: 'sourceRange',
|
||||
sourceRange: [sourceStart, sourceStart + lineToChange.length],
|
||||
@ -150,12 +150,12 @@ const mySketch001 = startSketchOn('XY')
|
||||
const ast = parse(code)
|
||||
if (err(ast)) return ast
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const sourceStart = code.indexOf(lineToChange)
|
||||
expect(sourceStart).toBe(95)
|
||||
const newSketchLnRetVal = addNewSketchLn({
|
||||
node: ast,
|
||||
programMemory,
|
||||
programMemory: execState.memory,
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
from: [0, 0],
|
||||
@ -186,7 +186,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
|
||||
const modifiedAst2 = addCloseToPipe({
|
||||
node: ast,
|
||||
programMemory,
|
||||
programMemory: execState.memory,
|
||||
pathToNode: [
|
||||
['body', ''],
|
||||
[0, 'index'],
|
||||
@ -230,7 +230,7 @@ describe('testing addTagForSketchOnFace', () => {
|
||||
const pathToNode = getNodePathFromSourceRange(ast, sourceRange)
|
||||
const sketchOnFaceRetVal = addTagForSketchOnFace(
|
||||
{
|
||||
// previousProgramMemory: programMemory, // redundant?
|
||||
// previousProgramMemory: execState.memory, // redundant?
|
||||
pathToNode,
|
||||
node: ast,
|
||||
},
|
||||
|
||||
@ -40,7 +40,7 @@ async function testingSwapSketchFnCall({
|
||||
const ast = parse(inputCode)
|
||||
if (err(ast)) return Promise.reject(ast)
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const selections = {
|
||||
codeBasedSelections: [range],
|
||||
otherSelections: [],
|
||||
@ -51,7 +51,7 @@ async function testingSwapSketchFnCall({
|
||||
return Promise.reject(new Error('transformInfos undefined'))
|
||||
const ast2 = transformAstSketchLines({
|
||||
ast,
|
||||
programMemory,
|
||||
programMemory: execState.memory,
|
||||
selectionRanges: selections,
|
||||
transformInfos,
|
||||
referenceSegName: '',
|
||||
@ -366,10 +366,10 @@ const part001 = startSketchOn('XY')
|
||||
|> line([2.14, 1.35], %) // normal-segment
|
||||
|> xLine(3.54, %)`
|
||||
it('normal case works', async () => {
|
||||
const programMemory = await enginelessExecutor(parse(code))
|
||||
const execState = await enginelessExecutor(parse(code))
|
||||
const index = code.indexOf('// normal-segment') - 7
|
||||
const sg = sketchGroupFromKclValue(
|
||||
programMemory.get('part001'),
|
||||
execState.memory.get('part001'),
|
||||
'part001'
|
||||
) as SketchGroup
|
||||
const _segment = getSketchSegmentFromSourceRange(sg, [index, index])
|
||||
@ -383,11 +383,11 @@ const part001 = startSketchOn('XY')
|
||||
})
|
||||
})
|
||||
it('verify it works when the segment is in the `start` property', async () => {
|
||||
const programMemory = await enginelessExecutor(parse(code))
|
||||
const execState = await enginelessExecutor(parse(code))
|
||||
const index = code.indexOf('// segment-in-start') - 7
|
||||
const _segment = getSketchSegmentFromSourceRange(
|
||||
sketchGroupFromKclValue(
|
||||
programMemory.get('part001'),
|
||||
execState.memory.get('part001'),
|
||||
'part001'
|
||||
) as SketchGroup,
|
||||
[index, index]
|
||||
|
||||
@ -220,7 +220,7 @@ const part001 = startSketchOn('XY')
|
||||
}
|
||||
})
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const transformInfos = getTransformInfos(
|
||||
makeSelections(selectionRanges.slice(1)),
|
||||
ast,
|
||||
@ -231,7 +231,7 @@ const part001 = startSketchOn('XY')
|
||||
ast,
|
||||
selectionRanges: makeSelections(selectionRanges),
|
||||
transformInfos,
|
||||
programMemory,
|
||||
programMemory: execState.memory,
|
||||
})
|
||||
if (err(newAst)) return Promise.reject(newAst)
|
||||
|
||||
@ -311,7 +311,7 @@ const part001 = startSketchOn('XY')
|
||||
}
|
||||
})
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const transformInfos = getTransformInfos(
|
||||
makeSelections(selectionRanges),
|
||||
ast,
|
||||
@ -322,7 +322,7 @@ const part001 = startSketchOn('XY')
|
||||
ast,
|
||||
selectionRanges: makeSelections(selectionRanges),
|
||||
transformInfos,
|
||||
programMemory,
|
||||
programMemory: execState.memory,
|
||||
referenceSegName: '',
|
||||
})
|
||||
if (err(newAst)) return Promise.reject(newAst)
|
||||
@ -373,7 +373,7 @@ const part001 = startSketchOn('XY')
|
||||
}
|
||||
})
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const transformInfos = getTransformInfos(
|
||||
makeSelections(selectionRanges),
|
||||
ast,
|
||||
@ -384,7 +384,7 @@ const part001 = startSketchOn('XY')
|
||||
ast,
|
||||
selectionRanges: makeSelections(selectionRanges),
|
||||
transformInfos,
|
||||
programMemory,
|
||||
programMemory: execState.memory,
|
||||
referenceSegName: '',
|
||||
})
|
||||
if (err(newAst)) return Promise.reject(newAst)
|
||||
@ -470,7 +470,7 @@ async function helperThing(
|
||||
}
|
||||
})
|
||||
|
||||
const programMemory = await enginelessExecutor(ast)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const transformInfos = getTransformInfos(
|
||||
makeSelections(selectionRanges.slice(1)),
|
||||
ast,
|
||||
@ -481,7 +481,7 @@ async function helperThing(
|
||||
ast,
|
||||
selectionRanges: makeSelections(selectionRanges),
|
||||
transformInfos,
|
||||
programMemory,
|
||||
programMemory: execState.memory,
|
||||
})
|
||||
|
||||
if (err(newAst)) return Promise.reject(newAst)
|
||||
|
||||
@ -17,9 +17,9 @@ describe('testing angledLineThatIntersects', () => {
|
||||
offset: ${offset},
|
||||
}, %, $yo2)
|
||||
const intersect = segEndX(yo2)`
|
||||
const mem = await enginelessExecutor(parse(code('-1')))
|
||||
expect(mem.get('intersect')?.value).toBe(1 + Math.sqrt(2))
|
||||
const execState = await enginelessExecutor(parse(code('-1')))
|
||||
expect(execState.memory.get('intersect')?.value).toBe(1 + Math.sqrt(2))
|
||||
const noOffset = await enginelessExecutor(parse(code('0')))
|
||||
expect(noOffset.get('intersect')?.value).toBeCloseTo(1)
|
||||
expect(noOffset.memory.get('intersect')?.value).toBeCloseTo(1)
|
||||
})
|
||||
})
|
||||
|
||||
@ -37,6 +37,8 @@ import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
||||
import { DeepPartial } from 'lib/types'
|
||||
import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration'
|
||||
import { SketchGroup } from '../wasm-lib/kcl/bindings/SketchGroup'
|
||||
import { ArtifactId } from 'wasm-lib/kcl/bindings/ArtifactId'
|
||||
import { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
||||
|
||||
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
||||
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
||||
@ -136,6 +138,34 @@ export const parse = (code: string | Error): Program | Error => {
|
||||
|
||||
export type PathToNode = [string | number, string][]
|
||||
|
||||
interface RawExecState {
|
||||
memory: RawProgramMemory
|
||||
artifacts: { [key: ArtifactId]: Artifact }
|
||||
}
|
||||
|
||||
export interface ExecState {
|
||||
memory: ProgramMemory
|
||||
artifacts: { [key: ArtifactId]: Artifact }
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an empty ExecState. This is useful on init to prevent needing an
|
||||
* Option.
|
||||
*/
|
||||
export function emptyExecState(): ExecState {
|
||||
return {
|
||||
memory: ProgramMemory.empty(),
|
||||
artifacts: {},
|
||||
}
|
||||
}
|
||||
|
||||
function execStateFromRaw(raw: RawExecState): ExecState {
|
||||
return {
|
||||
memory: ProgramMemory.fromRaw(raw.memory),
|
||||
artifacts: raw.artifacts,
|
||||
}
|
||||
}
|
||||
|
||||
interface Memory {
|
||||
[key: string]: KclValue
|
||||
}
|
||||
@ -353,12 +383,49 @@ export function sketchGroupFromKclValue(
|
||||
}
|
||||
}
|
||||
|
||||
export function optionalSketchGroupFromKclValue(
|
||||
value: KclValue
|
||||
): SketchGroup | null {
|
||||
if (value.type === 'UserVal' && value.value.type === 'SketchGroup')
|
||||
return value.value
|
||||
return null
|
||||
}
|
||||
|
||||
export function kclValueFromArtifactId(
|
||||
execState: ExecState,
|
||||
id: ArtifactId
|
||||
): KclValue | null {
|
||||
const artifact = execState.artifacts[id]
|
||||
if (!artifact) {
|
||||
console.warn('kclValueFromArtifactId id not found', id, execState)
|
||||
}
|
||||
if (!artifact) return null
|
||||
return artifact.value
|
||||
}
|
||||
|
||||
export function sketchGroupFromArtifactId(
|
||||
execState: ExecState,
|
||||
id: ArtifactId
|
||||
): SketchGroup | null {
|
||||
const kclValue = kclValueFromArtifactId(execState, id)
|
||||
if (!kclValue) {
|
||||
console.warn('sketchGroupFromArtifactId id not found', id)
|
||||
return null
|
||||
}
|
||||
const sketch = optionalSketchGroupFromKclValue(kclValue)
|
||||
if (!sketch) {
|
||||
console.warn('sketchGroupFromArtifactId not a SketchGroup', kclValue)
|
||||
return null
|
||||
}
|
||||
return sketch
|
||||
}
|
||||
|
||||
export const executor = async (
|
||||
node: Program,
|
||||
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
||||
engineCommandManager: EngineCommandManager,
|
||||
isMock: boolean = false
|
||||
): Promise<ProgramMemory> => {
|
||||
): Promise<ExecState> => {
|
||||
if (err(programMemory)) return Promise.reject(programMemory)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
@ -380,7 +447,7 @@ export const _executor = async (
|
||||
programMemory: ProgramMemory | Error = ProgramMemory.empty(),
|
||||
engineCommandManager: EngineCommandManager,
|
||||
isMock: boolean
|
||||
): Promise<ProgramMemory> => {
|
||||
): Promise<ExecState> => {
|
||||
if (err(programMemory)) return Promise.reject(programMemory)
|
||||
|
||||
try {
|
||||
@ -392,7 +459,7 @@ export const _executor = async (
|
||||
baseUnit =
|
||||
(await getSettingsState)()?.modeling.defaultUnit.current || 'mm'
|
||||
}
|
||||
const memory: RawProgramMemory = await execute_wasm(
|
||||
const execState: RawExecState = await execute_wasm(
|
||||
JSON.stringify(node),
|
||||
JSON.stringify(programMemory.toRaw()),
|
||||
baseUnit,
|
||||
@ -400,7 +467,7 @@ export const _executor = async (
|
||||
fileSystemManager,
|
||||
isMock
|
||||
)
|
||||
return ProgramMemory.fromRaw(memory)
|
||||
return execStateFromRaw(execState)
|
||||
} catch (e: any) {
|
||||
console.log(e)
|
||||
const parsed: RustKclError = JSON.parse(e.toString())
|
||||
|
||||
@ -1,4 +1,10 @@
|
||||
import { Program, ProgramMemory, _executor, SourceRange } from '../lang/wasm'
|
||||
import {
|
||||
Program,
|
||||
ProgramMemory,
|
||||
_executor,
|
||||
SourceRange,
|
||||
ExecState,
|
||||
} from '../lang/wasm'
|
||||
import {
|
||||
EngineCommandManager,
|
||||
EngineCommandManagerEvents,
|
||||
@ -78,7 +84,7 @@ class MockEngineCommandManager {
|
||||
export async function enginelessExecutor(
|
||||
ast: Program | Error,
|
||||
pm: ProgramMemory | Error = ProgramMemory.empty()
|
||||
): Promise<ProgramMemory> {
|
||||
): Promise<ExecState> {
|
||||
if (err(ast)) return Promise.reject(ast)
|
||||
if (err(pm)) return Promise.reject(pm)
|
||||
|
||||
@ -88,15 +94,15 @@ export async function enginelessExecutor(
|
||||
}) as any as EngineCommandManager
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
mockEngineCommandManager.startNewSession()
|
||||
const programMemory = await _executor(ast, pm, mockEngineCommandManager, true)
|
||||
const execState = await _executor(ast, pm, mockEngineCommandManager, true)
|
||||
await mockEngineCommandManager.waitForAllCommands()
|
||||
return programMemory
|
||||
return execState
|
||||
}
|
||||
|
||||
export async function executor(
|
||||
ast: Program,
|
||||
pm: ProgramMemory = ProgramMemory.empty()
|
||||
): Promise<ProgramMemory> {
|
||||
): Promise<ExecState> {
|
||||
const engineCommandManager = new EngineCommandManager()
|
||||
engineCommandManager.start({
|
||||
setIsStreamReady: () => {},
|
||||
@ -117,14 +123,9 @@ export async function executor(
|
||||
toSync(async () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
engineCommandManager.startNewSession()
|
||||
const programMemory = await _executor(
|
||||
ast,
|
||||
pm,
|
||||
engineCommandManager,
|
||||
false
|
||||
)
|
||||
const execState = await _executor(ast, pm, engineCommandManager, false)
|
||||
await engineCommandManager.waitForAllCommands()
|
||||
resolve(programMemory)
|
||||
resolve(execState)
|
||||
}, reportRejection)
|
||||
)
|
||||
})
|
||||
|
||||
@ -97,7 +97,7 @@ export function useCalculateKclExpression({
|
||||
})
|
||||
if (trap(error, { suppress: true })) return
|
||||
}
|
||||
const { programMemory } = await executeAst({
|
||||
const { execState } = await executeAst({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
useFakeExecutor: true,
|
||||
@ -111,7 +111,7 @@ export function useCalculateKclExpression({
|
||||
const init =
|
||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||
resultDeclaration?.declarations?.[0]?.init
|
||||
const result = programMemory?.get('__result__')?.value
|
||||
const result = execState.memory?.get('__result__')?.value
|
||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||
init && setValueNode(init)
|
||||
}
|
||||
|
||||
@ -59,7 +59,7 @@ pub async fn execute_wasm(
|
||||
// gloo-serialize crate instead.
|
||||
// DO NOT USE serde_wasm_bindgen::to_value(&memory).map_err(|e| e.to_string())
|
||||
// it will break the frontend.
|
||||
JsValue::from_serde(&exec_state.memory).map_err(|e| e.to_string())
|
||||
JsValue::from_serde(&exec_state).map_err(|e| e.to_string())
|
||||
}
|
||||
|
||||
// wasm_bindgen wrapper for execute
|
||||
|
||||
Reference in New Issue
Block a user