ProgramMemory refactor - eliminate copies of memory (#5273)
* refactor program memory for encapsulation and remove special treatment of return values Signed-off-by: Nick Cameron <nrc@ncameron.org> * Refactor ProgramMemory to isolate mutation Signed-off-by: Nick Cameron <nrc@ncameron.org> * Refactor ProgramMemory Signed-off-by: Nick Cameron <nrc@ncameron.org> * Generated output Signed-off-by: Nick Cameron <nrc@ncameron.org> * Cache the result of executing modules for their items Signed-off-by: Nick Cameron <nrc@ncameron.org> * Compress envs when popped Signed-off-by: Nick Cameron <nrc@ncameron.org> * Remove the last traces of geometry from the memory module Signed-off-by: Nick Cameron <nrc@ncameron.org> * docs Signed-off-by: Nick Cameron <nrc@ncameron.org> * Fixups Signed-off-by: Nick Cameron <nrc@ncameron.org> * Frontend Signed-off-by: Nick Cameron <nrc@ncameron.org> * Improve Environment GC Signed-off-by: Nick Cameron <nrc@ncameron.org> * Fixup mock execution frontend and interpreter, misc bug fixes Signed-off-by: Nick Cameron <nrc@ncameron.org> --------- Signed-off-by: Nick Cameron <nrc@ncameron.org>
This commit is contained in:
1011
docs/kcl/std.json
1011
docs/kcl/std.json
File diff suppressed because it is too large
Load Diff
16
docs/kcl/types/EnvironmentRef.md
Normal file
16
docs/kcl/types/EnvironmentRef.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "EnvironmentRef"
|
||||
excerpt: ""
|
||||
layout: manual
|
||||
---
|
||||
|
||||
|
||||
[`SnapshotRef`](/docs/kcl/types/SnapshotRef)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -311,7 +311,7 @@ Data for an imported geometry.
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Function`| | No |
|
||||
| `memory` |[`ProgramMemory`](/docs/kcl/types/ProgramMemory)| Any KCL value. | No |
|
||||
| `memory` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| Any KCL value. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
@ -351,6 +351,23 @@ Data for an imported geometry.
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Tombstone`| | No |
|
||||
| `value` |`null`| | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -17,6 +17,5 @@ layout: manual
|
||||
|----------|------|-------------|----------|
|
||||
| `environments` |`[` [`Environment`](/docs/kcl/types/Environment) `]`| | No |
|
||||
| `currentEnv` |`integer`| | No |
|
||||
| `return` |[`KclValue`](/docs/kcl/types/KclValue)| | No |
|
||||
|
||||
|
||||
|
16
docs/kcl/types/SnapshotRef.md
Normal file
16
docs/kcl/types/SnapshotRef.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "SnapshotRef"
|
||||
excerpt: "An index pointing to a snapshot within a specific (unspecified) environment."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
An index pointing to a snapshot within a specific (unspecified) environment.
|
||||
|
||||
**Type:** `integer` (`uint`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -31,7 +31,6 @@ import {
|
||||
recast,
|
||||
defaultSourceRange,
|
||||
resultIsOk,
|
||||
ProgramMemory,
|
||||
topLevelRange,
|
||||
} from 'lang/wasm'
|
||||
import { CustomIcon, CustomIconName } from 'components/CustomIcon'
|
||||
@ -425,7 +424,7 @@ export async function deleteSegment({
|
||||
modifiedAst = deleteSegmentFromPipeExpression(
|
||||
dependentRanges,
|
||||
modifiedAst,
|
||||
kclManager.programMemory,
|
||||
kclManager.variables,
|
||||
codeManager.code,
|
||||
pathToNode
|
||||
)
|
||||
@ -439,8 +438,8 @@ export async function deleteSegment({
|
||||
const testExecute = await executeAst({
|
||||
ast: modifiedAst,
|
||||
engineCommandManager: engineCommandManager,
|
||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||
programMemoryOverride: ProgramMemory.empty(),
|
||||
isMock: true,
|
||||
usePrevMemory: false,
|
||||
})
|
||||
if (testExecute.errors.length) {
|
||||
toast.error('Segment tag used outside of current Sketch. Could not delete.')
|
||||
@ -675,7 +674,7 @@ const ConstraintSymbol = ({
|
||||
shallowPath,
|
||||
argPosition,
|
||||
kclManager.ast,
|
||||
kclManager.programMemory
|
||||
kclManager.variables
|
||||
)
|
||||
|
||||
if (!transform) return
|
||||
|
@ -47,7 +47,6 @@ import {
|
||||
PathToNode,
|
||||
PipeExpression,
|
||||
Program,
|
||||
ProgramMemory,
|
||||
recast,
|
||||
Sketch,
|
||||
Solid,
|
||||
@ -61,6 +60,7 @@ import {
|
||||
SourceRange,
|
||||
topLevelRange,
|
||||
CallExpressionKw,
|
||||
VariableMap,
|
||||
} from 'lang/wasm'
|
||||
import { calculate_circle_from_3_points } from '../wasm-lib/pkg/wasm_lib'
|
||||
import {
|
||||
@ -158,7 +158,6 @@ type Vec3Array = [number, number, number]
|
||||
export class SceneEntities {
|
||||
engineCommandManager: EngineCommandManager
|
||||
scene: Scene
|
||||
sceneProgramMemory: ProgramMemory = ProgramMemory.empty()
|
||||
activeSegments: { [key: string]: Group } = {}
|
||||
intersectionPlane: Mesh | null = null
|
||||
axisGroup: Group | null = null
|
||||
@ -502,29 +501,25 @@ export class SceneEntities {
|
||||
selectionRanges?: Selections
|
||||
}): Promise<{
|
||||
truncatedAst: Node<Program>
|
||||
programMemoryOverride: ProgramMemory
|
||||
sketch: Sketch
|
||||
variableDeclarationName: string
|
||||
}> {
|
||||
const prepared = this.prepareTruncatedMemoryAndAst(
|
||||
const prepared = this.prepareTruncatedAst(
|
||||
sketchPathToNode || [],
|
||||
maybeModdedAst
|
||||
)
|
||||
if (err(prepared)) return Promise.reject(prepared)
|
||||
const { truncatedAst, programMemoryOverride, variableDeclarationName } =
|
||||
prepared
|
||||
const { truncatedAst, variableDeclarationName } = prepared
|
||||
|
||||
const { execState } = await executeAst({
|
||||
ast: truncatedAst,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||
programMemoryOverride,
|
||||
isMock: true,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
const sketch = sketchFromPathToNode({
|
||||
pathToNode: sketchPathToNode,
|
||||
ast: maybeModdedAst,
|
||||
programMemory,
|
||||
variables: execState.variables,
|
||||
})
|
||||
if (err(sketch)) return Promise.reject(sketch)
|
||||
if (!sketch) return Promise.reject('sketch not found')
|
||||
@ -532,11 +527,9 @@ export class SceneEntities {
|
||||
if (!isArray(sketch?.paths))
|
||||
return {
|
||||
truncatedAst,
|
||||
programMemoryOverride,
|
||||
sketch,
|
||||
variableDeclarationName,
|
||||
}
|
||||
this.sceneProgramMemory = programMemory
|
||||
const group = new Group()
|
||||
position && group.position.set(...position)
|
||||
group.userData = {
|
||||
@ -684,7 +677,6 @@ export class SceneEntities {
|
||||
|
||||
return {
|
||||
truncatedAst,
|
||||
programMemoryOverride,
|
||||
sketch,
|
||||
variableDeclarationName,
|
||||
}
|
||||
@ -733,7 +725,7 @@ export class SceneEntities {
|
||||
const variableDeclarationName = _node1.node?.declaration.id?.name || ''
|
||||
|
||||
const sg = sketchFromKclValue(
|
||||
kclManager.programMemory.get(variableDeclarationName),
|
||||
kclManager.variables[variableDeclarationName],
|
||||
variableDeclarationName
|
||||
)
|
||||
if (err(sg)) return Promise.reject(sg)
|
||||
@ -742,7 +734,7 @@ export class SceneEntities {
|
||||
const index = sg.paths.length // because we've added a new segment that's not in the memory yet, no need for `-1`
|
||||
const mod = addNewSketchLn({
|
||||
node: _ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
variables: kclManager.variables,
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
to: lastSeg.to,
|
||||
@ -761,8 +753,7 @@ export class SceneEntities {
|
||||
if (shouldTearDown) await this.tearDownSketch({ removeAxis: false })
|
||||
sceneInfra.resetMouseListeners()
|
||||
|
||||
const { truncatedAst, programMemoryOverride, sketch } =
|
||||
await this.setupSketch({
|
||||
const { truncatedAst, sketch } = await this.setupSketch({
|
||||
sketchPathToNode,
|
||||
forward,
|
||||
up,
|
||||
@ -801,7 +792,7 @@ export class SceneEntities {
|
||||
])
|
||||
modifiedAst = addCallExpressionsToPipe({
|
||||
node: kclManager.ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
variables: kclManager.variables,
|
||||
pathToNode: sketchPathToNode,
|
||||
expressions: [
|
||||
lastSegment.type === 'TangentialArcTo'
|
||||
@ -817,7 +808,7 @@ export class SceneEntities {
|
||||
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
||||
modifiedAst = addCloseToPipe({
|
||||
node: modifiedAst,
|
||||
programMemory: kclManager.programMemory,
|
||||
variables: kclManager.variables,
|
||||
pathToNode: sketchPathToNode,
|
||||
})
|
||||
if (trap(modifiedAst)) return Promise.reject(modifiedAst)
|
||||
@ -867,7 +858,7 @@ export class SceneEntities {
|
||||
|
||||
const tmp = addNewSketchLn({
|
||||
node: kclManager.ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
variables: kclManager.variables,
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
from: [lastSegment.to[0], lastSegment.to[1]],
|
||||
@ -908,7 +899,6 @@ export class SceneEntities {
|
||||
sketchPathToNode,
|
||||
draftInfo: {
|
||||
truncatedAst,
|
||||
programMemoryOverride,
|
||||
variableDeclarationName,
|
||||
},
|
||||
})
|
||||
@ -949,7 +939,7 @@ export class SceneEntities {
|
||||
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||
_ast = pResult.program
|
||||
|
||||
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||
const { truncatedAst } = await this.setupSketch({
|
||||
sketchPathToNode,
|
||||
forward,
|
||||
up,
|
||||
@ -982,13 +972,10 @@ export class SceneEntities {
|
||||
const { execState } = await executeAst({
|
||||
ast: truncatedAst,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||
programMemoryOverride,
|
||||
isMock: true,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
this.sceneProgramMemory = programMemory
|
||||
const sketch = sketchFromKclValue(
|
||||
programMemory.get(variableDeclarationName),
|
||||
execState.variables[variableDeclarationName],
|
||||
variableDeclarationName
|
||||
)
|
||||
if (err(sketch)) return Promise.reject(sketch)
|
||||
@ -1045,15 +1032,12 @@ export class SceneEntities {
|
||||
const { execState } = await executeAst({
|
||||
ast: _ast,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||
programMemoryOverride,
|
||||
isMock: true,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
|
||||
// Prepare to update the THREEjs scene
|
||||
this.sceneProgramMemory = programMemory
|
||||
const sketch = sketchFromKclValue(
|
||||
programMemory.get(variableDeclarationName),
|
||||
execState.variables[variableDeclarationName],
|
||||
variableDeclarationName
|
||||
)
|
||||
if (err(sketch)) return
|
||||
@ -1104,7 +1088,7 @@ export class SceneEntities {
|
||||
if (trap(pResult) || !resultIsOk(pResult)) return Promise.reject(pResult)
|
||||
_ast = pResult.program
|
||||
|
||||
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||
const { truncatedAst } = await this.setupSketch({
|
||||
sketchPathToNode,
|
||||
forward,
|
||||
up,
|
||||
@ -1144,13 +1128,10 @@ export class SceneEntities {
|
||||
const { execState } = await executeAst({
|
||||
ast: truncatedAst,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||
programMemoryOverride,
|
||||
isMock: true,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
this.sceneProgramMemory = programMemory
|
||||
const sketch = sketchFromKclValue(
|
||||
programMemory.get(variableDeclarationName),
|
||||
execState.variables[variableDeclarationName],
|
||||
variableDeclarationName
|
||||
)
|
||||
if (err(sketch)) return Promise.reject(sketch)
|
||||
@ -1210,15 +1191,12 @@ export class SceneEntities {
|
||||
const { execState } = await executeAst({
|
||||
ast: _ast,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||
programMemoryOverride,
|
||||
isMock: true,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
|
||||
// Prepare to update the THREEjs scene
|
||||
this.sceneProgramMemory = programMemory
|
||||
const sketch = sketchFromKclValue(
|
||||
programMemory.get(variableDeclarationName),
|
||||
execState.variables[variableDeclarationName],
|
||||
variableDeclarationName
|
||||
)
|
||||
if (err(sketch)) return
|
||||
@ -1605,7 +1583,7 @@ export class SceneEntities {
|
||||
// do a quick mock execution to get the program memory up-to-date
|
||||
await kclManager.executeAstMock(_ast)
|
||||
|
||||
const { programMemoryOverride, truncatedAst } = await this.setupSketch({
|
||||
const { truncatedAst } = await this.setupSketch({
|
||||
sketchPathToNode,
|
||||
forward,
|
||||
up,
|
||||
@ -1634,7 +1612,7 @@ export class SceneEntities {
|
||||
if (sketchInit.type === 'PipeExpression') {
|
||||
const moddedResult = changeSketchArguments(
|
||||
modded,
|
||||
kclManager.programMemory,
|
||||
kclManager.variables,
|
||||
{
|
||||
type: 'path',
|
||||
pathToNode: [
|
||||
@ -1657,13 +1635,10 @@ export class SceneEntities {
|
||||
const { execState } = await executeAst({
|
||||
ast: modded,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||
programMemoryOverride,
|
||||
isMock: true,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
this.sceneProgramMemory = programMemory
|
||||
const sketch = sketchFromKclValue(
|
||||
programMemory.get(variableDeclarationName),
|
||||
execState.variables[variableDeclarationName],
|
||||
variableDeclarationName
|
||||
)
|
||||
if (err(sketch)) return
|
||||
@ -1700,7 +1675,7 @@ export class SceneEntities {
|
||||
if (sketchInit.type === 'PipeExpression') {
|
||||
const moddedResult = changeSketchArguments(
|
||||
modded,
|
||||
kclManager.programMemory,
|
||||
kclManager.variables,
|
||||
{
|
||||
type: 'path',
|
||||
pathToNode: [
|
||||
@ -1788,7 +1763,7 @@ export class SceneEntities {
|
||||
const sketch = sketchFromPathToNode({
|
||||
pathToNode,
|
||||
ast: kclManager.ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
variables: kclManager.variables,
|
||||
})
|
||||
if (trap(sketch)) return
|
||||
if (!sketch) {
|
||||
@ -1801,7 +1776,7 @@ export class SceneEntities {
|
||||
const prevSegment = sketch.paths[pipeIndex - 2]
|
||||
const mod = addNewSketchLn({
|
||||
node: kclManager.ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
variables: kclManager.variables,
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
to: [intersectionPoint.twoD.x, intersectionPoint.twoD.y],
|
||||
@ -1873,15 +1848,15 @@ export class SceneEntities {
|
||||
...this.mouseEnterLeaveCallbacks(),
|
||||
})
|
||||
}
|
||||
prepareTruncatedMemoryAndAst = (
|
||||
prepareTruncatedAst = (
|
||||
sketchPathToNode: PathToNode,
|
||||
ast?: Node<Program>,
|
||||
draftSegment?: DraftSegment
|
||||
) =>
|
||||
prepareTruncatedMemoryAndAst(
|
||||
prepareTruncatedAst(
|
||||
sketchPathToNode,
|
||||
ast || kclManager.ast,
|
||||
kclManager.lastSuccessfulProgramMemory,
|
||||
kclManager.lastSuccessfulVariables,
|
||||
draftSegment
|
||||
)
|
||||
onDragSegment({
|
||||
@ -1897,7 +1872,6 @@ export class SceneEntities {
|
||||
intersects: Intersection<Object3D<Object3DEventMap>>[]
|
||||
draftInfo?: {
|
||||
truncatedAst: Node<Program>
|
||||
programMemoryOverride: ProgramMemory
|
||||
variableDeclarationName: string
|
||||
}
|
||||
}) {
|
||||
@ -2010,12 +1984,12 @@ export class SceneEntities {
|
||||
to: dragTo,
|
||||
from,
|
||||
},
|
||||
previousProgramMemory: kclManager.programMemory,
|
||||
variables: kclManager.variables,
|
||||
})
|
||||
} else {
|
||||
modded = changeSketchArguments(
|
||||
modifiedAst,
|
||||
kclManager.programMemory,
|
||||
kclManager.variables,
|
||||
{
|
||||
type: 'sourceRange',
|
||||
sourceRange: topLevelRange(node.start, node.end),
|
||||
@ -2028,10 +2002,9 @@ export class SceneEntities {
|
||||
modifiedAst = modded.modifiedAst
|
||||
const info = draftInfo
|
||||
? draftInfo
|
||||
: this.prepareTruncatedMemoryAndAst(pathToNode || [])
|
||||
: this.prepareTruncatedAst(pathToNode || [])
|
||||
if (trap(info, { suppress: true })) return
|
||||
const { truncatedAst, programMemoryOverride, variableDeclarationName } =
|
||||
info
|
||||
const { truncatedAst, variableDeclarationName } = info
|
||||
;(async () => {
|
||||
const code = recast(modifiedAst)
|
||||
if (trap(code)) return
|
||||
@ -2042,13 +2015,11 @@ export class SceneEntities {
|
||||
const { execState } = await executeAst({
|
||||
ast: truncatedAst,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||
programMemoryOverride,
|
||||
isMock: true,
|
||||
})
|
||||
const programMemory = execState.memory
|
||||
this.sceneProgramMemory = programMemory
|
||||
const variables = execState.variables
|
||||
|
||||
const maybeSketch = programMemory.get(variableDeclarationName)
|
||||
const maybeSketch = variables[variableDeclarationName]
|
||||
let sketch: Sketch | undefined
|
||||
const sk = sketchFromKclValueOptional(
|
||||
maybeSketch,
|
||||
@ -2394,15 +2365,14 @@ export class SceneEntities {
|
||||
|
||||
// calculations/pure-functions/easy to test so no excuse not to
|
||||
|
||||
function prepareTruncatedMemoryAndAst(
|
||||
function prepareTruncatedAst(
|
||||
sketchPathToNode: PathToNode,
|
||||
ast: Node<Program>,
|
||||
programMemory: ProgramMemory,
|
||||
variables: VariableMap,
|
||||
draftSegment?: DraftSegment
|
||||
):
|
||||
| {
|
||||
truncatedAst: Node<Program>
|
||||
programMemoryOverride: ProgramMemory
|
||||
variableDeclarationName: string
|
||||
}
|
||||
| Error {
|
||||
@ -2417,7 +2387,7 @@ function prepareTruncatedMemoryAndAst(
|
||||
if (err(_node)) return _node
|
||||
const variableDeclarationName = _node.node?.declaration.id?.name || ''
|
||||
const sg = sketchFromKclValue(
|
||||
programMemory.get(variableDeclarationName),
|
||||
variables[variableDeclarationName],
|
||||
variableDeclarationName
|
||||
)
|
||||
if (err(sg)) return sg
|
||||
@ -2476,43 +2446,8 @@ function prepareTruncatedMemoryAndAst(
|
||||
body: [structuredClone(_ast.body[bodyIndex])],
|
||||
}
|
||||
|
||||
// Grab all the TagDeclarators and TagIdentifiers from memory.
|
||||
let start = _node.node.start
|
||||
const programMemoryOverride = programMemory.filterVariables(true, (value) => {
|
||||
if (
|
||||
!('__meta' in value) ||
|
||||
value.__meta === undefined ||
|
||||
value.__meta.length === 0 ||
|
||||
value.__meta[0].sourceRange === undefined
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (value.__meta[0].sourceRange[0] >= start) {
|
||||
// We only want things before our start point.
|
||||
return false
|
||||
}
|
||||
|
||||
return value.type === 'TagIdentifier'
|
||||
})
|
||||
if (err(programMemoryOverride)) return programMemoryOverride
|
||||
|
||||
for (let i = 0; i < bodyIndex; i++) {
|
||||
const node = _ast.body[i]
|
||||
if (node.type !== 'VariableDeclaration') {
|
||||
continue
|
||||
}
|
||||
const name = node.declaration.id.name
|
||||
const memoryItem = programMemory.get(name)
|
||||
if (!memoryItem) {
|
||||
continue
|
||||
}
|
||||
const error = programMemoryOverride.set(name, structuredClone(memoryItem))
|
||||
if (err(error)) return error
|
||||
}
|
||||
return {
|
||||
truncatedAst,
|
||||
programMemoryOverride,
|
||||
variableDeclarationName,
|
||||
}
|
||||
}
|
||||
@ -2532,11 +2467,11 @@ export function getParentGroup(
|
||||
export function sketchFromPathToNode({
|
||||
pathToNode,
|
||||
ast,
|
||||
programMemory,
|
||||
variables,
|
||||
}: {
|
||||
pathToNode: PathToNode
|
||||
ast: Program
|
||||
programMemory: ProgramMemory
|
||||
variables: VariableMap
|
||||
}): Sketch | null | Error {
|
||||
const _varDec = getNodeFromPath<VariableDeclarator>(
|
||||
kclManager.ast,
|
||||
@ -2545,7 +2480,7 @@ export function sketchFromPathToNode({
|
||||
)
|
||||
if (err(_varDec)) return _varDec
|
||||
const varDec = _varDec.node
|
||||
const result = programMemory.get(varDec?.id?.name || '')
|
||||
const result = variables[varDec?.id?.name || '']
|
||||
if (result?.type === 'Solid') {
|
||||
return result.value.sketch
|
||||
}
|
||||
@ -2584,7 +2519,7 @@ export function getSketchQuaternion(
|
||||
const sketch = sketchFromPathToNode({
|
||||
pathToNode: sketchPathToNode,
|
||||
ast: kclManager.ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
variables: kclManager.variables,
|
||||
})
|
||||
if (err(sketch)) return sketch
|
||||
const zAxis = sketch?.on.zAxis || sketchNormalBackUp
|
||||
@ -2601,7 +2536,7 @@ export async function getSketchOrientationDetails(
|
||||
const sketch = sketchFromPathToNode({
|
||||
pathToNode: sketchPathToNode,
|
||||
ast: kclManager.ast,
|
||||
programMemory: kclManager.programMemory,
|
||||
variables: kclManager.variables,
|
||||
})
|
||||
if (err(sketch)) return Promise.reject(sketch)
|
||||
if (!sketch) return Promise.reject('sketch not found')
|
||||
|
@ -1,11 +1,5 @@
|
||||
import { useEffect, useState, useRef } from 'react'
|
||||
import {
|
||||
parse,
|
||||
BinaryPart,
|
||||
Expr,
|
||||
ProgramMemory,
|
||||
resultIsOk,
|
||||
} from '../lang/wasm'
|
||||
import { parse, BinaryPart, Expr, resultIsOk, VariableMap } from '../lang/wasm'
|
||||
import {
|
||||
createIdentifier,
|
||||
createLiteral,
|
||||
@ -100,7 +94,7 @@ export function useCalc({
|
||||
newVariableInsertIndex: number
|
||||
setNewVariableName: (a: string) => void
|
||||
} {
|
||||
const { programMemory } = useKclContext()
|
||||
const { variables } = useKclContext()
|
||||
const { context } = useModelingContext()
|
||||
const selectionRange =
|
||||
context.selectionRanges?.graphSelections[0]?.codeRef?.range
|
||||
@ -127,7 +121,7 @@ export function useCalc({
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
if (programMemory.has(newVariableName)) {
|
||||
if (variables[newVariableName]) {
|
||||
setIsNewVariableNameUnique(false)
|
||||
} else {
|
||||
setIsNewVariableNameUnique(true)
|
||||
@ -135,14 +129,14 @@ export function useCalc({
|
||||
}, [newVariableName])
|
||||
|
||||
useEffect(() => {
|
||||
if (!programMemory || !selectionRange) return
|
||||
if (!variables || !selectionRange) return
|
||||
const varInfo = findAllPreviousVariables(
|
||||
kclManager.ast,
|
||||
kclManager.programMemory,
|
||||
kclManager.variables,
|
||||
selectionRange
|
||||
)
|
||||
setAvailableVarInfo(varInfo)
|
||||
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
||||
}, [kclManager.ast, kclManager.variables, selectionRange])
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
@ -150,9 +144,9 @@ export function useCalc({
|
||||
const pResult = parse(code)
|
||||
if (trap(pResult) || !resultIsOk(pResult)) return
|
||||
const ast = pResult.program
|
||||
const _programMem: ProgramMemory = ProgramMemory.empty()
|
||||
const _variables: VariableMap = {}
|
||||
for (const { key, value } of availableVarInfo.variables) {
|
||||
const error = _programMem.set(key, {
|
||||
const error = (_variables[key] = {
|
||||
type: 'String',
|
||||
value,
|
||||
__meta: [],
|
||||
@ -163,8 +157,8 @@ export function useCalc({
|
||||
executeAst({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||
programMemoryOverride: kclManager.programMemory.clone(),
|
||||
isMock: true,
|
||||
variables,
|
||||
}).then(({ execState }) => {
|
||||
const resultDeclaration = ast.body.find(
|
||||
(a) =>
|
||||
@ -174,7 +168,7 @@ export function useCalc({
|
||||
const init =
|
||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||
resultDeclaration?.declaration.init
|
||||
const result = execState.memory?.get('__result__')?.value
|
||||
const result = execState.variables['__result__']?.value
|
||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||
init && setValueNode(init)
|
||||
})
|
||||
|
@ -157,8 +157,6 @@ export const LspProvider = ({ children }: { children: React.ReactNode }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
plugin.requestSemanticTokens()
|
||||
break
|
||||
case 'kcl/memoryUpdated':
|
||||
break
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(error)
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { processMemory } from './MemoryPane'
|
||||
import { enginelessExecutor } from '../../../lib/testHelpers'
|
||||
import { assertParse, initPromise, ProgramMemory } from '../../../lang/wasm'
|
||||
import { assertParse, initPromise } from '../../../lang/wasm'
|
||||
|
||||
beforeAll(async () => {
|
||||
await initPromise
|
||||
@ -29,15 +29,11 @@ describe('processMemory', () => {
|
||||
|> line(endAbsolute = [2.15, 4.32])
|
||||
// |> rx(90, %)`
|
||||
const ast = assertParse(code)
|
||||
const execState = await enginelessExecutor(ast, ProgramMemory.empty())
|
||||
const output = processMemory(execState.memory)
|
||||
const execState = await enginelessExecutor(ast)
|
||||
const output = processMemory(execState.variables)
|
||||
expect(output.myVar).toEqual(5)
|
||||
expect(output.otherVar).toEqual(3)
|
||||
expect(output).toEqual({
|
||||
HALF_TURN: 180,
|
||||
QUARTER_TURN: 90,
|
||||
THREE_QUARTER_TURN: 270,
|
||||
ZERO: 0,
|
||||
myVar: 5,
|
||||
myFn: '__function(a)__',
|
||||
otherVar: 3,
|
||||
|
@ -2,10 +2,10 @@ import toast from 'react-hot-toast'
|
||||
import ReactJson from 'react-json-view'
|
||||
import { useMemo } from 'react'
|
||||
import {
|
||||
ProgramMemory,
|
||||
Path,
|
||||
ExtrudeSurface,
|
||||
sketchFromKclValueOptional,
|
||||
VariableMap,
|
||||
} from 'lang/wasm'
|
||||
import { useKclContext } from 'lang/KclProvider'
|
||||
import { useResolvedTheme } from 'hooks/useResolvedTheme'
|
||||
@ -15,12 +15,12 @@ import Tooltip from 'components/Tooltip'
|
||||
import { useModelingContext } from 'hooks/useModelingContext'
|
||||
|
||||
export const MemoryPaneMenu = () => {
|
||||
const { programMemory } = useKclContext()
|
||||
const { variables } = useKclContext()
|
||||
|
||||
function copyProgramMemoryToClipboard() {
|
||||
if (globalThis && 'navigator' in globalThis) {
|
||||
navigator.clipboard
|
||||
.writeText(JSON.stringify(programMemory))
|
||||
.writeText(JSON.stringify(variables))
|
||||
.then(() => toast.success('Program memory copied to clipboard'))
|
||||
.catch((e) =>
|
||||
trap(new Error('Failed to copy program memory to clipboard'))
|
||||
@ -50,12 +50,9 @@ export const MemoryPaneMenu = () => {
|
||||
|
||||
export const MemoryPane = () => {
|
||||
const theme = useResolvedTheme()
|
||||
const { programMemory } = useKclContext()
|
||||
const { variables } = useKclContext()
|
||||
const { state } = useModelingContext()
|
||||
const ProcessedMemory = useMemo(
|
||||
() => processMemory(programMemory),
|
||||
[programMemory]
|
||||
)
|
||||
const ProcessedMemory = useMemo(() => processMemory(variables), [variables])
|
||||
return (
|
||||
<div className="h-full relative">
|
||||
<div className="absolute inset-0 p-2 flex flex-col items-start">
|
||||
@ -85,9 +82,10 @@ export const MemoryPane = () => {
|
||||
)
|
||||
}
|
||||
|
||||
export const processMemory = (programMemory: ProgramMemory) => {
|
||||
export const processMemory = (variables: VariableMap) => {
|
||||
const processedMemory: any = {}
|
||||
for (const [key, val] of programMemory?.visibleEntries()) {
|
||||
for (const [key, val] of Object.entries(variables)) {
|
||||
if (val === undefined) continue
|
||||
if (
|
||||
val.type === 'Sketch' ||
|
||||
// @ts-ignore
|
||||
|
@ -95,7 +95,7 @@ export function applyConstraintEqualAngle({
|
||||
ast: kclManager.ast,
|
||||
selectionRanges,
|
||||
transformInfos: transforms,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
})
|
||||
if (err(transform)) return transform
|
||||
const { modifiedAst, pathToNodeMap } = transform
|
||||
|
@ -93,7 +93,7 @@ export function applyConstraintEqualLength({
|
||||
ast: kclManager.ast,
|
||||
selectionRanges,
|
||||
transformInfos: transforms,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
})
|
||||
if (err(transform)) return transform
|
||||
const { modifiedAst, pathToNodeMap } = transform
|
||||
|
@ -1,6 +1,6 @@
|
||||
import { toolTips } from 'lang/langHelpers'
|
||||
import { Selections } from 'lib/selections'
|
||||
import { Program, ProgramMemory, Expr } from '../../lang/wasm'
|
||||
import { Program, Expr, VariableMap } from '../../lang/wasm'
|
||||
import { getNodeFromPath } from '../../lang/queryAst'
|
||||
import {
|
||||
PathToNodeMap,
|
||||
@ -51,7 +51,7 @@ export function applyConstraintHorzVert(
|
||||
selectionRanges: Selections,
|
||||
horOrVert: 'vertical' | 'horizontal',
|
||||
ast: Node<Program>,
|
||||
programMemory: ProgramMemory
|
||||
memVars: VariableMap
|
||||
):
|
||||
| {
|
||||
modifiedAst: Node<Program>
|
||||
@ -66,7 +66,7 @@ export function applyConstraintHorzVert(
|
||||
ast,
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory,
|
||||
memVars,
|
||||
referenceSegName: '',
|
||||
})
|
||||
}
|
||||
|
@ -46,7 +46,7 @@ export function intersectInfo({
|
||||
isLinesParallelAndConstrained(
|
||||
kclManager.ast,
|
||||
engineCommandManager.artifactGraph,
|
||||
kclManager.programMemory,
|
||||
kclManager.variables,
|
||||
selectionRanges.graphSelections[0],
|
||||
selectionRanges.graphSelections[1]
|
||||
)
|
||||
@ -147,7 +147,7 @@ export async function applyConstraintIntersect({
|
||||
ast: structuredClone(kclManager.ast),
|
||||
selectionRanges: forcedSelectionRanges,
|
||||
transformInfos: transforms,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
})
|
||||
if (err(transform1)) return Promise.reject(transform1)
|
||||
const { modifiedAst, tagInfo, valueUsedInTransform, pathToNodeMap } =
|
||||
@ -184,7 +184,7 @@ export async function applyConstraintIntersect({
|
||||
ast: kclManager.ast,
|
||||
selectionRanges: forcedSelectionRanges,
|
||||
transformInfos: transforms,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
forceSegName: segName,
|
||||
forceValueUsedInTransform: finalValue,
|
||||
})
|
||||
|
@ -88,7 +88,7 @@ export function applyRemoveConstrainingValues({
|
||||
ast: kclManager.ast,
|
||||
selectionRanges: updatedSelectionRanges,
|
||||
transformInfos: transforms,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
referenceSegName: '',
|
||||
})
|
||||
}
|
||||
|
@ -104,7 +104,7 @@ export async function applyConstraintAbsDistance({
|
||||
ast: structuredClone(kclManager.ast),
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
referenceSegName: '',
|
||||
})
|
||||
if (err(transform1)) return Promise.reject(transform1)
|
||||
@ -124,7 +124,7 @@ export async function applyConstraintAbsDistance({
|
||||
ast: structuredClone(kclManager.ast),
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
referenceSegName: '',
|
||||
forceValueUsedInTransform: finalValue,
|
||||
})
|
||||
@ -172,7 +172,7 @@ export function applyConstraintAxisAlign({
|
||||
ast: structuredClone(kclManager.ast),
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
referenceSegName: '',
|
||||
forceValueUsedInTransform: finalValue,
|
||||
})
|
||||
|
@ -95,7 +95,7 @@ export async function applyConstraintAngleBetween({
|
||||
ast: structuredClone(kclManager.ast),
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
})
|
||||
if (err(transformed1)) return Promise.reject(transformed1)
|
||||
const { modifiedAst, tagInfo, valueUsedInTransform, pathToNodeMap } =
|
||||
@ -133,7 +133,7 @@ export async function applyConstraintAngleBetween({
|
||||
ast: kclManager.ast,
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
forceSegName: segName,
|
||||
forceValueUsedInTransform: finalValue,
|
||||
})
|
||||
|
@ -107,7 +107,7 @@ export async function applyConstraintHorzVertDistance({
|
||||
ast: structuredClone(kclManager.ast),
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
})
|
||||
if (err(transformed)) return Promise.reject(transformed)
|
||||
const { modifiedAst, tagInfo, valueUsedInTransform, pathToNodeMap } =
|
||||
@ -145,7 +145,7 @@ export async function applyConstraintHorzVertDistance({
|
||||
ast: kclManager.ast,
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
forceSegName: segName,
|
||||
forceValueUsedInTransform: finalValue,
|
||||
})
|
||||
@ -195,7 +195,7 @@ export function applyConstraintHorzVertAlign({
|
||||
ast: kclManager.ast,
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
forceValueUsedInTransform: finalValue,
|
||||
})
|
||||
if (err(retval)) return retval
|
||||
|
@ -105,7 +105,7 @@ export async function applyConstraintLength({
|
||||
ast,
|
||||
selectionRanges,
|
||||
transformInfos: transforms,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
referenceSegName: '',
|
||||
forceValueUsedInTransform: distanceExpression,
|
||||
})
|
||||
@ -137,7 +137,7 @@ export async function applyConstraintAngleLength({
|
||||
ast: structuredClone(kclManager.ast),
|
||||
selectionRanges,
|
||||
transformInfos: transforms,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
referenceSegName: '',
|
||||
})
|
||||
if (err(sketched)) return Promise.reject(sketched)
|
||||
@ -189,7 +189,7 @@ export async function applyConstraintAngleLength({
|
||||
ast: structuredClone(kclManager.ast),
|
||||
selectionRanges,
|
||||
transformInfos: transforms,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
referenceSegName: '',
|
||||
forceValueUsedInTransform: finalValue,
|
||||
})
|
||||
|
@ -55,7 +55,7 @@ export function useConvertToVariable(range?: SourceRange) {
|
||||
const { modifiedAst: _modifiedAst, pathToReplacedNode } =
|
||||
moveValueIntoNewVariable(
|
||||
ast,
|
||||
kclManager.programMemory,
|
||||
kclManager.variables,
|
||||
range || context.selectionRanges.graphSelections[0]?.codeRef?.range,
|
||||
variableName
|
||||
)
|
||||
|
@ -7,7 +7,7 @@ import { KCLError } from './errors'
|
||||
|
||||
const KclContext = createContext({
|
||||
code: codeManager?.code || '',
|
||||
programMemory: kclManager?.programMemory,
|
||||
variables: kclManager?.variables,
|
||||
ast: kclManager?.ast,
|
||||
isExecuting: kclManager?.isExecuting,
|
||||
diagnostics: kclManager?.diagnostics,
|
||||
@ -31,7 +31,7 @@ export function KclContextProvider({
|
||||
// Both the code state and the editor state start off with the same code.
|
||||
const [code, setCode] = useState(loadedCode || codeManager.code)
|
||||
|
||||
const [programMemory, setProgramMemory] = useState(kclManager.programMemory)
|
||||
const [variables, setVariables] = useState(kclManager.variables)
|
||||
const [ast, setAst] = useState(kclManager.ast)
|
||||
const [isExecuting, setIsExecuting] = useState(false)
|
||||
const [diagnostics, setDiagnostics] = useState<Diagnostic[]>([])
|
||||
@ -44,7 +44,7 @@ export function KclContextProvider({
|
||||
setCode,
|
||||
})
|
||||
kclManager.registerCallBacks({
|
||||
setProgramMemory,
|
||||
setVariables,
|
||||
setAst,
|
||||
setLogs,
|
||||
setErrors,
|
||||
@ -58,7 +58,7 @@ export function KclContextProvider({
|
||||
<KclContext.Provider
|
||||
value={{
|
||||
code,
|
||||
programMemory,
|
||||
variables,
|
||||
ast,
|
||||
isExecuting,
|
||||
diagnostics,
|
||||
|
@ -17,13 +17,14 @@ import {
|
||||
emptyExecState,
|
||||
ExecState,
|
||||
initPromise,
|
||||
KclValue,
|
||||
parse,
|
||||
PathToNode,
|
||||
Program,
|
||||
ProgramMemory,
|
||||
recast,
|
||||
SourceRange,
|
||||
topLevelRange,
|
||||
VariableMap,
|
||||
} from 'lang/wasm'
|
||||
import { getNodeFromPath, getSettingsAnnotation } from './queryAst'
|
||||
import { codeManager, editorManager, sceneInfra } from 'lib/singletons'
|
||||
@ -61,8 +62,8 @@ export class KclManager {
|
||||
trivia: [],
|
||||
}
|
||||
private _execState: ExecState = emptyExecState()
|
||||
private _programMemory: ProgramMemory = ProgramMemory.empty()
|
||||
lastSuccessfulProgramMemory: ProgramMemory = ProgramMemory.empty()
|
||||
private _variables: VariableMap = {}
|
||||
lastSuccessfulVariables: VariableMap = {}
|
||||
lastSuccessfulOperations: Operation[] = []
|
||||
private _logs: string[] = []
|
||||
private _errors: KCLError[] = []
|
||||
@ -78,7 +79,9 @@ export class KclManager {
|
||||
|
||||
private _isExecutingCallback: (arg: boolean) => void = () => {}
|
||||
private _astCallBack: (arg: Node<Program>) => void = () => {}
|
||||
private _programMemoryCallBack: (arg: ProgramMemory) => void = () => {}
|
||||
private _variablesCallBack: (arg: {
|
||||
[key in string]?: KclValue | undefined
|
||||
}) => void = () => {}
|
||||
private _logsCallBack: (arg: string[]) => void = () => {}
|
||||
private _kclErrorsCallBack: (errors: KCLError[]) => void = () => {}
|
||||
private _diagnosticsCallback: (errors: Diagnostic[]) => void = () => {}
|
||||
@ -97,18 +100,18 @@ export class KclManager {
|
||||
this._switchedFiles = switchedFiles
|
||||
}
|
||||
|
||||
get programMemory() {
|
||||
return this._programMemory
|
||||
get variables() {
|
||||
return this._variables
|
||||
}
|
||||
// This is private because callers should be setting the entire execState.
|
||||
private set programMemory(programMemory) {
|
||||
this._programMemory = programMemory
|
||||
this._programMemoryCallBack(programMemory)
|
||||
private set variables(variables) {
|
||||
this._variables = variables
|
||||
this._variablesCallBack(variables)
|
||||
}
|
||||
|
||||
private set execState(execState) {
|
||||
this._execState = execState
|
||||
this.programMemory = execState.memory
|
||||
this.variables = execState.variables
|
||||
}
|
||||
|
||||
get execState() {
|
||||
@ -201,7 +204,7 @@ export class KclManager {
|
||||
}
|
||||
|
||||
registerCallBacks({
|
||||
setProgramMemory,
|
||||
setVariables,
|
||||
setAst,
|
||||
setLogs,
|
||||
setErrors,
|
||||
@ -209,7 +212,7 @@ export class KclManager {
|
||||
setIsExecuting,
|
||||
setWasmInitFailed,
|
||||
}: {
|
||||
setProgramMemory: (arg: ProgramMemory) => void
|
||||
setVariables: (arg: VariableMap) => void
|
||||
setAst: (arg: Node<Program>) => void
|
||||
setLogs: (arg: string[]) => void
|
||||
setErrors: (errors: KCLError[]) => void
|
||||
@ -217,7 +220,7 @@ export class KclManager {
|
||||
setIsExecuting: (arg: boolean) => void
|
||||
setWasmInitFailed: (arg: boolean) => void
|
||||
}) {
|
||||
this._programMemoryCallBack = setProgramMemory
|
||||
this._variablesCallBack = setVariables
|
||||
this._astCallBack = setAst
|
||||
this._logsCallBack = setLogs
|
||||
this._kclErrorsCallBack = setErrors
|
||||
@ -329,6 +332,7 @@ export class KclManager {
|
||||
ast,
|
||||
path: codeManager.currentFilePath || undefined,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
isMock: false,
|
||||
})
|
||||
|
||||
// Program was not interrupted, setup the scene
|
||||
@ -385,11 +389,11 @@ export class KclManager {
|
||||
this.addDiagnostics(isInterrupted ? [] : kclErrorsToDiagnostics(errors))
|
||||
this.execState = execState
|
||||
if (!errors.length) {
|
||||
this.lastSuccessfulProgramMemory = execState.memory
|
||||
this.lastSuccessfulVariables = execState.variables
|
||||
this.lastSuccessfulOperations = execState.operations
|
||||
}
|
||||
this.ast = { ...ast }
|
||||
// updateArtifactGraph relies on updated executeState/programMemory
|
||||
// updateArtifactGraph relies on updated executeState/variables
|
||||
this.engineCommandManager.updateArtifactGraph(execState.artifactGraph)
|
||||
this._executeCallback()
|
||||
if (!isInterrupted) {
|
||||
@ -442,16 +446,15 @@ export class KclManager {
|
||||
const { logs, errors, execState } = await executeAst({
|
||||
ast: newAst,
|
||||
engineCommandManager: this.engineCommandManager,
|
||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||
programMemoryOverride: ProgramMemory.empty(),
|
||||
isMock: true,
|
||||
})
|
||||
|
||||
this._logs = logs
|
||||
this.addDiagnostics(kclErrorsToDiagnostics(errors))
|
||||
this._execState = execState
|
||||
this._programMemory = execState.memory
|
||||
this._variables = execState.variables
|
||||
if (!errors.length) {
|
||||
this.lastSuccessfulProgramMemory = execState.memory
|
||||
this.lastSuccessfulVariables = execState.variables
|
||||
this.lastSuccessfulOperations = execState.operations
|
||||
}
|
||||
}
|
||||
|
@ -15,8 +15,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
|> line(endAbsolute = [0.46, -5.82])
|
||||
// |> rx(45, %)`
|
||||
const execState = await enginelessExecutor(assertParse(code))
|
||||
// @ts-ignore
|
||||
const sketch001 = execState.memory.get('mySketch001')
|
||||
const sketch001 = execState.variables['mySketch001']
|
||||
expect(sketch001).toEqual({
|
||||
type: 'Sketch',
|
||||
value: {
|
||||
@ -73,8 +72,7 @@ const mySketch001 = startSketchOn('XY')
|
||||
// |> rx(45, %)
|
||||
|> extrude(length = 2)`
|
||||
const execState = await enginelessExecutor(assertParse(code))
|
||||
// @ts-ignore
|
||||
const sketch001 = execState.memory.get('mySketch001')
|
||||
const sketch001 = execState.variables['mySketch001']
|
||||
expect(sketch001).toEqual({
|
||||
type: 'Solid',
|
||||
value: {
|
||||
@ -165,9 +163,9 @@ const sk2 = startSketchOn('XY')
|
||||
|
||||
`
|
||||
const execState = await enginelessExecutor(assertParse(code))
|
||||
const programMemory = execState.memory
|
||||
const variables = execState.variables
|
||||
// @ts-ignore
|
||||
const geos = [programMemory.get('theExtrude'), programMemory.get('sk2')]
|
||||
const geos = [variables['theExtrude'], variables['sk2']]
|
||||
expect(geos).toEqual([
|
||||
{
|
||||
type: 'Solid',
|
||||
|
@ -2,12 +2,12 @@ import fs from 'node:fs'
|
||||
|
||||
import {
|
||||
assertParse,
|
||||
ProgramMemory,
|
||||
Sketch,
|
||||
initPromise,
|
||||
sketchFromKclValue,
|
||||
defaultArtifactGraph,
|
||||
topLevelRange,
|
||||
VariableMap,
|
||||
} from './wasm'
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
import { KCLError } from './errors'
|
||||
@ -21,13 +21,13 @@ describe('test executor', () => {
|
||||
const code = `const myVar = 5
|
||||
const newVar = myVar + 1`
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(5)
|
||||
expect(mem.get('newVar')?.value).toBe(6)
|
||||
expect(mem['myVar']?.value).toBe(5)
|
||||
expect(mem['newVar']?.value).toBe(6)
|
||||
})
|
||||
it('test assigning a var with a string', async () => {
|
||||
const code = `const myVar = "a str"`
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe('a str')
|
||||
expect(mem['myVar']?.value).toBe('a str')
|
||||
})
|
||||
it('test assigning a var by cont concatenating two strings string execute', async () => {
|
||||
const code = fs.readFileSync(
|
||||
@ -35,7 +35,7 @@ const newVar = myVar + 1`
|
||||
'utf-8'
|
||||
)
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe('a str another str')
|
||||
expect(mem['myVar']?.value).toBe('a str another str')
|
||||
})
|
||||
it('fn funcN = () => {} execute', async () => {
|
||||
const mem = await exe(
|
||||
@ -47,8 +47,8 @@ const newVar = myVar + 1`
|
||||
'const magicNum = funcN(9, theVar)',
|
||||
].join('\n')
|
||||
)
|
||||
expect(mem.get('theVar')?.value).toBe(60)
|
||||
expect(mem.get('magicNum')?.value).toBe(69)
|
||||
expect(mem['theVar']?.value).toBe(60)
|
||||
expect(mem['magicNum']?.value).toBe(69)
|
||||
})
|
||||
it('sketch declaration', async () => {
|
||||
let code = `const mySketch = startSketchOn('XY')
|
||||
@ -60,7 +60,7 @@ const newVar = myVar + 1`
|
||||
`
|
||||
const mem = await exe(code)
|
||||
// geo is three js buffer geometry and is very bloated to have in tests
|
||||
const sk = mem.get('mySketch')
|
||||
const sk = mem['mySketch']
|
||||
expect(sk?.type).toEqual('Sketch')
|
||||
if (sk?.type !== 'Sketch') {
|
||||
return
|
||||
@ -117,7 +117,7 @@ const newVar = myVar + 1`
|
||||
'const myVar = 5 + 1 |> myFn(%)',
|
||||
].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(7)
|
||||
expect(mem['myVar']?.value).toBe(7)
|
||||
})
|
||||
|
||||
// Enable rotations #152
|
||||
@ -130,15 +130,15 @@ const newVar = myVar + 1`
|
||||
// 'const rotated = rx(90, mySk1)',
|
||||
// ].join('\n')
|
||||
// const mem = await exe(code)
|
||||
// expect(mem.get('mySk1')?.value).toHaveLength(3)
|
||||
// expect(mem.get('rotated')?.type).toBe('Sketch')
|
||||
// expect(mem['mySk1']?.value).toHaveLength(3)
|
||||
// expect(mem['rotated')?.type).toBe('Sketch']
|
||||
// if (
|
||||
// mem.get('mySk1')?.type !== 'Sketch' ||
|
||||
// mem.get('rotated')?.type !== 'Sketch'
|
||||
// mem['mySk1']?.type !== 'Sketch' ||
|
||||
// mem['rotated']?.type !== 'Sketch'
|
||||
// )
|
||||
// throw new Error('not a sketch')
|
||||
// expect(mem.get('mySk1')?.rotation).toEqual([0, 0, 0, 1])
|
||||
// expect(mem.get('rotated')?.rotation.map((a) => a.toFixed(4))).toEqual([
|
||||
// expect(mem['mySk1']?.rotation).toEqual([0, 0, 0, 1])
|
||||
// expect(mem['rotated']?.rotation.map((a) => a.toFixed(4))).toEqual([
|
||||
// '0.7071',
|
||||
// '0.0000',
|
||||
// '0.0000',
|
||||
@ -157,7 +157,7 @@ const newVar = myVar + 1`
|
||||
// ' |> rx(90, %)',
|
||||
].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('mySk1')).toEqual({
|
||||
expect(mem['mySk1']).toEqual({
|
||||
type: 'Sketch',
|
||||
value: {
|
||||
type: 'Sketch',
|
||||
@ -236,7 +236,7 @@ const newVar = myVar + 1`
|
||||
)
|
||||
const mem = await exe(code)
|
||||
// TODO path to node is probably wrong here, zero indexes are not correct
|
||||
expect(mem.get('three')).toEqual({
|
||||
expect(mem['three']).toEqual({
|
||||
type: 'Number',
|
||||
value: 3,
|
||||
__meta: [
|
||||
@ -245,7 +245,7 @@ const newVar = myVar + 1`
|
||||
},
|
||||
],
|
||||
})
|
||||
expect(mem.get('yo')).toEqual({
|
||||
expect(mem['yo']).toEqual({
|
||||
type: 'Array',
|
||||
value: [
|
||||
{ type: 'Number', value: 1, __meta: [{ sourceRange: [28, 29, 0] }] },
|
||||
@ -263,9 +263,6 @@ const newVar = myVar + 1`
|
||||
},
|
||||
],
|
||||
})
|
||||
// Check that there are no other variables or environments.
|
||||
expect(mem.numEnvironments()).toBe(1)
|
||||
expect(mem.numVariables(0)).toBe(2)
|
||||
})
|
||||
it('execute object expression', async () => {
|
||||
const code = [
|
||||
@ -273,7 +270,7 @@ const newVar = myVar + 1`
|
||||
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
||||
].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('yo')).toEqual({
|
||||
expect(mem['yo']).toEqual({
|
||||
type: 'Object',
|
||||
value: {
|
||||
aStr: {
|
||||
@ -309,7 +306,7 @@ const newVar = myVar + 1`
|
||||
'\n'
|
||||
)
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')).toEqual({
|
||||
expect(mem['myVar']).toEqual({
|
||||
type: 'String',
|
||||
value: '123',
|
||||
__meta: [
|
||||
@ -325,80 +322,80 @@ describe('testing math operators', () => {
|
||||
it('can sum', async () => {
|
||||
const code = ['const myVar = 1 + 2'].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(3)
|
||||
expect(mem['myVar']?.value).toBe(3)
|
||||
})
|
||||
it('can subtract', async () => {
|
||||
const code = ['const myVar = 1 - 2'].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(-1)
|
||||
expect(mem['myVar']?.value).toBe(-1)
|
||||
})
|
||||
it('can multiply', async () => {
|
||||
const code = ['const myVar = 1 * 2'].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(2)
|
||||
expect(mem['myVar']?.value).toBe(2)
|
||||
})
|
||||
it('can divide', async () => {
|
||||
const code = ['const myVar = 1 / 2'].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(0.5)
|
||||
expect(mem['myVar']?.value).toBe(0.5)
|
||||
})
|
||||
it('can modulus', async () => {
|
||||
const code = ['const myVar = 5 % 2'].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(1)
|
||||
expect(mem['myVar']?.value).toBe(1)
|
||||
})
|
||||
it('can do multiple operations', async () => {
|
||||
const code = ['const myVar = 1 + 2 * 3'].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(7)
|
||||
expect(mem['myVar']?.value).toBe(7)
|
||||
})
|
||||
it('big example with parans', async () => {
|
||||
const code = ['const myVar = 1 + 2 * (3 - 4) / -5 + 6'].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(7.4)
|
||||
expect(mem['myVar']?.value).toBe(7.4)
|
||||
})
|
||||
it('with identifier', async () => {
|
||||
const code = ['const yo = 6', 'const myVar = yo / 2'].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(3)
|
||||
expect(mem['myVar']?.value).toBe(3)
|
||||
})
|
||||
it('with lots of testing', async () => {
|
||||
const code = ['const myVar = 2 * ((2 + 3 ) / 4 + 5)'].join('\n')
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(12.5)
|
||||
expect(mem['myVar']?.value).toBe(12.5)
|
||||
})
|
||||
it('with callExpression at start', async () => {
|
||||
const code = 'const myVar = min(4, 100) + 2'
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(6)
|
||||
expect(mem['myVar']?.value).toBe(6)
|
||||
})
|
||||
it('with callExpression at end', async () => {
|
||||
const code = 'const myVar = 2 + min(4, 100)'
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(6)
|
||||
expect(mem['myVar']?.value).toBe(6)
|
||||
})
|
||||
it('with nested callExpression', async () => {
|
||||
const code = 'const myVar = 2 + min(100, legLen(5, 3))'
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(6)
|
||||
expect(mem['myVar']?.value).toBe(6)
|
||||
})
|
||||
it('with unaryExpression', async () => {
|
||||
const code = 'const myVar = -min(100, 3)'
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(-3)
|
||||
expect(mem['myVar']?.value).toBe(-3)
|
||||
})
|
||||
it('with unaryExpression in callExpression', async () => {
|
||||
const code = 'const myVar = min(-legLen(5, 4), 5)'
|
||||
const code2 = 'const myVar = min(5 , -legLen(5, 4))'
|
||||
const mem = await exe(code)
|
||||
const mem2 = await exe(code2)
|
||||
expect(mem.get('myVar')?.value).toBe(-3)
|
||||
expect(mem.get('myVar')?.value).toBe(mem2.get('myVar')?.value)
|
||||
expect(mem['myVar']?.value).toBe(-3)
|
||||
expect(mem['myVar']?.value).toBe(mem2['myVar']?.value)
|
||||
})
|
||||
it('with unaryExpression in ArrayExpression', async () => {
|
||||
const code = 'const myVar = [1,-legLen(5, 4)]'
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toEqual([
|
||||
expect(mem['myVar']?.value).toEqual([
|
||||
{
|
||||
__meta: [
|
||||
{
|
||||
@ -426,7 +423,7 @@ describe('testing math operators', () => {
|
||||
'|> line(end = [-2.21, -legLen(5, min(3, 999))])',
|
||||
].join('\n')
|
||||
const mem = await exe(code)
|
||||
const sketch = sketchFromKclValue(mem.get('part001'), 'part001')
|
||||
const sketch = sketchFromKclValue(mem['part001'], 'part001')
|
||||
// result of `-legLen(5, min(3, 999))` should be -4
|
||||
const yVal = (sketch as Sketch).paths?.[0]?.to?.[1]
|
||||
expect(yVal).toBe(-4)
|
||||
@ -444,7 +441,7 @@ describe('testing math operators', () => {
|
||||
``,
|
||||
].join('\n')
|
||||
const mem = await exe(code)
|
||||
const sketch = sketchFromKclValue(mem.get('part001'), 'part001')
|
||||
const sketch = sketchFromKclValue(mem['part001'], 'part001')
|
||||
// expect -legLen(segLen('seg01'), myVar) to equal -4 setting the y value back to 0
|
||||
expect((sketch as Sketch).paths?.[1]?.from).toEqual([3, 4])
|
||||
expect((sketch as Sketch).paths?.[1]?.to).toEqual([6, 0])
|
||||
@ -454,7 +451,7 @@ describe('testing math operators', () => {
|
||||
)
|
||||
const removedUnaryExpMem = await exe(removedUnaryExp)
|
||||
const removedUnaryExpMemSketch = sketchFromKclValue(
|
||||
removedUnaryExpMem.get('part001'),
|
||||
removedUnaryExpMem['part001'],
|
||||
'part001'
|
||||
)
|
||||
|
||||
@ -464,12 +461,12 @@ describe('testing math operators', () => {
|
||||
it('with nested callExpression and binaryExpression', async () => {
|
||||
const code = 'const myVar = 2 + min(100, -1 + legLen(5, 3))'
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myVar')?.value).toBe(5)
|
||||
expect(mem['myVar']?.value).toBe(5)
|
||||
})
|
||||
it('can do power of math', async () => {
|
||||
const code = 'const myNeg2 = 4 ^ 2 - 3 ^ 2 * 2'
|
||||
const mem = await exe(code)
|
||||
expect(mem.get('myNeg2')?.value).toBe(-2)
|
||||
expect(mem['myNeg2']?.value).toBe(-2)
|
||||
})
|
||||
})
|
||||
|
||||
@ -498,12 +495,9 @@ const theExtrude = startSketchOn('XY')
|
||||
|
||||
// helpers
|
||||
|
||||
async function exe(
|
||||
code: string,
|
||||
programMemory: ProgramMemory = ProgramMemory.empty()
|
||||
) {
|
||||
async function exe(code: string, variables: VariableMap = {}) {
|
||||
const ast = assertParse(code)
|
||||
|
||||
const execState = await enginelessExecutor(ast, programMemory)
|
||||
return execState.memory
|
||||
const execState = await enginelessExecutor(ast, true, undefined, variables)
|
||||
return execState.variables
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import { assertParse, initPromise, programMemoryInit } from './wasm'
|
||||
import { assertParse, initPromise } from './wasm'
|
||||
import { enginelessExecutor } from '../lib/testHelpers'
|
||||
|
||||
import path from 'node:path'
|
||||
@ -72,7 +72,7 @@ describe('Test KCL Samples from public Github repository', () => {
|
||||
const ast = assertParse(code)
|
||||
await enginelessExecutor(
|
||||
ast,
|
||||
programMemoryInit(),
|
||||
false,
|
||||
file.pathFromProjectDirectoryToFirstFile
|
||||
)
|
||||
},
|
||||
|
@ -1,12 +1,12 @@
|
||||
import {
|
||||
Program,
|
||||
executor,
|
||||
ProgramMemory,
|
||||
executeWithEngine,
|
||||
executeMock,
|
||||
kclLint,
|
||||
emptyExecState,
|
||||
ExecState,
|
||||
VariableMap,
|
||||
} from 'lang/wasm'
|
||||
import { enginelessExecutor } from 'lib/testHelpers'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import { KCLError } from 'lang/errors'
|
||||
import { Diagnostic } from '@codemirror/lint'
|
||||
@ -48,14 +48,16 @@ export async function executeAst({
|
||||
ast,
|
||||
path,
|
||||
engineCommandManager,
|
||||
// If you set programMemoryOverride we assume you mean mock mode. Since that
|
||||
// is the only way to go about it.
|
||||
programMemoryOverride,
|
||||
isMock,
|
||||
usePrevMemory,
|
||||
variables,
|
||||
}: {
|
||||
ast: Node<Program>
|
||||
path?: string
|
||||
engineCommandManager: EngineCommandManager
|
||||
programMemoryOverride?: ProgramMemory
|
||||
isMock: boolean
|
||||
usePrevMemory?: boolean
|
||||
variables?: VariableMap
|
||||
isInterrupted?: boolean
|
||||
}): Promise<{
|
||||
logs: string[]
|
||||
@ -64,9 +66,9 @@ export async function executeAst({
|
||||
isInterrupted: boolean
|
||||
}> {
|
||||
try {
|
||||
const execState = await (programMemoryOverride
|
||||
? enginelessExecutor(ast, programMemoryOverride, path)
|
||||
: executor(ast, engineCommandManager, path))
|
||||
const execState = await (isMock
|
||||
? executeMock(ast, usePrevMemory, path, variables)
|
||||
: executeWithEngine(ast, engineCommandManager, path))
|
||||
|
||||
await engineCommandManager.waitForAllCommands()
|
||||
|
||||
|
@ -315,7 +315,7 @@ yo2 = hmm([identifierGuy + 5])`
|
||||
const startIndex = code.indexOf('100 + 100') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
ast,
|
||||
execState.memory,
|
||||
execState.variables,
|
||||
topLevelRange(startIndex, startIndex),
|
||||
'newVar'
|
||||
)
|
||||
@ -329,7 +329,7 @@ yo2 = hmm([identifierGuy + 5])`
|
||||
const startIndex = code.indexOf('2.8') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
ast,
|
||||
execState.memory,
|
||||
execState.variables,
|
||||
topLevelRange(startIndex, startIndex),
|
||||
'newVar'
|
||||
)
|
||||
@ -343,7 +343,7 @@ yo2 = hmm([identifierGuy + 5])`
|
||||
const startIndex = code.indexOf('def(')
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
ast,
|
||||
execState.memory,
|
||||
execState.variables,
|
||||
topLevelRange(startIndex, startIndex),
|
||||
'newVar'
|
||||
)
|
||||
@ -357,7 +357,7 @@ yo2 = hmm([identifierGuy + 5])`
|
||||
const startIndex = code.indexOf('jkl(') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
ast,
|
||||
execState.memory,
|
||||
execState.variables,
|
||||
topLevelRange(startIndex, startIndex),
|
||||
'newVar'
|
||||
)
|
||||
@ -371,7 +371,7 @@ yo2 = hmm([identifierGuy + 5])`
|
||||
const startIndex = code.indexOf('identifierGuy +') + 1
|
||||
const { modifiedAst } = moveValueIntoNewVariable(
|
||||
ast,
|
||||
execState.memory,
|
||||
execState.variables,
|
||||
topLevelRange(startIndex, startIndex),
|
||||
'newVar'
|
||||
)
|
||||
@ -557,7 +557,7 @@ describe('Testing deleteSegmentFromPipeExpression', () => {
|
||||
const modifiedAst = deleteSegmentFromPipeExpression(
|
||||
[],
|
||||
ast,
|
||||
execState.memory,
|
||||
execState.variables,
|
||||
code,
|
||||
pathToNode
|
||||
)
|
||||
@ -639,7 +639,7 @@ ${!replace1 ? ` |> ${line}\n` : ''} |> angledLine([-65, ${
|
||||
const modifiedAst = deleteSegmentFromPipeExpression(
|
||||
dependentSegments,
|
||||
ast,
|
||||
execState.memory,
|
||||
execState.variables,
|
||||
code,
|
||||
pathToNode
|
||||
)
|
||||
@ -745,7 +745,7 @@ describe('Testing removeSingleConstraintInfo', () => {
|
||||
pathToNode,
|
||||
argPosition,
|
||||
ast,
|
||||
execState.memory
|
||||
execState.variables
|
||||
)
|
||||
if (!mod) return new Error('mod is undefined')
|
||||
const recastCode = recast(mod.modifiedAst)
|
||||
@ -794,7 +794,7 @@ describe('Testing removeSingleConstraintInfo', () => {
|
||||
pathToNode,
|
||||
argPosition,
|
||||
ast,
|
||||
execState.memory
|
||||
execState.variables
|
||||
)
|
||||
if (!mod) return new Error('mod is undefined')
|
||||
const recastCode = recast(mod.modifiedAst)
|
||||
@ -978,7 +978,7 @@ sketch002 = startSketchOn({
|
||||
codeRef: codeRefFromRange(range, ast),
|
||||
artifact,
|
||||
},
|
||||
execState.memory,
|
||||
execState.variables,
|
||||
async () => {
|
||||
await new Promise((resolve) => setTimeout(resolve, 100))
|
||||
return {
|
||||
|
@ -18,11 +18,11 @@ import {
|
||||
UnaryExpression,
|
||||
BinaryExpression,
|
||||
PathToNode,
|
||||
ProgramMemory,
|
||||
SourceRange,
|
||||
sketchFromKclValue,
|
||||
isPathToNodeNumber,
|
||||
formatNumber,
|
||||
VariableMap,
|
||||
} from './wasm'
|
||||
import {
|
||||
isNodeSafeToReplacePath,
|
||||
@ -1218,7 +1218,7 @@ export function replaceValueAtNodePath({
|
||||
|
||||
export function moveValueIntoNewVariablePath(
|
||||
ast: Node<Program>,
|
||||
programMemory: ProgramMemory,
|
||||
memVars: VariableMap,
|
||||
pathToNode: PathToNode,
|
||||
variableName: string
|
||||
): {
|
||||
@ -1231,11 +1231,7 @@ export function moveValueIntoNewVariablePath(
|
||||
|
||||
if (!isSafe || value.type === 'Identifier') return { modifiedAst: ast }
|
||||
|
||||
const { insertIndex } = findAllPreviousVariablesPath(
|
||||
ast,
|
||||
programMemory,
|
||||
pathToNode
|
||||
)
|
||||
const { insertIndex } = findAllPreviousVariablesPath(ast, memVars, pathToNode)
|
||||
let _node = structuredClone(ast)
|
||||
const boop = replacer(_node, variableName)
|
||||
if (trap(boop)) return { modifiedAst: ast }
|
||||
@ -1251,7 +1247,7 @@ export function moveValueIntoNewVariablePath(
|
||||
|
||||
export function moveValueIntoNewVariable(
|
||||
ast: Node<Program>,
|
||||
programMemory: ProgramMemory,
|
||||
memVars: VariableMap,
|
||||
sourceRange: SourceRange,
|
||||
variableName: string
|
||||
): {
|
||||
@ -1263,11 +1259,7 @@ export function moveValueIntoNewVariable(
|
||||
const { isSafe, value, replacer } = meta
|
||||
if (!isSafe || value.type === 'Identifier') return { modifiedAst: ast }
|
||||
|
||||
const { insertIndex } = findAllPreviousVariables(
|
||||
ast,
|
||||
programMemory,
|
||||
sourceRange
|
||||
)
|
||||
const { insertIndex } = findAllPreviousVariables(ast, memVars, sourceRange)
|
||||
let _node = structuredClone(ast)
|
||||
const replaced = replacer(_node, variableName)
|
||||
if (trap(replaced)) return { modifiedAst: ast }
|
||||
@ -1289,7 +1281,7 @@ export function moveValueIntoNewVariable(
|
||||
export function deleteSegmentFromPipeExpression(
|
||||
dependentRanges: SourceRange[],
|
||||
modifiedAst: Node<Program>,
|
||||
programMemory: ProgramMemory,
|
||||
memVars: VariableMap,
|
||||
code: string,
|
||||
pathToNode: PathToNode
|
||||
): Node<Program> | Error {
|
||||
@ -1321,7 +1313,7 @@ export function deleteSegmentFromPipeExpression(
|
||||
callExp.shallowPath,
|
||||
constraintInfo.argPosition,
|
||||
_modifiedAst,
|
||||
programMemory
|
||||
memVars
|
||||
)
|
||||
if (!transform) return
|
||||
_modifiedAst = transform.modifiedAst
|
||||
@ -1350,7 +1342,7 @@ export function removeSingleConstraintInfo(
|
||||
pathToCallExp: PathToNode,
|
||||
argDetails: SimplifiedArgDetails,
|
||||
ast: Node<Program>,
|
||||
programMemory: ProgramMemory
|
||||
memVars: VariableMap
|
||||
):
|
||||
| {
|
||||
modifiedAst: Node<Program>
|
||||
@ -1367,7 +1359,7 @@ export function removeSingleConstraintInfo(
|
||||
ast,
|
||||
selectionRanges: [pathToCallExp],
|
||||
transformInfos: [transform],
|
||||
programMemory,
|
||||
memVars,
|
||||
referenceSegName: '',
|
||||
})
|
||||
if (err(retval)) return false
|
||||
@ -1377,7 +1369,7 @@ export function removeSingleConstraintInfo(
|
||||
export async function deleteFromSelection(
|
||||
ast: Node<Program>,
|
||||
selection: Selection,
|
||||
programMemory: ProgramMemory,
|
||||
variables: VariableMap,
|
||||
getFaceDetails: (id: string) => Promise<Models['FaceIsPlanar_type']> = () =>
|
||||
({} as any)
|
||||
): Promise<Node<Program> | Error> {
|
||||
@ -1506,7 +1498,7 @@ export async function deleteFromSelection(
|
||||
return
|
||||
}
|
||||
const sketchToPreserve = sketchFromKclValue(
|
||||
programMemory.get(sketchName),
|
||||
variables[sketchName],
|
||||
sketchName
|
||||
)
|
||||
if (err(sketchToPreserve)) return sketchToPreserve
|
||||
|
@ -298,7 +298,7 @@ export function getPathToExtrudeForSegmentSelection(
|
||||
const sketchVar = varDecNode.node.declaration.id.name
|
||||
|
||||
const sketch = sketchFromKclValue(
|
||||
dependencies.kclManager.programMemory.get(sketchVar),
|
||||
dependencies.kclManager.variables[sketchVar],
|
||||
sketchVar
|
||||
)
|
||||
if (trap(sketch)) return sketch
|
||||
|
@ -9,7 +9,6 @@ import {
|
||||
CallExpression,
|
||||
VariableDeclarator,
|
||||
} from './wasm'
|
||||
import { ProgramMemory } from 'lang/wasm'
|
||||
import {
|
||||
findAllPreviousVariables,
|
||||
isNodeSafeToReplace,
|
||||
@ -63,7 +62,7 @@ variableBelowShouldNotBeIncluded = 3
|
||||
|
||||
const { variables, bodyPath, insertIndex } = findAllPreviousVariables(
|
||||
ast,
|
||||
execState.memory,
|
||||
execState.variables,
|
||||
topLevelRange(rangeStart, rangeStart)
|
||||
)
|
||||
expect(variables).toEqual([
|
||||
@ -398,7 +397,7 @@ part001 = startSketchAt([-1.41, 3.46])
|
||||
selection: {
|
||||
codeRef: codeRefFromRange(topLevelRange(100, 101), ast),
|
||||
},
|
||||
programMemory: execState.memory,
|
||||
memVars: execState.variables,
|
||||
})
|
||||
expect(result).toEqual(true)
|
||||
})
|
||||
@ -418,7 +417,7 @@ part001 = startSketchAt([-1.41, 3.46])
|
||||
selection: {
|
||||
codeRef: codeRefFromRange(topLevelRange(100, 101), ast),
|
||||
},
|
||||
programMemory: execState.memory,
|
||||
memVars: execState.variables,
|
||||
})
|
||||
expect(result).toEqual(true)
|
||||
})
|
||||
@ -432,7 +431,7 @@ part001 = startSketchAt([-1.41, 3.46])
|
||||
selection: {
|
||||
codeRef: codeRefFromRange(topLevelRange(10, 11), ast),
|
||||
},
|
||||
programMemory: execState.memory,
|
||||
memVars: execState.variables,
|
||||
})
|
||||
expect(result).toEqual(false)
|
||||
})
|
||||
@ -722,7 +721,7 @@ describe('Testing specific sketch getNodeFromPath workflow', () => {
|
||||
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
||||
const modifiedAst = addCallExpressionsToPipe({
|
||||
node: ast,
|
||||
programMemory: ProgramMemory.empty(),
|
||||
variables: {},
|
||||
pathToNode: sketchPathToNode,
|
||||
expressions: [
|
||||
createCallExpressionStdLib(
|
||||
@ -777,7 +776,7 @@ describe('Testing specific sketch getNodeFromPath workflow', () => {
|
||||
const sketchPathToNode = getNodePathFromSourceRange(ast, sketchRange)
|
||||
const modifiedAst = addCloseToPipe({
|
||||
node: ast,
|
||||
programMemory: ProgramMemory.empty(),
|
||||
variables: {},
|
||||
pathToNode: sketchPathToNode,
|
||||
})
|
||||
|
||||
|
@ -13,7 +13,6 @@ import {
|
||||
PathToNode,
|
||||
PipeExpression,
|
||||
Program,
|
||||
ProgramMemory,
|
||||
ReturnStatement,
|
||||
sketchFromKclValue,
|
||||
sketchFromKclValueOptional,
|
||||
@ -26,6 +25,7 @@ import {
|
||||
kclSettings,
|
||||
unitLenToUnitLength,
|
||||
unitAngToUnitAngle,
|
||||
VariableMap,
|
||||
} from './wasm'
|
||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||
import { createIdentifier, splitPathAtLastIndex } from './modifyAst'
|
||||
@ -287,7 +287,7 @@ export interface PrevVariable<T> {
|
||||
|
||||
export function findAllPreviousVariablesPath(
|
||||
ast: Program,
|
||||
programMemory: ProgramMemory,
|
||||
memVars: VariableMap,
|
||||
path: PathToNode,
|
||||
type: 'number' | 'string' = 'number'
|
||||
): {
|
||||
@ -325,7 +325,7 @@ export function findAllPreviousVariablesPath(
|
||||
bodyItems?.forEach?.((item) => {
|
||||
if (item.type !== 'VariableDeclaration' || item.end > startRange) return
|
||||
const varName = item.declaration.id.name
|
||||
const varValue = programMemory?.get(varName)
|
||||
const varValue = memVars[varName]
|
||||
if (!varValue || typeof varValue?.value !== type) return
|
||||
variables.push({
|
||||
key: varName,
|
||||
@ -342,7 +342,7 @@ export function findAllPreviousVariablesPath(
|
||||
|
||||
export function findAllPreviousVariables(
|
||||
ast: Program,
|
||||
programMemory: ProgramMemory,
|
||||
memVars: VariableMap,
|
||||
sourceRange: SourceRange,
|
||||
type: 'number' | 'string' = 'number'
|
||||
): {
|
||||
@ -351,7 +351,7 @@ export function findAllPreviousVariables(
|
||||
insertIndex: number
|
||||
} {
|
||||
const path = getNodePathFromSourceRange(ast, sourceRange)
|
||||
return findAllPreviousVariablesPath(ast, programMemory, path, type)
|
||||
return findAllPreviousVariablesPath(ast, memVars, path, type)
|
||||
}
|
||||
|
||||
type ReplacerFn = (
|
||||
@ -479,7 +479,7 @@ function isTypeInArrayExp(
|
||||
export function isLinesParallelAndConstrained(
|
||||
ast: Program,
|
||||
artifactGraph: ArtifactGraph,
|
||||
programMemory: ProgramMemory,
|
||||
memVars: VariableMap,
|
||||
primaryLine: Selection,
|
||||
secondaryLine: Selection
|
||||
):
|
||||
@ -509,7 +509,7 @@ export function isLinesParallelAndConstrained(
|
||||
if (err(_varDec)) return _varDec
|
||||
const varDec = _varDec.node
|
||||
const varName = (varDec as VariableDeclaration)?.declaration.id?.name
|
||||
const sg = sketchFromKclValue(programMemory?.get(varName), varName)
|
||||
const sg = sketchFromKclValue(memVars[varName], varName)
|
||||
if (err(sg)) return sg
|
||||
const _primarySegment = getSketchSegmentFromSourceRange(
|
||||
sg,
|
||||
@ -589,11 +589,11 @@ export function isLinesParallelAndConstrained(
|
||||
export function hasExtrudeSketch({
|
||||
ast,
|
||||
selection,
|
||||
programMemory,
|
||||
memVars,
|
||||
}: {
|
||||
ast: Program
|
||||
selection: Selection
|
||||
programMemory: ProgramMemory
|
||||
memVars: VariableMap
|
||||
}): boolean {
|
||||
const varDecMeta = getNodeFromPath<VariableDeclaration>(
|
||||
ast,
|
||||
@ -607,7 +607,7 @@ export function hasExtrudeSketch({
|
||||
const varDec = varDecMeta.node
|
||||
if (varDec.type !== 'VariableDeclaration') return false
|
||||
const varName = varDec.declaration.id.name
|
||||
const varValue = programMemory?.get(varName)
|
||||
const varValue = memVars[varName]
|
||||
return (
|
||||
varValue?.type === 'Solid' ||
|
||||
!(sketchFromKclValueOptional(varValue, varName) instanceof Reason)
|
||||
|
@ -124,7 +124,7 @@ describe('testing changeSketchArguments', () => {
|
||||
const sourceStart = code.indexOf(lineToChange)
|
||||
const changeSketchArgsRetVal = changeSketchArguments(
|
||||
ast,
|
||||
execState.memory,
|
||||
execState.variables,
|
||||
{
|
||||
type: 'sourceRange',
|
||||
sourceRange: topLevelRange(
|
||||
@ -160,7 +160,7 @@ mySketch001 = startSketchOn('XY')
|
||||
expect(sourceStart).toBe(89)
|
||||
const newSketchLnRetVal = addNewSketchLn({
|
||||
node: ast,
|
||||
programMemory: execState.memory,
|
||||
variables: execState.variables,
|
||||
input: {
|
||||
type: 'straight-segment',
|
||||
from: [0, 0],
|
||||
@ -190,7 +190,7 @@ mySketch001 = startSketchOn('XY')
|
||||
|
||||
const modifiedAst2 = addCloseToPipe({
|
||||
node: ast,
|
||||
programMemory: execState.memory,
|
||||
variables: execState.variables,
|
||||
pathToNode: [
|
||||
['body', ''],
|
||||
[0, 'index'],
|
||||
|
@ -1,5 +1,4 @@
|
||||
import {
|
||||
ProgramMemory,
|
||||
Path,
|
||||
Sketch,
|
||||
SourceRange,
|
||||
@ -14,6 +13,7 @@ import {
|
||||
Identifier,
|
||||
sketchFromKclValue,
|
||||
topLevelRange,
|
||||
VariableMap,
|
||||
} from 'lang/wasm'
|
||||
import {
|
||||
getNodeFromPath,
|
||||
@ -355,7 +355,7 @@ function getTagKwArg(): SketchLineHelperKw['getTag'] {
|
||||
export const line: SketchLineHelperKw = {
|
||||
add: ({
|
||||
node,
|
||||
previousProgramMemory,
|
||||
variables,
|
||||
pathToNode,
|
||||
segmentInput,
|
||||
replaceExistingCallback,
|
||||
@ -494,7 +494,7 @@ export const line: SketchLineHelperKw = {
|
||||
export const lineTo: SketchLineHelperKw = {
|
||||
add: ({
|
||||
node,
|
||||
previousProgramMemory,
|
||||
variables,
|
||||
pathToNode,
|
||||
segmentInput,
|
||||
replaceExistingCallback,
|
||||
@ -1313,7 +1313,7 @@ export const angledLine: SketchLineHelper = {
|
||||
export const angledLineOfXLength: SketchLineHelper = {
|
||||
add: ({
|
||||
node,
|
||||
previousProgramMemory,
|
||||
variables,
|
||||
pathToNode,
|
||||
segmentInput,
|
||||
replaceExistingCallback,
|
||||
@ -1337,10 +1337,7 @@ export const angledLineOfXLength: SketchLineHelper = {
|
||||
const { node: varDec } = nodeMeta2
|
||||
|
||||
const variableName = varDec.id.name
|
||||
const sketch = sketchFromKclValue(
|
||||
previousProgramMemory?.get(variableName),
|
||||
variableName
|
||||
)
|
||||
const sketch = sketchFromKclValue(variables[variableName], variableName)
|
||||
if (err(sketch)) {
|
||||
return sketch
|
||||
}
|
||||
@ -1429,7 +1426,7 @@ export const angledLineOfXLength: SketchLineHelper = {
|
||||
export const angledLineOfYLength: SketchLineHelper = {
|
||||
add: ({
|
||||
node,
|
||||
previousProgramMemory,
|
||||
variables,
|
||||
pathToNode,
|
||||
segmentInput,
|
||||
replaceExistingCallback,
|
||||
@ -1452,10 +1449,7 @@ export const angledLineOfYLength: SketchLineHelper = {
|
||||
if (err(nodeMeta2)) return nodeMeta2
|
||||
const { node: varDec } = nodeMeta2
|
||||
const variableName = varDec.id.name
|
||||
const sketch = sketchFromKclValue(
|
||||
previousProgramMemory?.get(variableName),
|
||||
variableName
|
||||
)
|
||||
const sketch = sketchFromKclValue(variables[variableName], variableName)
|
||||
if (err(sketch)) return sketch
|
||||
|
||||
const angle = createLiteral(roundOff(getAngle(from, to), 0))
|
||||
@ -1793,7 +1787,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
}
|
||||
return new Error('not implemented')
|
||||
},
|
||||
updateArgs: ({ node, pathToNode, input, previousProgramMemory }) => {
|
||||
updateArgs: ({ node, pathToNode, input, variables }) => {
|
||||
if (input.type !== 'straight-segment') return STRAIGHT_SEGMENT_ERR
|
||||
const { to, from } = input
|
||||
const _node = { ...node }
|
||||
@ -1820,10 +1814,7 @@ export const angledLineThatIntersects: SketchLineHelper = {
|
||||
|
||||
const { node: varDec } = nodeMeta2
|
||||
const varName = varDec.declaration.id.name
|
||||
const sketch = sketchFromKclValue(
|
||||
previousProgramMemory.get(varName),
|
||||
varName
|
||||
)
|
||||
const sketch = sketchFromKclValue(variables[varName], varName)
|
||||
if (err(sketch)) return sketch
|
||||
const intersectPath = sketch.paths.find(
|
||||
({ tag }: Path) => tag && tag.value === intersectTagName
|
||||
@ -1996,7 +1987,7 @@ export const sketchLineHelperMapKw: { [key: string]: SketchLineHelperKw } = {
|
||||
|
||||
export function changeSketchArguments(
|
||||
node: Node<Program>,
|
||||
programMemory: ProgramMemory,
|
||||
variables: VariableMap,
|
||||
sourceRangeOrPath:
|
||||
| {
|
||||
type: 'sourceRange'
|
||||
@ -2030,7 +2021,7 @@ export function changeSketchArguments(
|
||||
|
||||
return updateArgs({
|
||||
node: _node,
|
||||
previousProgramMemory: programMemory,
|
||||
variables,
|
||||
pathToNode: shallowPath,
|
||||
input,
|
||||
})
|
||||
@ -2047,7 +2038,7 @@ export function changeSketchArguments(
|
||||
|
||||
return updateArgs({
|
||||
node: _node,
|
||||
previousProgramMemory: programMemory,
|
||||
variables,
|
||||
pathToNode: shallowPath,
|
||||
input,
|
||||
})
|
||||
@ -2112,7 +2103,7 @@ export function compareVec2Epsilon2(
|
||||
|
||||
interface CreateLineFnCallArgs {
|
||||
node: Node<Program>
|
||||
programMemory: ProgramMemory
|
||||
variables: VariableMap
|
||||
input: SegmentInputs
|
||||
fnName: ToolTip
|
||||
pathToNode: PathToNode
|
||||
@ -2121,7 +2112,7 @@ interface CreateLineFnCallArgs {
|
||||
|
||||
export function addNewSketchLn({
|
||||
node: _node,
|
||||
programMemory: previousProgramMemory,
|
||||
variables,
|
||||
fnName,
|
||||
pathToNode,
|
||||
input: segmentInput,
|
||||
@ -2151,7 +2142,7 @@ export function addNewSketchLn({
|
||||
)
|
||||
return add({
|
||||
node,
|
||||
previousProgramMemory,
|
||||
variables,
|
||||
pathToNode,
|
||||
segmentInput,
|
||||
spliceBetween,
|
||||
@ -2164,7 +2155,7 @@ export function addCallExpressionsToPipe({
|
||||
expressions,
|
||||
}: {
|
||||
node: Node<Program>
|
||||
programMemory: ProgramMemory
|
||||
variables: VariableMap
|
||||
pathToNode: PathToNode
|
||||
expressions: Node<CallExpression | CallExpressionKw>[]
|
||||
}) {
|
||||
@ -2188,7 +2179,7 @@ export function addCloseToPipe({
|
||||
pathToNode,
|
||||
}: {
|
||||
node: Program
|
||||
programMemory: ProgramMemory
|
||||
variables: VariableMap
|
||||
pathToNode: PathToNode
|
||||
}) {
|
||||
const _node = { ...node }
|
||||
@ -2209,7 +2200,7 @@ export function addCloseToPipe({
|
||||
|
||||
export function replaceSketchLine({
|
||||
node,
|
||||
programMemory,
|
||||
variables,
|
||||
pathToNode: _pathToNode,
|
||||
fnName,
|
||||
segmentInput,
|
||||
@ -2217,7 +2208,7 @@ export function replaceSketchLine({
|
||||
referencedSegment,
|
||||
}: {
|
||||
node: Node<Program>
|
||||
programMemory: ProgramMemory
|
||||
variables: VariableMap
|
||||
pathToNode: PathToNode
|
||||
fnName: ToolTip
|
||||
segmentInput: SegmentInputs
|
||||
@ -2241,7 +2232,7 @@ export function replaceSketchLine({
|
||||
: sketchLineHelperMap[fnName]
|
||||
const addRetVal = add({
|
||||
node: _node,
|
||||
previousProgramMemory: programMemory,
|
||||
variables,
|
||||
pathToNode: _pathToNode,
|
||||
referencedSegment,
|
||||
segmentInput,
|
||||
|
@ -53,7 +53,7 @@ async function testingSwapSketchFnCall({
|
||||
return Promise.reject(new Error('transformInfos undefined'))
|
||||
const ast2 = transformAstSketchLines({
|
||||
ast,
|
||||
programMemory: execState.memory,
|
||||
memVars: execState.variables,
|
||||
selectionRanges: selections,
|
||||
transformInfos,
|
||||
referenceSegName: '',
|
||||
@ -373,7 +373,7 @@ part001 = startSketchOn('XY')
|
||||
const execState = await enginelessExecutor(assertParse(code))
|
||||
const index = code.indexOf('// normal-segment') - 7
|
||||
const sg = sketchFromKclValue(
|
||||
execState.memory.get('part001'),
|
||||
execState.variables['part001'],
|
||||
'part001'
|
||||
) as Sketch
|
||||
const _segment = getSketchSegmentFromSourceRange(
|
||||
@ -393,7 +393,7 @@ part001 = startSketchOn('XY')
|
||||
const execState = await enginelessExecutor(assertParse(code))
|
||||
const index = code.indexOf('// segment-in-start') - 7
|
||||
const _segment = getSketchSegmentFromSourceRange(
|
||||
sketchFromKclValue(execState.memory.get('part001'), 'part001') as Sketch,
|
||||
sketchFromKclValue(execState.variables['part001'], 'part001') as Sketch,
|
||||
topLevelRange(index, index)
|
||||
)
|
||||
if (err(_segment)) throw _segment
|
||||
|
@ -193,7 +193,7 @@ describe('testing transformAstForSketchLines for equal length constraint', () =>
|
||||
ast,
|
||||
selectionRanges: transformedSelection,
|
||||
transformInfos,
|
||||
programMemory: execState.memory,
|
||||
memVars: execState.variables,
|
||||
})
|
||||
if (err(newAst)) return Promise.reject(newAst)
|
||||
|
||||
@ -356,7 +356,7 @@ part001 = startSketchOn('XY')
|
||||
ast,
|
||||
selectionRanges: makeSelections(selectionRanges),
|
||||
transformInfos,
|
||||
programMemory: execState.memory,
|
||||
memVars: execState.variables,
|
||||
})
|
||||
if (err(newAst)) return Promise.reject(newAst)
|
||||
|
||||
@ -445,7 +445,7 @@ part001 = startSketchOn('XY')
|
||||
ast,
|
||||
selectionRanges: makeSelections(selectionRanges),
|
||||
transformInfos,
|
||||
programMemory: execState.memory,
|
||||
memVars: execState.variables,
|
||||
referenceSegName: '',
|
||||
})
|
||||
if (err(newAst)) return Promise.reject(newAst)
|
||||
@ -505,7 +505,7 @@ part001 = startSketchOn('XY')
|
||||
ast,
|
||||
selectionRanges: makeSelections(selectionRanges),
|
||||
transformInfos,
|
||||
programMemory: execState.memory,
|
||||
memVars: execState.variables,
|
||||
referenceSegName: '',
|
||||
})
|
||||
if (err(newAst)) return Promise.reject(newAst)
|
||||
@ -600,7 +600,7 @@ async function helperThing(
|
||||
ast,
|
||||
selectionRanges: makeSelections(selectionRanges),
|
||||
transformInfos,
|
||||
programMemory: execState.memory,
|
||||
memVars: execState.variables,
|
||||
})
|
||||
|
||||
if (err(newAst)) return Promise.reject(newAst)
|
||||
|
@ -17,13 +17,13 @@ import {
|
||||
BinaryPart,
|
||||
VariableDeclarator,
|
||||
PathToNode,
|
||||
ProgramMemory,
|
||||
sketchFromKclValue,
|
||||
Literal,
|
||||
SourceRange,
|
||||
LiteralValue,
|
||||
recast,
|
||||
LabeledArg,
|
||||
VariableMap,
|
||||
} from '../wasm'
|
||||
import { getNodeFromPath, getNodeFromPathCurry } from '../queryAst'
|
||||
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
||||
@ -1745,14 +1745,14 @@ export function transformSecondarySketchLinesTagFirst({
|
||||
ast,
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory,
|
||||
memVars,
|
||||
forceSegName,
|
||||
forceValueUsedInTransform,
|
||||
}: {
|
||||
ast: Node<Program>
|
||||
selectionRanges: Selections
|
||||
transformInfos: TransformInfo[]
|
||||
programMemory: ProgramMemory
|
||||
memVars: VariableMap
|
||||
forceSegName?: string
|
||||
forceValueUsedInTransform?: BinaryPart
|
||||
}):
|
||||
@ -1788,7 +1788,7 @@ export function transformSecondarySketchLinesTagFirst({
|
||||
},
|
||||
referencedSegmentRange: primarySelection,
|
||||
transformInfos,
|
||||
programMemory,
|
||||
memVars,
|
||||
referenceSegName: tag,
|
||||
forceValueUsedInTransform,
|
||||
})
|
||||
@ -1822,7 +1822,7 @@ export function transformAstSketchLines({
|
||||
ast,
|
||||
selectionRanges,
|
||||
transformInfos,
|
||||
programMemory,
|
||||
memVars,
|
||||
referenceSegName,
|
||||
forceValueUsedInTransform,
|
||||
referencedSegmentRange,
|
||||
@ -1830,7 +1830,7 @@ export function transformAstSketchLines({
|
||||
ast: Node<Program>
|
||||
selectionRanges: Selections | PathToNode[]
|
||||
transformInfos: TransformInfo[]
|
||||
programMemory: ProgramMemory
|
||||
memVars: VariableMap
|
||||
referenceSegName: string
|
||||
referencedSegmentRange?: SourceRange
|
||||
forceValueUsedInTransform?: BinaryPart
|
||||
@ -1946,7 +1946,7 @@ export function transformAstSketchLines({
|
||||
})
|
||||
|
||||
const varName = varDec.node.id.name
|
||||
let kclVal = programMemory.get(varName)
|
||||
let kclVal = memVars[varName]
|
||||
let sketch
|
||||
if (kclVal?.type === 'Solid') {
|
||||
sketch = kclVal.value.sketch
|
||||
@ -1977,7 +1977,7 @@ export function transformAstSketchLines({
|
||||
// Note to ADAM: Here is where the replaceExisting call gets sent.
|
||||
const replacedSketchLine = replaceSketchLine({
|
||||
node: node,
|
||||
programMemory,
|
||||
variables: memVars,
|
||||
pathToNode: _pathToNode,
|
||||
referencedSegment,
|
||||
fnName: transformTo || (call.node.callee.name as ToolTip),
|
||||
|
@ -18,8 +18,8 @@ describe('testing angledLineThatIntersects', () => {
|
||||
}, %, $yo2)
|
||||
intersect = segEndX(yo2)`
|
||||
const execState = await enginelessExecutor(assertParse(code('-1')))
|
||||
expect(execState.memory.get('intersect')?.value).toBe(1 + Math.sqrt(2))
|
||||
expect(execState.variables['intersect']?.value).toBe(1 + Math.sqrt(2))
|
||||
const noOffset = await enginelessExecutor(assertParse(code('0')))
|
||||
expect(noOffset.memory.get('intersect')?.value).toBeCloseTo(1)
|
||||
expect(noOffset.variables['intersect']?.value).toBeCloseTo(1)
|
||||
})
|
||||
})
|
||||
|
@ -1,6 +1,5 @@
|
||||
import { ToolTip } from 'lang/langHelpers'
|
||||
import {
|
||||
ProgramMemory,
|
||||
Path,
|
||||
SourceRange,
|
||||
Program,
|
||||
@ -10,14 +9,15 @@ import {
|
||||
Literal,
|
||||
BinaryPart,
|
||||
CallExpressionKw,
|
||||
VariableMap,
|
||||
} from '../wasm'
|
||||
import { LineInputsType } from './sketchcombos'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
|
||||
export interface ModifyAstBase {
|
||||
node: Node<Program>
|
||||
// TODO #896: Remove ProgramMemory from this interface
|
||||
previousProgramMemory: ProgramMemory
|
||||
// TODO #896: Remove memory variables from this interface
|
||||
variables: VariableMap
|
||||
pathToNode: PathToNode
|
||||
}
|
||||
|
||||
|
@ -18,7 +18,7 @@ it('can execute parsed AST', async () => {
|
||||
expect(pResult.program).not.toEqual(null)
|
||||
const execState = await enginelessExecutor(pResult.program as Node<Program>)
|
||||
expect(err(execState)).toEqual(false)
|
||||
expect(execState.memory.get('x')?.value).toEqual(1)
|
||||
expect(execState.variables['x']?.value).toEqual(1)
|
||||
})
|
||||
|
||||
it('formats numbers with units', () => {
|
||||
|
327
src/lang/wasm.ts
327
src/lang/wasm.ts
@ -3,7 +3,8 @@ import {
|
||||
parse_wasm,
|
||||
recast_wasm,
|
||||
format_number,
|
||||
execute,
|
||||
execute_with_engine,
|
||||
execute_mock,
|
||||
kcl_lint,
|
||||
modify_ast_for_sketch_wasm,
|
||||
is_points_ccw,
|
||||
@ -281,6 +282,8 @@ export const assertParse = (code: string): Node<Program> => {
|
||||
return result.program
|
||||
}
|
||||
|
||||
export type VariableMap = { [key in string]?: KclValue }
|
||||
|
||||
export type PathToNode = [string | number, string][]
|
||||
|
||||
export const isPathToNodeNumber = (
|
||||
@ -290,7 +293,7 @@ export const isPathToNodeNumber = (
|
||||
}
|
||||
|
||||
export interface ExecState {
|
||||
memory: ProgramMemory
|
||||
variables: { [key in string]?: KclValue }
|
||||
operations: Operation[]
|
||||
artifacts: { [key in ArtifactId]?: RustArtifact }
|
||||
artifactCommands: ArtifactCommand[]
|
||||
@ -303,7 +306,7 @@ export interface ExecState {
|
||||
*/
|
||||
export function emptyExecState(): ExecState {
|
||||
return {
|
||||
memory: ProgramMemory.empty(),
|
||||
variables: {},
|
||||
operations: [],
|
||||
artifacts: {},
|
||||
artifactCommands: [],
|
||||
@ -328,7 +331,7 @@ function execStateFromRust(
|
||||
}
|
||||
|
||||
return {
|
||||
memory: ProgramMemory.fromRaw(execOutcome.memory),
|
||||
variables: execOutcome.variables,
|
||||
operations: execOutcome.operations,
|
||||
artifacts: execOutcome.artifacts,
|
||||
artifactCommands: execOutcome.artifactCommands,
|
||||
@ -336,6 +339,16 @@ function execStateFromRust(
|
||||
}
|
||||
}
|
||||
|
||||
function mockExecStateFromRust(execOutcome: RustExecOutcome): ExecState {
|
||||
return {
|
||||
variables: execOutcome.variables,
|
||||
operations: execOutcome.operations,
|
||||
artifacts: execOutcome.artifacts,
|
||||
artifactCommands: execOutcome.artifactCommands,
|
||||
artifactGraph: new Map<ArtifactId, Artifact>(),
|
||||
}
|
||||
}
|
||||
|
||||
export type ArtifactGraph = Map<ArtifactId, Artifact>
|
||||
|
||||
function rustArtifactGraphToMap(
|
||||
@ -354,203 +367,6 @@ export function defaultArtifactGraph(): ArtifactGraph {
|
||||
return new Map()
|
||||
}
|
||||
|
||||
interface Memory {
|
||||
[key: string]: KclValue | undefined
|
||||
}
|
||||
|
||||
const ROOT_ENVIRONMENT_REF: EnvironmentRef = 0
|
||||
|
||||
function emptyEnvironment(): Environment {
|
||||
return { bindings: {}, parent: null }
|
||||
}
|
||||
|
||||
function emptyRootEnvironment(): Environment {
|
||||
return {
|
||||
// This is dumb this is copied from rust.
|
||||
bindings: {
|
||||
ZERO: { type: 'Number', value: 0.0, __meta: [] },
|
||||
QUARTER_TURN: { type: 'Number', value: 90.0, __meta: [] },
|
||||
HALF_TURN: { type: 'Number', value: 180.0, __meta: [] },
|
||||
THREE_QUARTER_TURN: { type: 'Number', value: 270.0, __meta: [] },
|
||||
},
|
||||
parent: null,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This duplicates logic in Rust. The hope is to keep ProgramMemory internals
|
||||
* isolated from the rest of the TypeScript code so that we can move it to Rust
|
||||
* in the future.
|
||||
*/
|
||||
export class ProgramMemory {
|
||||
private environments: Environment[]
|
||||
private currentEnv: EnvironmentRef
|
||||
private return: KclValue | null
|
||||
|
||||
/**
|
||||
* Empty memory doesn't include prelude definitions.
|
||||
*/
|
||||
static empty(): ProgramMemory {
|
||||
return new ProgramMemory()
|
||||
}
|
||||
|
||||
static fromRaw(raw: RawProgramMemory): ProgramMemory {
|
||||
return new ProgramMemory(raw.environments, raw.currentEnv, raw.return)
|
||||
}
|
||||
|
||||
constructor(
|
||||
environments: Environment[] = [emptyRootEnvironment()],
|
||||
currentEnv: EnvironmentRef = ROOT_ENVIRONMENT_REF,
|
||||
returnVal: KclValue | null = null
|
||||
) {
|
||||
this.environments = environments
|
||||
this.currentEnv = currentEnv
|
||||
this.return = returnVal
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a deep copy.
|
||||
*/
|
||||
clone(): ProgramMemory {
|
||||
return ProgramMemory.fromRaw(structuredClone(this.toRaw()))
|
||||
}
|
||||
|
||||
has(name: string): boolean {
|
||||
let envRef = this.currentEnv
|
||||
while (true) {
|
||||
const env = this.environments[envRef]
|
||||
if (env.bindings.hasOwnProperty(name)) {
|
||||
return true
|
||||
}
|
||||
if (!env.parent) {
|
||||
break
|
||||
}
|
||||
envRef = env.parent
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
get(name: string): KclValue | null {
|
||||
let envRef = this.currentEnv
|
||||
while (true) {
|
||||
const env = this.environments[envRef]
|
||||
if (env.bindings.hasOwnProperty(name)) {
|
||||
return env.bindings[name] ?? null
|
||||
}
|
||||
if (!env.parent) {
|
||||
break
|
||||
}
|
||||
envRef = env.parent
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
set(name: string, value: KclValue): Error | null {
|
||||
if (this.environments.length === 0) {
|
||||
return new Error('No environment to set memory in')
|
||||
}
|
||||
const env = this.environments[this.currentEnv]
|
||||
env.bindings[name] = value
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new ProgramMemory with only `KclValue`s that pass the
|
||||
* predicate. Values are deep copied.
|
||||
*
|
||||
* Note: Return value of the returned ProgramMemory is always null.
|
||||
*/
|
||||
filterVariables(
|
||||
keepPrelude: boolean,
|
||||
predicate: (value: KclValue) => boolean
|
||||
): ProgramMemory | Error {
|
||||
const environments: Environment[] = []
|
||||
for (const [i, env] of this.environments.entries()) {
|
||||
let bindings: Memory
|
||||
if (i === ROOT_ENVIRONMENT_REF && keepPrelude) {
|
||||
// Get prelude definitions. Create these first so that they're always
|
||||
// first in iteration order.
|
||||
const memoryOrError = programMemoryInit()
|
||||
if (err(memoryOrError)) return memoryOrError
|
||||
bindings = memoryOrError.environments[0].bindings
|
||||
} else {
|
||||
bindings = emptyEnvironment().bindings
|
||||
}
|
||||
|
||||
for (const [name, value] of Object.entries(env.bindings)) {
|
||||
if (value === undefined) continue
|
||||
// Check the predicate.
|
||||
if (!predicate(value)) {
|
||||
continue
|
||||
}
|
||||
// Deep copy.
|
||||
bindings[name] = structuredClone(value)
|
||||
}
|
||||
environments.push({ bindings, parent: env.parent })
|
||||
}
|
||||
return new ProgramMemory(environments, this.currentEnv, null)
|
||||
}
|
||||
|
||||
numEnvironments(): number {
|
||||
return this.environments.length
|
||||
}
|
||||
|
||||
numVariables(envRef: EnvironmentRef): number {
|
||||
return Object.keys(this.environments[envRef]).length
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all variable entries in memory that are visible, in a flat
|
||||
* structure. If variables are shadowed, they're not visible, and therefore,
|
||||
* not included.
|
||||
*
|
||||
* This should only be used to display in the MemoryPane UI.
|
||||
*/
|
||||
visibleEntries(): Map<string, KclValue> {
|
||||
const map = new Map<string, KclValue>()
|
||||
let envRef = this.currentEnv
|
||||
while (true) {
|
||||
const env = this.environments[envRef]
|
||||
for (const [name, value] of Object.entries(env.bindings)) {
|
||||
if (value === undefined) continue
|
||||
// Don't include shadowed variables.
|
||||
if (!map.has(name)) {
|
||||
map.set(name, value)
|
||||
}
|
||||
}
|
||||
if (!env.parent) {
|
||||
break
|
||||
}
|
||||
envRef = env.parent
|
||||
}
|
||||
return map
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if any visible variables are a Sketch or Solid.
|
||||
*/
|
||||
hasSketchOrSolid(): boolean {
|
||||
for (const node of this.visibleEntries().values()) {
|
||||
if (node.type === 'Solid' || node.type === 'Sketch') {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the representation that can be serialized to JSON. This should only
|
||||
* be used within this module.
|
||||
*/
|
||||
toRaw(): RawProgramMemory {
|
||||
return {
|
||||
environments: this.environments,
|
||||
currentEnv: this.currentEnv,
|
||||
return: this.return,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: In the future, make the parameter be a KclValue.
|
||||
export function sketchFromKclValueOptional(
|
||||
obj: any,
|
||||
@ -590,24 +406,60 @@ export function sketchFromKclValue(
|
||||
* @param node The AST of the program to execute.
|
||||
* @param path The full path of the file being executed. Use `null` for
|
||||
* expressions that don't have a file, like expressions in the command bar.
|
||||
* @param programMemoryOverride If this is not `null`, this will be used as the
|
||||
* initial program memory, and the execution will be engineless (AKA mock
|
||||
* execution).
|
||||
*/
|
||||
export const executor = async (
|
||||
export const executeMock = async (
|
||||
node: Node<Program>,
|
||||
usePrevMemory?: boolean,
|
||||
path?: string,
|
||||
variables?: { [key in string]?: KclValue }
|
||||
): Promise<ExecState> => {
|
||||
try {
|
||||
if (!variables) {
|
||||
variables = {}
|
||||
}
|
||||
if (usePrevMemory === undefined) {
|
||||
usePrevMemory = true
|
||||
}
|
||||
const execOutcome: RustExecOutcome = await execute_mock(
|
||||
JSON.stringify(node),
|
||||
path,
|
||||
JSON.stringify({ settings: await jsAppSettings() }),
|
||||
usePrevMemory,
|
||||
JSON.stringify(variables),
|
||||
fileSystemManager
|
||||
)
|
||||
return mockExecStateFromRust(execOutcome)
|
||||
} catch (e: any) {
|
||||
return Promise.reject(errFromErrWithOutputs(e))
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a KCL program.
|
||||
* @param node The AST of the program to execute.
|
||||
* @param path The full path of the file being executed. Use `null` for
|
||||
* expressions that don't have a file, like expressions in the command bar.
|
||||
*/
|
||||
export const executeWithEngine = async (
|
||||
node: Node<Program>,
|
||||
engineCommandManager: EngineCommandManager,
|
||||
path?: string,
|
||||
programMemoryOverride: ProgramMemory | Error | null = null
|
||||
path?: string
|
||||
): Promise<ExecState> => {
|
||||
if (programMemoryOverride !== null && err(programMemoryOverride))
|
||||
return Promise.reject(programMemoryOverride)
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
if (programMemoryOverride !== null && err(programMemoryOverride))
|
||||
return Promise.reject(programMemoryOverride)
|
||||
|
||||
try {
|
||||
const execOutcome: RustExecOutcome = await execute_with_engine(
|
||||
JSON.stringify(node),
|
||||
path,
|
||||
JSON.stringify({ settings: await jsAppSettings() }),
|
||||
engineCommandManager,
|
||||
fileSystemManager
|
||||
)
|
||||
return execStateFromRust(execOutcome, node)
|
||||
} catch (e: any) {
|
||||
return Promise.reject(errFromErrWithOutputs(e))
|
||||
}
|
||||
}
|
||||
|
||||
const jsAppSettings = async () => {
|
||||
let jsAppSettings = default_app_settings()
|
||||
if (!TEST) {
|
||||
const lastSettingsSnapshot = await import(
|
||||
@ -617,19 +469,13 @@ export const executor = async (
|
||||
jsAppSettings = getAllCurrentSettings(lastSettingsSnapshot)
|
||||
}
|
||||
}
|
||||
const execOutcome: RustExecOutcome = await execute(
|
||||
JSON.stringify(node),
|
||||
path,
|
||||
JSON.stringify(programMemoryOverride?.toRaw() || null),
|
||||
JSON.stringify({ settings: jsAppSettings }),
|
||||
engineCommandManager,
|
||||
fileSystemManager
|
||||
)
|
||||
return execStateFromRust(execOutcome, node)
|
||||
} catch (e: any) {
|
||||
console.log(e)
|
||||
return jsAppSettings
|
||||
}
|
||||
|
||||
const errFromErrWithOutputs = (e: any): KCLError => {
|
||||
console.log('execute error', e)
|
||||
const parsed: KclErrorWithOutputs = JSON.parse(e.toString())
|
||||
const kclError = new KCLError(
|
||||
return new KCLError(
|
||||
parsed.error.kind,
|
||||
parsed.error.msg,
|
||||
firstSourceRange(parsed.error),
|
||||
@ -637,9 +483,6 @@ export const executor = async (
|
||||
parsed.artifactCommands,
|
||||
rustArtifactGraphToMap(parsed.artifactGraph)
|
||||
)
|
||||
|
||||
return Promise.reject(kclError)
|
||||
}
|
||||
}
|
||||
|
||||
export const kclLint = async (ast: Program): Promise<Array<Discovered>> => {
|
||||
@ -707,7 +550,6 @@ export const modifyAstForSketch = async (
|
||||
defaultArtifactGraph()
|
||||
)
|
||||
|
||||
console.log(kclError)
|
||||
return Promise.reject(kclError)
|
||||
}
|
||||
}
|
||||
@ -755,31 +597,6 @@ export function getTangentialArcToInfo({
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns new ProgramMemory with prelude definitions.
|
||||
*/
|
||||
export function programMemoryInit(): ProgramMemory | Error {
|
||||
try {
|
||||
const memory: RawProgramMemory = program_memory_init()
|
||||
return new ProgramMemory(
|
||||
memory.environments,
|
||||
memory.currentEnv,
|
||||
memory.return
|
||||
)
|
||||
} catch (e: any) {
|
||||
console.log(e)
|
||||
const parsed: RustKclError = JSON.parse(e.toString())
|
||||
return new KCLError(
|
||||
parsed.kind,
|
||||
parsed.msg,
|
||||
firstSourceRange(parsed),
|
||||
[],
|
||||
[],
|
||||
defaultArtifactGraph()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
export async function coreDump(
|
||||
coreDumpManager: CoreDumpManager,
|
||||
openGithubIssue: boolean = false
|
||||
|
@ -576,7 +576,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
ast: structuredClone(kclManager.ast),
|
||||
selectionRanges,
|
||||
transformInfos: transforms,
|
||||
programMemory: kclManager.programMemory,
|
||||
memVars: kclManager.variables,
|
||||
referenceSegName: '',
|
||||
})
|
||||
if (err(sketched)) return KCL_DEFAULT_LENGTH
|
||||
|
@ -1,67 +1,55 @@
|
||||
import { ParseResult, ProgramMemory } from 'lang/wasm'
|
||||
import { ParseResult, VariableMap } from 'lang/wasm'
|
||||
import { getCalculatedKclExpressionValue } from './kclHelpers'
|
||||
|
||||
describe('KCL expression calculations', () => {
|
||||
it('calculates a simple expression', async () => {
|
||||
const actual = await getCalculatedKclExpressionValue({
|
||||
value: '1 + 2',
|
||||
programMemory: ProgramMemory.empty(),
|
||||
})
|
||||
const actual = await getCalculatedKclExpressionValue('1 + 2', {})
|
||||
const coercedActual = actual as Exclude<typeof actual, Error | ParseResult>
|
||||
expect(coercedActual).not.toHaveProperty('errors')
|
||||
expect(coercedActual.valueAsString).toEqual('3')
|
||||
expect(coercedActual?.astNode).toBeDefined()
|
||||
})
|
||||
it('calculates a simple expression with a variable', async () => {
|
||||
const programMemory = ProgramMemory.empty()
|
||||
programMemory.set('x', {
|
||||
const variables: VariableMap = {}
|
||||
variables['x'] = {
|
||||
type: 'Number',
|
||||
value: 2,
|
||||
__meta: [],
|
||||
})
|
||||
const actual = await getCalculatedKclExpressionValue({
|
||||
value: '1 + x',
|
||||
programMemory,
|
||||
})
|
||||
}
|
||||
const actual = await getCalculatedKclExpressionValue('1 + x', variables)
|
||||
const coercedActual = actual as Exclude<typeof actual, Error | ParseResult>
|
||||
expect(coercedActual.valueAsString).toEqual('3')
|
||||
expect(coercedActual.astNode).toBeDefined()
|
||||
})
|
||||
it('returns NAN for an invalid expression', async () => {
|
||||
const actual = await getCalculatedKclExpressionValue({
|
||||
value: '1 + x',
|
||||
programMemory: ProgramMemory.empty(),
|
||||
})
|
||||
const actual = await getCalculatedKclExpressionValue('1 + x', {})
|
||||
const coercedActual = actual as Exclude<typeof actual, Error | ParseResult>
|
||||
expect(coercedActual.valueAsString).toEqual('NAN')
|
||||
expect(coercedActual.astNode).toBeDefined()
|
||||
})
|
||||
it('returns NAN for an expression with an invalid variable', async () => {
|
||||
const programMemory = ProgramMemory.empty()
|
||||
programMemory.set('y', {
|
||||
const variables: VariableMap = {}
|
||||
variables['y'] = {
|
||||
type: 'Number',
|
||||
value: 2,
|
||||
__meta: [],
|
||||
})
|
||||
const actual = await getCalculatedKclExpressionValue({
|
||||
value: '1 + x',
|
||||
programMemory,
|
||||
})
|
||||
}
|
||||
const actual = await getCalculatedKclExpressionValue('1 + x', variables)
|
||||
const coercedActual = actual as Exclude<typeof actual, Error | ParseResult>
|
||||
expect(coercedActual.valueAsString).toEqual('NAN')
|
||||
expect(coercedActual.astNode).toBeDefined()
|
||||
})
|
||||
it('calculates a more complex expression with a variable', async () => {
|
||||
const programMemory = ProgramMemory.empty()
|
||||
programMemory.set('x', {
|
||||
const variables: VariableMap = {}
|
||||
variables['x'] = {
|
||||
type: 'Number',
|
||||
value: 2,
|
||||
__meta: [],
|
||||
})
|
||||
const actual = await getCalculatedKclExpressionValue({
|
||||
value: '(1 + x * x) * 2',
|
||||
programMemory,
|
||||
})
|
||||
}
|
||||
const actual = await getCalculatedKclExpressionValue(
|
||||
'(1 + x * x) * 2',
|
||||
variables
|
||||
)
|
||||
const coercedActual = actual as Exclude<typeof actual, Error | ParseResult>
|
||||
expect(coercedActual.valueAsString).toEqual('10')
|
||||
expect(coercedActual.astNode).toBeDefined()
|
||||
|
@ -1,48 +1,20 @@
|
||||
import { err } from './trap'
|
||||
import { engineCommandManager } from 'lib/singletons'
|
||||
import { parse, ProgramMemory, programMemoryInit, resultIsOk } from 'lang/wasm'
|
||||
import { parse, resultIsOk, VariableMap } from 'lang/wasm'
|
||||
import { PrevVariable } from 'lang/queryAst'
|
||||
import { executeAst } from 'lang/langHelpers'
|
||||
import { KclExpression } from './commandTypes'
|
||||
|
||||
const DUMMY_VARIABLE_NAME = '__result__'
|
||||
|
||||
export function programMemoryFromVariables(
|
||||
variables: PrevVariable<string | number>[]
|
||||
): ProgramMemory | Error {
|
||||
const memory = programMemoryInit()
|
||||
if (err(memory)) return memory
|
||||
for (const { key, value } of variables) {
|
||||
const error = memory.set(
|
||||
key,
|
||||
typeof value === 'number'
|
||||
? {
|
||||
type: 'Number',
|
||||
value,
|
||||
__meta: [],
|
||||
}
|
||||
: {
|
||||
type: 'String',
|
||||
value,
|
||||
__meta: [],
|
||||
}
|
||||
)
|
||||
if (err(error)) return error
|
||||
}
|
||||
return memory
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate the value of the KCL expression,
|
||||
* given the value and the variables that are available
|
||||
*/
|
||||
export async function getCalculatedKclExpressionValue({
|
||||
value,
|
||||
programMemory,
|
||||
}: {
|
||||
value: string
|
||||
programMemory: ProgramMemory
|
||||
}) {
|
||||
export async function getCalculatedKclExpressionValue(
|
||||
value: string,
|
||||
variables: VariableMap
|
||||
) {
|
||||
// Create a one-line program that assigns the value to a variable
|
||||
const dummyProgramCode = `const ${DUMMY_VARIABLE_NAME} = ${value}`
|
||||
const pResult = parse(dummyProgramCode)
|
||||
@ -53,7 +25,8 @@ export async function getCalculatedKclExpressionValue({
|
||||
const { execState } = await executeAst({
|
||||
ast,
|
||||
engineCommandManager,
|
||||
programMemoryOverride: programMemory,
|
||||
isMock: true,
|
||||
variables,
|
||||
})
|
||||
|
||||
// Find the variable declaration for the result
|
||||
@ -65,7 +38,7 @@ export async function getCalculatedKclExpressionValue({
|
||||
const variableDeclaratorAstNode =
|
||||
resultDeclaration?.type === 'VariableDeclaration' &&
|
||||
resultDeclaration?.declaration.init
|
||||
const resultRawValue = execState.memory?.get(DUMMY_VARIABLE_NAME)?.value
|
||||
const resultRawValue = execState.variables[DUMMY_VARIABLE_NAME]?.value
|
||||
|
||||
return {
|
||||
astNode: variableDeclaratorAstNode,
|
||||
@ -74,17 +47,14 @@ export async function getCalculatedKclExpressionValue({
|
||||
}
|
||||
}
|
||||
|
||||
export async function stringToKclExpression({
|
||||
export async function stringToKclExpression(
|
||||
value: string,
|
||||
variables: VariableMap
|
||||
) {
|
||||
const calculatedResult = await getCalculatedKclExpressionValue(
|
||||
value,
|
||||
programMemory,
|
||||
}: {
|
||||
value: string
|
||||
programMemory: ProgramMemory
|
||||
}) {
|
||||
const calculatedResult = await getCalculatedKclExpressionValue({
|
||||
value,
|
||||
programMemory,
|
||||
})
|
||||
variables
|
||||
)
|
||||
if (err(calculatedResult) || 'errors' in calculatedResult) {
|
||||
return calculatedResult
|
||||
} else if (!calculatedResult.astNode) {
|
||||
|
@ -80,13 +80,13 @@ const prepareToEditExtrude: PrepareToEditCallback =
|
||||
}
|
||||
|
||||
// Convert the length argument from a string to a KCL expression
|
||||
const distanceResult = await stringToKclExpression({
|
||||
value: codeManager.code.slice(
|
||||
const distanceResult = await stringToKclExpression(
|
||||
codeManager.code.slice(
|
||||
operation.labeledArgs?.['length']?.sourceRange[0],
|
||||
operation.labeledArgs?.['length']?.sourceRange[1]
|
||||
),
|
||||
programMemory: kclManager.programMemory.clone(),
|
||||
})
|
||||
{}
|
||||
)
|
||||
if (err(distanceResult) || 'errors' in distanceResult) {
|
||||
return baseCommand
|
||||
}
|
||||
@ -163,13 +163,13 @@ const prepareToEditOffsetPlane: PrepareToEditCallback = async ({
|
||||
}
|
||||
|
||||
// Convert the distance argument from a string to a KCL expression
|
||||
const distanceResult = await stringToKclExpression({
|
||||
value: codeManager.code.slice(
|
||||
const distanceResult = await stringToKclExpression(
|
||||
codeManager.code.slice(
|
||||
operation.labeledArgs.offset.sourceRange[0],
|
||||
operation.labeledArgs.offset.sourceRange[1]
|
||||
),
|
||||
programMemory: kclManager.programMemory.clone(),
|
||||
})
|
||||
{}
|
||||
)
|
||||
|
||||
if (err(distanceResult) || 'errors' in distanceResult) {
|
||||
return baseCommand
|
||||
|
@ -1,9 +1,9 @@
|
||||
import {
|
||||
Program,
|
||||
ProgramMemory,
|
||||
executor,
|
||||
executeMock,
|
||||
SourceRange,
|
||||
ExecState,
|
||||
VariableMap,
|
||||
} from '../lang/wasm'
|
||||
import { EngineCommandManager } from 'lang/std/engineConnection'
|
||||
import { EngineCommand } from 'lang/std/artifactGraph'
|
||||
@ -80,18 +80,9 @@ class MockEngineCommandManager {
|
||||
|
||||
export async function enginelessExecutor(
|
||||
ast: Node<Program>,
|
||||
pmo: ProgramMemory | Error = ProgramMemory.empty(),
|
||||
path?: string
|
||||
usePrevMemory?: boolean,
|
||||
path?: string,
|
||||
variables?: VariableMap
|
||||
): Promise<ExecState> {
|
||||
if (pmo !== null && err(pmo)) return Promise.reject(pmo)
|
||||
|
||||
const mockEngineCommandManager = new MockEngineCommandManager({
|
||||
setIsStreamReady: () => {},
|
||||
setMediaStream: () => {},
|
||||
}) as any as EngineCommandManager
|
||||
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
||||
mockEngineCommandManager.startNewSession()
|
||||
const execState = await executor(ast, mockEngineCommandManager, path, pmo)
|
||||
await mockEngineCommandManager.waitForAllCommands()
|
||||
return execState
|
||||
return await executeMock(ast, usePrevMemory, path, variables)
|
||||
}
|
||||
|
@ -5,10 +5,7 @@ import { findUniqueName } from 'lang/modifyAst'
|
||||
import { PrevVariable, findAllPreviousVariables } from 'lang/queryAst'
|
||||
import { Expr } from 'lang/wasm'
|
||||
import { useEffect, useRef, useState } from 'react'
|
||||
import {
|
||||
getCalculatedKclExpressionValue,
|
||||
programMemoryFromVariables,
|
||||
} from './kclHelpers'
|
||||
import { getCalculatedKclExpressionValue } from './kclHelpers'
|
||||
import { parse, resultIsOk } from 'lang/wasm'
|
||||
import { err } from 'lib/trap'
|
||||
|
||||
@ -36,7 +33,7 @@ export function useCalculateKclExpression({
|
||||
newVariableInsertIndex: number
|
||||
setNewVariableName: (a: string) => void
|
||||
} {
|
||||
const { programMemory, code } = useKclContext()
|
||||
const { variables, code } = useKclContext()
|
||||
const { context } = useModelingContext()
|
||||
// If there is no selection, use the end of the code
|
||||
// so all variables are available
|
||||
@ -80,7 +77,7 @@ export function useCalculateKclExpression({
|
||||
|
||||
useEffect(() => {
|
||||
if (
|
||||
programMemory.has(newVariableName) ||
|
||||
variables[newVariableName] ||
|
||||
newVariableName === '' ||
|
||||
!isValidVariableName(newVariableName)
|
||||
) {
|
||||
@ -88,33 +85,22 @@ export function useCalculateKclExpression({
|
||||
} else {
|
||||
setIsNewVariableNameUnique(true)
|
||||
}
|
||||
}, [programMemory, newVariableName])
|
||||
}, [variables, newVariableName])
|
||||
|
||||
useEffect(() => {
|
||||
if (!programMemory) return
|
||||
if (!variables) return
|
||||
const varInfo = findAllPreviousVariables(
|
||||
kclManager.ast,
|
||||
kclManager.programMemory,
|
||||
kclManager.variables,
|
||||
// If there is no selection, use the end of the code
|
||||
selectionRange || [code.length, code.length]
|
||||
)
|
||||
setAvailableVarInfo(varInfo)
|
||||
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
||||
}, [kclManager.ast, kclManager.variables, selectionRange])
|
||||
|
||||
useEffect(() => {
|
||||
const execAstAndSetResult = async () => {
|
||||
const programMemory = programMemoryFromVariables(
|
||||
availableVarInfo.variables
|
||||
)
|
||||
if (programMemory instanceof Error) {
|
||||
setCalcResult('NAN')
|
||||
setValueNode(null)
|
||||
return
|
||||
}
|
||||
const result = await getCalculatedKclExpressionValue({
|
||||
value,
|
||||
programMemory,
|
||||
})
|
||||
const result = await getCalculatedKclExpressionValue(value, {})
|
||||
if (result instanceof Error || 'errors' in result) {
|
||||
setCalcResult('NAN')
|
||||
setValueNode(null)
|
||||
@ -128,7 +114,7 @@ export function useCalculateKclExpression({
|
||||
setCalcResult('NAN')
|
||||
setValueNode(null)
|
||||
})
|
||||
}, [value, availableVarInfo, code, kclManager.programMemory])
|
||||
}, [value, availableVarInfo, code, kclManager.variables])
|
||||
|
||||
return {
|
||||
valueNode,
|
||||
|
@ -5,7 +5,7 @@ import { findAllPreviousVariables } from 'lang/queryAst'
|
||||
import { useEffect, useState } from 'react'
|
||||
|
||||
export function usePreviousVariables() {
|
||||
const { programMemory, code } = useKclContext()
|
||||
const { variables, code } = useKclContext()
|
||||
const { context } = useModelingContext()
|
||||
const selectionRange = context.selectionRanges.graphSelections[0]?.codeRef
|
||||
?.range || [code.length, code.length]
|
||||
@ -18,14 +18,14 @@ export function usePreviousVariables() {
|
||||
})
|
||||
|
||||
useEffect(() => {
|
||||
if (!programMemory || !selectionRange) return
|
||||
if (!variables || !selectionRange) return
|
||||
const varInfo = findAllPreviousVariables(
|
||||
kclManager.ast,
|
||||
kclManager.programMemory,
|
||||
kclManager.variables,
|
||||
selectionRange
|
||||
)
|
||||
setPreviousVariablesInfo(varInfo)
|
||||
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
||||
}, [kclManager.ast, kclManager.variables, selectionRange])
|
||||
|
||||
return previousVariablesInfo
|
||||
}
|
||||
|
@ -11,7 +11,8 @@ import {
|
||||
parse_wasm as ParseWasm,
|
||||
recast_wasm as RecastWasm,
|
||||
format_number as FormatNumber,
|
||||
execute as Execute,
|
||||
execute_with_engine as ExecuteWithEngine,
|
||||
execute_mock as ExecuteMock,
|
||||
kcl_lint as KclLint,
|
||||
modify_ast_for_sketch_wasm as ModifyAstForSketch,
|
||||
is_points_ccw as IsPointsCcw,
|
||||
@ -57,8 +58,11 @@ export const recast_wasm: typeof RecastWasm = (...args) => {
|
||||
export const format_number: typeof FormatNumber = (...args) => {
|
||||
return getModule().format_number(...args)
|
||||
}
|
||||
export const execute: typeof Execute = (...args) => {
|
||||
return getModule().execute(...args)
|
||||
export const execute_with_engine: typeof ExecuteWithEngine = (...args) => {
|
||||
return getModule().execute_with_engine(...args)
|
||||
}
|
||||
export const execute_mock: typeof ExecuteMock = (...args) => {
|
||||
return getModule().execute_mock(...args)
|
||||
}
|
||||
export const kcl_lint: typeof KclLint = (...args) => {
|
||||
return getModule().kcl_lint(...args)
|
||||
|
@ -1,6 +1,5 @@
|
||||
import {
|
||||
PathToNode,
|
||||
ProgramMemory,
|
||||
VariableDeclaration,
|
||||
VariableDeclarator,
|
||||
parse,
|
||||
@ -708,7 +707,7 @@ export const modelingMachine = setup({
|
||||
const modifiedAst = await deleteFromSelection(
|
||||
ast,
|
||||
selectionRanges.graphSelections[0],
|
||||
kclManager.programMemory,
|
||||
kclManager.variables,
|
||||
getFaceDetails
|
||||
)
|
||||
if (err(modifiedAst)) {
|
||||
@ -719,8 +718,7 @@ export const modelingMachine = setup({
|
||||
const testExecute = await executeAst({
|
||||
ast: modifiedAst,
|
||||
engineCommandManager,
|
||||
// We make sure to send an empty program memory to denote we mean mock mode.
|
||||
programMemoryOverride: ProgramMemory.empty(),
|
||||
isMock: true,
|
||||
})
|
||||
if (testExecute.errors.length) {
|
||||
toast.error(errorMessage)
|
||||
@ -1087,7 +1085,7 @@ export const modelingMachine = setup({
|
||||
selectionRanges,
|
||||
'horizontal',
|
||||
kclManager.ast,
|
||||
kclManager.programMemory
|
||||
kclManager.variables
|
||||
)
|
||||
if (trap(constraint)) return false
|
||||
const { modifiedAst, pathToNodeMap } = constraint
|
||||
@ -1122,7 +1120,7 @@ export const modelingMachine = setup({
|
||||
selectionRanges,
|
||||
'vertical',
|
||||
kclManager.ast,
|
||||
kclManager.programMemory
|
||||
kclManager.variables
|
||||
)
|
||||
if (trap(constraint)) return false
|
||||
const { modifiedAst, pathToNodeMap } = constraint
|
||||
|
@ -33,6 +33,9 @@ tokio = { version = "1.41.1", features = ["rt-multi-thread", "macros", "time"] }
|
||||
twenty-twenty = "0.8"
|
||||
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
|
||||
|
||||
[features]
|
||||
dhat-heap = ["kcl-lib/dhat-heap"]
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
console_error_panic_hook = "0.1.7"
|
||||
futures = "0.3.31"
|
||||
|
@ -11,28 +11,46 @@ use crate::{
|
||||
walk::Node as WalkNode,
|
||||
};
|
||||
|
||||
use super::ProgramMemory;
|
||||
|
||||
lazy_static::lazy_static! {
|
||||
/// A static mutable lock for updating the last successful execution state for the cache.
|
||||
static ref OLD_AST_MEMORY: Arc<RwLock<Option<OldAstState>>> = Default::default();
|
||||
static ref OLD_AST: Arc<RwLock<Option<OldAstState>>> = Default::default();
|
||||
// The last successful run's memory. Not cleared after an unssuccessful run.
|
||||
static ref PREV_MEMORY: Arc<RwLock<Option<ProgramMemory>>> = Default::default();
|
||||
}
|
||||
|
||||
/// Read the old ast memory from the lock.
|
||||
pub(super) async fn read_old_ast_memory() -> Option<OldAstState> {
|
||||
let old_ast = OLD_AST_MEMORY.read().await;
|
||||
pub(super) async fn read_old_ast() -> Option<OldAstState> {
|
||||
let old_ast = OLD_AST.read().await;
|
||||
old_ast.clone()
|
||||
}
|
||||
|
||||
pub(super) async fn write_old_ast_memory(old_state: OldAstState) {
|
||||
let mut old_ast = OLD_AST_MEMORY.write().await;
|
||||
pub(super) async fn write_old_ast(old_state: OldAstState) {
|
||||
let mut old_ast = OLD_AST.write().await;
|
||||
*old_ast = Some(old_state);
|
||||
}
|
||||
|
||||
pub(super) async fn read_old_memory() -> Option<ProgramMemory> {
|
||||
let old_mem = PREV_MEMORY.read().await;
|
||||
old_mem.clone()
|
||||
}
|
||||
|
||||
pub(super) async fn write_old_memory(mem: ProgramMemory) {
|
||||
let mut old_mem = PREV_MEMORY.write().await;
|
||||
*old_mem = Some(mem);
|
||||
}
|
||||
|
||||
pub async fn bust_cache() {
|
||||
let mut old_ast = OLD_AST_MEMORY.write().await;
|
||||
// Set the cache to None.
|
||||
let mut old_ast = OLD_AST.write().await;
|
||||
*old_ast = None;
|
||||
}
|
||||
|
||||
pub async fn clear_mem_cache() {
|
||||
let mut old_mem = PREV_MEMORY.write().await;
|
||||
*old_mem = None;
|
||||
}
|
||||
|
||||
/// Information for the caching an AST and smartly re-executing it if we can.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CacheInformation<'a> {
|
||||
|
@ -9,16 +9,17 @@ use crate::{
|
||||
execution::{
|
||||
annotations,
|
||||
cad_op::{OpArg, Operation},
|
||||
memory,
|
||||
state::ModuleState,
|
||||
BodyType, ExecState, ExecutorContext, KclValue, MemoryFunction, Metadata, ProgramMemory, TagEngineInfo,
|
||||
TagIdentifier,
|
||||
BodyType, EnvironmentRef, ExecState, ExecutorContext, KclValue, MemoryFunction, Metadata, ProgramMemory,
|
||||
TagEngineInfo, TagIdentifier,
|
||||
},
|
||||
modules::{ModuleId, ModulePath, ModuleRepr},
|
||||
parsing::ast::types::{
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression,
|
||||
CallExpressionKw, Expr, FunctionExpression, IfExpression, ImportPath, ImportSelector, ItemVisibility,
|
||||
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, NodeRef, NonCodeValue, ObjectExpression,
|
||||
PipeExpression, TagDeclarator, UnaryExpression, UnaryOperator,
|
||||
PipeExpression, Program, TagDeclarator, UnaryExpression, UnaryOperator,
|
||||
},
|
||||
source_range::SourceRange,
|
||||
std::{
|
||||
@ -113,20 +114,21 @@ impl ExecutorContext {
|
||||
|
||||
match &import_stmt.selector {
|
||||
ImportSelector::List { items } => {
|
||||
let (_, module_memory, module_exports) = self
|
||||
.exec_module(module_id, exec_state, ExecutionKind::Isolated, source_range)
|
||||
let (env_ref, module_exports) = self
|
||||
.exec_module_for_items(module_id, exec_state, ExecutionKind::Isolated, source_range)
|
||||
.await?;
|
||||
for import_item in items {
|
||||
// Extract the item from the module.
|
||||
let item =
|
||||
module_memory
|
||||
.get(&import_item.name.name, import_item.into())
|
||||
let item = exec_state
|
||||
.memory()
|
||||
.get_from(&import_item.name.name, env_ref, import_item.into())
|
||||
.map_err(|_err| {
|
||||
KclError::UndefinedValue(KclErrorDetails {
|
||||
message: format!("{} is not defined in module", import_item.name.name),
|
||||
source_ranges: vec![SourceRange::from(&import_item.name)],
|
||||
})
|
||||
})?;
|
||||
})?
|
||||
.clone();
|
||||
// Check that the item is allowed to be imported.
|
||||
if !module_exports.contains(&import_item.name.name) {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
@ -140,8 +142,8 @@ impl ExecutorContext {
|
||||
|
||||
// Add the item to the current module.
|
||||
exec_state.mut_memory().add(
|
||||
import_item.identifier(),
|
||||
item.clone(),
|
||||
import_item.identifier().to_owned(),
|
||||
item,
|
||||
SourceRange::from(&import_item.name),
|
||||
)?;
|
||||
|
||||
@ -154,17 +156,21 @@ impl ExecutorContext {
|
||||
}
|
||||
}
|
||||
ImportSelector::Glob(_) => {
|
||||
let (_, module_memory, module_exports) = self
|
||||
.exec_module(module_id, exec_state, ExecutionKind::Isolated, source_range)
|
||||
let (env_ref, module_exports) = self
|
||||
.exec_module_for_items(module_id, exec_state, ExecutionKind::Isolated, source_range)
|
||||
.await?;
|
||||
for name in module_exports.iter() {
|
||||
let item = module_memory.get(name, source_range).map_err(|_err| {
|
||||
let item = exec_state
|
||||
.memory()
|
||||
.get_from(name, env_ref, source_range)
|
||||
.map_err(|_err| {
|
||||
KclError::Internal(KclErrorDetails {
|
||||
message: format!("{} is not defined in module (but was exported?)", name),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
exec_state.mut_memory().add(name, item.clone(), source_range)?;
|
||||
})?
|
||||
.clone();
|
||||
exec_state.mut_memory().add(name.to_owned(), item, source_range)?;
|
||||
|
||||
if let ItemVisibility::Export = import_stmt.visibility {
|
||||
exec_state.mod_local.module_exports.push(name.clone());
|
||||
@ -177,7 +183,7 @@ impl ExecutorContext {
|
||||
value: module_id,
|
||||
meta: vec![source_range.into()],
|
||||
};
|
||||
exec_state.mut_memory().add(&name, item, source_range)?;
|
||||
exec_state.mut_memory().add(name, item, source_range)?;
|
||||
}
|
||||
}
|
||||
last_expr = None;
|
||||
@ -215,7 +221,9 @@ impl ExecutorContext {
|
||||
StatementKind::Declaration { name: &var_name },
|
||||
)
|
||||
.await?;
|
||||
exec_state.mut_memory().add(&var_name, memory_item, source_range)?;
|
||||
exec_state
|
||||
.mut_memory()
|
||||
.add(var_name.clone(), memory_item, source_range)?;
|
||||
|
||||
// Track exports.
|
||||
if let ItemVisibility::Export = variable_declaration.visibility {
|
||||
@ -225,6 +233,14 @@ impl ExecutorContext {
|
||||
}
|
||||
BodyItem::ReturnStatement(return_statement) => {
|
||||
let metadata = Metadata::from(return_statement);
|
||||
|
||||
if body_type == BodyType::Root {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Cannot return from outside a function.".to_owned(),
|
||||
source_ranges: vec![metadata.source_range],
|
||||
}));
|
||||
}
|
||||
|
||||
let value = self
|
||||
.execute_expr(
|
||||
&return_statement.argument,
|
||||
@ -233,7 +249,15 @@ impl ExecutorContext {
|
||||
StatementKind::Expression,
|
||||
)
|
||||
.await?;
|
||||
exec_state.mut_memory().return_ = Some(value);
|
||||
exec_state
|
||||
.mut_memory()
|
||||
.add(memory::RETURN_NAME.to_owned(), value, metadata.source_range)
|
||||
.map_err(|_| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: "Multiple returns from a single function.".to_owned(),
|
||||
source_ranges: vec![metadata.source_range],
|
||||
})
|
||||
})?;
|
||||
last_expr = None;
|
||||
}
|
||||
}
|
||||
@ -273,7 +297,8 @@ impl ExecutorContext {
|
||||
let source = resolved_path.source(&self.fs, source_range).await?;
|
||||
// TODO handle parsing errors properly
|
||||
let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err()?;
|
||||
exec_state.add_module(id, resolved_path, ModuleRepr::Kcl(parsed));
|
||||
exec_state.add_module(id, resolved_path, ModuleRepr::Kcl(parsed, None));
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
ImportPath::Foreign { .. } => {
|
||||
@ -296,43 +321,84 @@ impl ExecutorContext {
|
||||
let id = exec_state.next_module_id();
|
||||
let source = resolved_path.source(&self.fs, source_range).await?;
|
||||
let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err().unwrap();
|
||||
exec_state.add_module(id, resolved_path, ModuleRepr::Kcl(parsed));
|
||||
exec_state.add_module(id, resolved_path, ModuleRepr::Kcl(parsed, None));
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn exec_module(
|
||||
async fn exec_module_for_items(
|
||||
&self,
|
||||
module_id: ModuleId,
|
||||
exec_state: &mut ExecState,
|
||||
exec_kind: ExecutionKind,
|
||||
source_range: SourceRange,
|
||||
) -> Result<(Option<KclValue>, ProgramMemory, Vec<String>), KclError> {
|
||||
let old_units = exec_state.length_unit();
|
||||
// TODO It sucks that we have to clone the whole module AST here
|
||||
let info = exec_state.global.module_infos[&module_id].clone();
|
||||
) -> Result<(EnvironmentRef, Vec<String>), KclError> {
|
||||
let path = exec_state.global.module_infos[&module_id].path.clone();
|
||||
let mut repr = exec_state.global.module_infos[&module_id].take_repr();
|
||||
// DON'T EARLY RETURN! We need to restore the module repr
|
||||
|
||||
match &info.repr {
|
||||
ModuleRepr::Root => Err(KclError::ImportCycle(KclErrorDetails {
|
||||
message: format!(
|
||||
"circular import of modules is not allowed: {} -> {}",
|
||||
exec_state
|
||||
.global
|
||||
.mod_loader
|
||||
.import_stack
|
||||
.iter()
|
||||
.map(|p| p.as_path().to_string_lossy())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" -> "),
|
||||
info.path
|
||||
),
|
||||
source_ranges: vec![source_range],
|
||||
let result = match &mut repr {
|
||||
ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
|
||||
ModuleRepr::Kcl(_, Some((env_ref, items))) => Ok((*env_ref, items.clone())),
|
||||
ModuleRepr::Kcl(program, cache) => self
|
||||
.exec_module_from_ast(program, &path, exec_state, exec_kind, source_range)
|
||||
.await
|
||||
.map(|(_, er, items)| {
|
||||
*cache = Some((er, items.clone()));
|
||||
(er, items)
|
||||
}),
|
||||
ModuleRepr::Foreign(geom) => Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Cannot import items from foreign modules".to_owned(),
|
||||
source_ranges: vec![geom.source_range],
|
||||
})),
|
||||
ModuleRepr::Kcl(program) => {
|
||||
ModuleRepr::Dummy => unreachable!(),
|
||||
};
|
||||
|
||||
exec_state.global.module_infos[&module_id].restore_repr(repr);
|
||||
result
|
||||
}
|
||||
|
||||
async fn exec_module_for_result(
|
||||
&self,
|
||||
module_id: ModuleId,
|
||||
exec_state: &mut ExecState,
|
||||
exec_kind: ExecutionKind,
|
||||
source_range: SourceRange,
|
||||
) -> Result<Option<KclValue>, KclError> {
|
||||
let path = exec_state.global.module_infos[&module_id].path.clone();
|
||||
let repr = exec_state.global.module_infos[&module_id].take_repr();
|
||||
// DON'T EARLY RETURN! We need to restore the module repr
|
||||
|
||||
let result = match &repr {
|
||||
ModuleRepr::Root => Err(exec_state.circular_import_error(&path, source_range)),
|
||||
ModuleRepr::Kcl(program, _) => self
|
||||
.exec_module_from_ast(program, &path, exec_state, exec_kind, source_range)
|
||||
.await
|
||||
.map(|(val, _, _)| val),
|
||||
ModuleRepr::Foreign(geom) => super::import::send_to_engine(geom.clone(), self)
|
||||
.await
|
||||
.map(|geom| Some(KclValue::ImportedGeometry(geom))),
|
||||
ModuleRepr::Dummy => unreachable!(),
|
||||
};
|
||||
|
||||
exec_state.global.module_infos[&module_id].restore_repr(repr);
|
||||
result
|
||||
}
|
||||
|
||||
async fn exec_module_from_ast(
|
||||
&self,
|
||||
program: &Node<Program>,
|
||||
path: &ModulePath,
|
||||
exec_state: &mut ExecState,
|
||||
exec_kind: ExecutionKind,
|
||||
source_range: SourceRange,
|
||||
) -> Result<(Option<KclValue>, EnvironmentRef, Vec<String>), KclError> {
|
||||
let old_units = exec_state.length_unit();
|
||||
let mut local_state = ModuleState::new(&self.settings);
|
||||
exec_state.global.mod_loader.enter_module(&info.path);
|
||||
exec_state.global.mod_loader.enter_module(path);
|
||||
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
|
||||
exec_state.mut_memory().push_new_root_env();
|
||||
let original_execution = self.engine.replace_execution_kind(exec_kind);
|
||||
|
||||
let result = self
|
||||
@ -341,13 +407,15 @@ impl ExecutorContext {
|
||||
|
||||
let new_units = exec_state.length_unit();
|
||||
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
|
||||
exec_state.global.mod_loader.leave_module(&info.path);
|
||||
let env_ref = exec_state.mut_memory().pop_env();
|
||||
exec_state.global.mod_loader.leave_module(path);
|
||||
if !exec_kind.is_isolated() && new_units != old_units {
|
||||
self.engine.set_units(old_units.into(), Default::default()).await?;
|
||||
}
|
||||
self.engine.replace_execution_kind(original_execution);
|
||||
|
||||
let result = result.map_err(|err| {
|
||||
result
|
||||
.map_err(|err| {
|
||||
if let KclError::ImportCycle(_) = err {
|
||||
// It was an import cycle. Keep the original message.
|
||||
err.override_source_ranges(vec![source_range])
|
||||
@ -355,21 +423,14 @@ impl ExecutorContext {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Error loading imported file. Open it to view more details. {}: {}",
|
||||
info.path,
|
||||
path,
|
||||
err.message()
|
||||
),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok((result, local_state.memory, local_state.module_exports))
|
||||
}
|
||||
ModuleRepr::Foreign(geom) => {
|
||||
let geom = super::import::send_to_engine(geom.clone(), self).await?;
|
||||
Ok((Some(KclValue::ImportedGeometry(geom)), ProgramMemory::new(), Vec::new()))
|
||||
}
|
||||
}
|
||||
})
|
||||
.map(|result| (result, env_ref, local_state.module_exports))
|
||||
}
|
||||
|
||||
#[async_recursion]
|
||||
@ -387,10 +448,9 @@ impl ExecutorContext {
|
||||
Expr::Identifier(identifier) => {
|
||||
let value = exec_state.memory().get(&identifier.name, identifier.into())?.clone();
|
||||
if let KclValue::Module { value: module_id, meta } = value {
|
||||
let (result, _, _) = self
|
||||
.exec_module(module_id, exec_state, ExecutionKind::Normal, metadata.source_range)
|
||||
.await?;
|
||||
result.unwrap_or_else(|| {
|
||||
self.exec_module_for_result(module_id, exec_state, ExecutionKind::Normal, metadata.source_range)
|
||||
.await?
|
||||
.unwrap_or_else(|| {
|
||||
// The module didn't have a return value. Currently,
|
||||
// the only way to have a return value is with the final
|
||||
// statement being an expression statement.
|
||||
@ -409,17 +469,12 @@ impl ExecutorContext {
|
||||
}
|
||||
}
|
||||
Expr::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, self).await?,
|
||||
Expr::FunctionExpression(function_expression) => {
|
||||
// Cloning memory here is crucial for semantics so that we close
|
||||
// over variables. Variables defined lexically later shouldn't
|
||||
// be available to the function body.
|
||||
KclValue::Function {
|
||||
Expr::FunctionExpression(function_expression) => KclValue::Function {
|
||||
expression: function_expression.clone(),
|
||||
meta: vec![metadata.to_owned()],
|
||||
func: None,
|
||||
memory: Box::new(exec_state.memory().clone()),
|
||||
}
|
||||
}
|
||||
memory: exec_state.mut_memory().snapshot(),
|
||||
},
|
||||
Expr::CallExpression(call_expression) => call_expression.execute(exec_state, self).await?,
|
||||
Expr::CallExpressionKw(call_expression) => call_expression.execute(exec_state, self).await?,
|
||||
Expr::PipeExpression(pipe_expression) => pipe_expression.get_result(exec_state, self).await?,
|
||||
@ -456,7 +511,7 @@ impl ExecutorContext {
|
||||
.await?;
|
||||
exec_state
|
||||
.mut_memory()
|
||||
.add(&expr.label.name, result.clone(), init.into())?;
|
||||
.add(expr.label.name.clone(), result.clone(), init.into())?;
|
||||
// TODO this lets us use the label as a variable name, but not as a tag in most cases
|
||||
result
|
||||
}
|
||||
@ -886,7 +941,7 @@ impl Node<CallExpressionKw> {
|
||||
};
|
||||
|
||||
// Attempt to call the function.
|
||||
let result = {
|
||||
let mut return_value = {
|
||||
// Don't early-return in this block.
|
||||
let result = func.std_lib_fn()(exec_state, args).await;
|
||||
|
||||
@ -900,9 +955,8 @@ impl Node<CallExpressionKw> {
|
||||
exec_state.mod_local.operations.push(op);
|
||||
}
|
||||
result
|
||||
};
|
||||
}?;
|
||||
|
||||
let mut return_value = result?;
|
||||
update_memory_for_tags_of_geometry(&mut return_value, exec_state)?;
|
||||
|
||||
Ok(return_value)
|
||||
@ -912,7 +966,6 @@ impl Node<CallExpressionKw> {
|
||||
// Clone the function so that we can use a mutable reference to
|
||||
// exec_state.
|
||||
let func = exec_state.memory().get(fn_name, source_range)?.clone();
|
||||
let fn_dynamic_state = exec_state.mod_local.dynamic_state.merge(exec_state.memory());
|
||||
|
||||
// Track call operation.
|
||||
let op_labeled_args = args
|
||||
@ -932,20 +985,14 @@ impl Node<CallExpressionKw> {
|
||||
source_range: callsite,
|
||||
});
|
||||
|
||||
let return_value = {
|
||||
let previous_dynamic_state =
|
||||
std::mem::replace(&mut exec_state.mod_local.dynamic_state, fn_dynamic_state);
|
||||
let result = func
|
||||
let return_value = func
|
||||
.call_fn_kw(args, exec_state, ctx.clone(), callsite)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
// Add the call expression to the source ranges.
|
||||
// TODO currently ignored by the frontend
|
||||
e.add_source_ranges(vec![source_range])
|
||||
});
|
||||
exec_state.mod_local.dynamic_state = previous_dynamic_state;
|
||||
result?
|
||||
};
|
||||
})?;
|
||||
|
||||
let result = return_value.ok_or_else(move || {
|
||||
let mut source_ranges: Vec<SourceRange> = vec![source_range];
|
||||
@ -1018,9 +1065,11 @@ impl Node<CallExpression> {
|
||||
ctx.clone(),
|
||||
exec_state.mod_local.pipe_value.clone().map(Arg::synthetic),
|
||||
);
|
||||
let result = {
|
||||
let mut return_value = {
|
||||
// Don't early-return in this block.
|
||||
exec_state.mut_memory().push_new_env_for_rust_call();
|
||||
let result = func.std_lib_fn()(exec_state, args).await;
|
||||
exec_state.mut_memory().pop_env();
|
||||
|
||||
if let Some(mut op) = op {
|
||||
op.set_std_lib_call_is_error(result.is_err());
|
||||
@ -1032,9 +1081,8 @@ impl Node<CallExpression> {
|
||||
exec_state.mod_local.operations.push(op);
|
||||
}
|
||||
result
|
||||
};
|
||||
}?;
|
||||
|
||||
let mut return_value = result?;
|
||||
update_memory_for_tags_of_geometry(&mut return_value, exec_state)?;
|
||||
|
||||
Ok(return_value)
|
||||
@ -1044,7 +1092,6 @@ impl Node<CallExpression> {
|
||||
// Clone the function so that we can use a mutable reference to
|
||||
// exec_state.
|
||||
let func = exec_state.memory().get(fn_name, source_range)?.clone();
|
||||
let fn_dynamic_state = exec_state.mod_local.dynamic_state.merge(exec_state.memory());
|
||||
|
||||
// Track call operation.
|
||||
exec_state
|
||||
@ -1059,17 +1106,11 @@ impl Node<CallExpression> {
|
||||
source_range: callsite,
|
||||
});
|
||||
|
||||
let return_value = {
|
||||
let previous_dynamic_state =
|
||||
std::mem::replace(&mut exec_state.mod_local.dynamic_state, fn_dynamic_state);
|
||||
let result = func.call_fn(fn_args, exec_state, ctx.clone()).await.map_err(|e| {
|
||||
let return_value = func.call_fn(fn_args, exec_state, ctx.clone()).await.map_err(|e| {
|
||||
// Add the call expression to the source ranges.
|
||||
// TODO currently ignored by the frontend
|
||||
e.add_source_ranges(vec![source_range])
|
||||
});
|
||||
exec_state.mod_local.dynamic_state = previous_dynamic_state;
|
||||
result?
|
||||
};
|
||||
})?;
|
||||
|
||||
let result = return_value.ok_or_else(move || {
|
||||
let mut source_ranges: Vec<SourceRange> = vec![source_range];
|
||||
@ -1103,15 +1144,29 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
|
||||
match result {
|
||||
KclValue::Sketch { value: ref mut sketch } => {
|
||||
for (_, tag) in sketch.tags.iter() {
|
||||
exec_state.mut_memory().update_tag(&tag.value, tag.clone())?;
|
||||
exec_state
|
||||
.mut_memory()
|
||||
.insert_or_update(tag.value.clone(), KclValue::TagIdentifier(Box::new(tag.clone())));
|
||||
}
|
||||
}
|
||||
KclValue::Solid { ref mut value } => {
|
||||
for v in &value.value {
|
||||
if let Some(tag) = v.get_tag() {
|
||||
// Get the past tag and update it.
|
||||
let mut t = if let Some(t) = value.sketch.tags.get(&tag.name) {
|
||||
t.clone()
|
||||
let tag_id = if let Some(t) = value.sketch.tags.get(&tag.name) {
|
||||
let mut t = t.clone();
|
||||
let Some(ref info) = t.info else {
|
||||
return Err(KclError::Internal(KclErrorDetails {
|
||||
message: format!("Tag {} does not have path info", tag.name),
|
||||
source_ranges: vec![tag.into()],
|
||||
}));
|
||||
};
|
||||
|
||||
let mut info = info.clone();
|
||||
info.surface = Some(v.clone());
|
||||
info.sketch = value.id;
|
||||
t.info = Some(info);
|
||||
t
|
||||
} else {
|
||||
// It's probably a fillet or a chamfer.
|
||||
// Initialize it.
|
||||
@ -1129,29 +1184,40 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
|
||||
}
|
||||
};
|
||||
|
||||
let Some(ref info) = t.info else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Tag {} does not have path info", tag.name),
|
||||
source_ranges: vec![tag.into()],
|
||||
}));
|
||||
};
|
||||
|
||||
let mut info = info.clone();
|
||||
info.surface = Some(v.clone());
|
||||
info.sketch = value.id;
|
||||
t.info = Some(info);
|
||||
|
||||
exec_state.mut_memory().update_tag(&tag.name, t.clone())?;
|
||||
exec_state
|
||||
.mut_memory()
|
||||
.insert_or_update(tag.name.clone(), KclValue::TagIdentifier(Box::new(tag_id.clone())));
|
||||
|
||||
// update the sketch tags.
|
||||
value.sketch.tags.insert(tag.name.clone(), t);
|
||||
value.sketch.tags.insert(tag.name.clone(), tag_id);
|
||||
}
|
||||
}
|
||||
|
||||
// Find the stale sketch in memory and update it.
|
||||
let cur_env_index = exec_state.memory().current_env.index();
|
||||
if let Some(current_env) = exec_state.mut_memory().environments.get_mut(cur_env_index) {
|
||||
current_env.update_sketch_tags(&value.sketch);
|
||||
if !value.sketch.tags.is_empty() {
|
||||
let updates: Vec<_> = exec_state
|
||||
.memory()
|
||||
.find_all_in_current_env(|v| match v {
|
||||
KclValue::Sketch { value: sk } => sk.artifact_id == value.sketch.artifact_id,
|
||||
_ => false,
|
||||
})
|
||||
.map(|(k, v)| {
|
||||
let mut sketch = v.as_sketch().unwrap().clone();
|
||||
for (tag_name, tag_id) in value.sketch.tags.iter() {
|
||||
sketch.tags.insert(tag_name.clone(), tag_id.clone());
|
||||
}
|
||||
(
|
||||
k.clone(),
|
||||
KclValue::Sketch {
|
||||
value: Box::new(sketch),
|
||||
},
|
||||
)
|
||||
})
|
||||
.collect();
|
||||
|
||||
updates
|
||||
.into_iter()
|
||||
.for_each(|(k, v)| exec_state.mut_memory().insert_or_update(k, v))
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
@ -1171,7 +1237,7 @@ impl Node<TagDeclarator> {
|
||||
|
||||
exec_state
|
||||
.mut_memory()
|
||||
.add(&self.name, memory_item.clone(), self.into())?;
|
||||
.add(self.name.clone(), memory_item.clone(), self.into())?;
|
||||
|
||||
Ok(self.into())
|
||||
}
|
||||
@ -1450,8 +1516,8 @@ impl Node<PipeExpression> {
|
||||
fn assign_args_to_params(
|
||||
function_expression: NodeRef<'_, FunctionExpression>,
|
||||
args: Vec<Arg>,
|
||||
mut fn_memory: ProgramMemory,
|
||||
) -> Result<ProgramMemory, KclError> {
|
||||
fn_memory: &mut ProgramMemory,
|
||||
) -> Result<(), KclError> {
|
||||
let num_args = function_expression.number_of_args();
|
||||
let (min_params, max_params) = num_args.into_inner();
|
||||
let n = args.len();
|
||||
@ -1475,14 +1541,18 @@ fn assign_args_to_params(
|
||||
for (index, param) in function_expression.params.iter().enumerate() {
|
||||
if let Some(arg) = args.get(index) {
|
||||
// Argument was provided.
|
||||
fn_memory.add(¶m.identifier.name, arg.value.clone(), (¶m.identifier).into())?;
|
||||
fn_memory.add(
|
||||
param.identifier.name.clone(),
|
||||
arg.value.clone(),
|
||||
(¶m.identifier).into(),
|
||||
)?;
|
||||
} else {
|
||||
// Argument was not provided.
|
||||
if let Some(ref default_val) = param.default_value {
|
||||
// If the corresponding parameter is optional,
|
||||
// then it's fine, the user doesn't need to supply it.
|
||||
fn_memory.add(
|
||||
¶m.identifier.name,
|
||||
param.identifier.name.clone(),
|
||||
default_val.clone().into(),
|
||||
(¶m.identifier).into(),
|
||||
)?;
|
||||
@ -1493,14 +1563,14 @@ fn assign_args_to_params(
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(fn_memory)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn assign_args_to_params_kw(
|
||||
function_expression: NodeRef<'_, FunctionExpression>,
|
||||
mut args: crate::std::args::KwArgs,
|
||||
mut fn_memory: ProgramMemory,
|
||||
) -> Result<ProgramMemory, KclError> {
|
||||
fn_memory: &mut ProgramMemory,
|
||||
) -> Result<(), KclError> {
|
||||
// Add the arguments to the memory. A new call frame should have already
|
||||
// been created.
|
||||
let source_ranges = vec![function_expression.into()];
|
||||
@ -1522,7 +1592,7 @@ fn assign_args_to_params_kw(
|
||||
}
|
||||
},
|
||||
};
|
||||
fn_memory.add(¶m.identifier.name, arg_val, (¶m.identifier).into())?;
|
||||
fn_memory.add(param.identifier.name.clone(), arg_val, (¶m.identifier).into())?;
|
||||
} else {
|
||||
let Some(unlabeled) = args.unlabeled.take() else {
|
||||
let param_name = ¶m.identifier.name;
|
||||
@ -1540,18 +1610,18 @@ fn assign_args_to_params_kw(
|
||||
});
|
||||
};
|
||||
fn_memory.add(
|
||||
¶m.identifier.name,
|
||||
param.identifier.name.clone(),
|
||||
unlabeled.value.clone(),
|
||||
(¶m.identifier).into(),
|
||||
)?;
|
||||
}
|
||||
}
|
||||
Ok(fn_memory)
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) async fn call_user_defined_function(
|
||||
args: Vec<Arg>,
|
||||
memory: &ProgramMemory,
|
||||
memory: EnvironmentRef,
|
||||
function_expression: NodeRef<'_, FunctionExpression>,
|
||||
exec_state: &mut ExecState,
|
||||
ctx: &ExecutorContext,
|
||||
@ -1559,29 +1629,32 @@ pub(crate) async fn call_user_defined_function(
|
||||
// Create a new environment to execute the function body in so that local
|
||||
// variables shadow variables in the parent scope. The new environment's
|
||||
// parent should be the environment of the closure.
|
||||
let mut body_memory = memory.clone();
|
||||
let body_env = body_memory.new_env_for_call(memory.current_env);
|
||||
body_memory.current_env = body_env;
|
||||
let fn_memory = assign_args_to_params(function_expression, args, body_memory)?;
|
||||
exec_state.mut_memory().push_new_env_for_call(memory);
|
||||
if let Err(e) = assign_args_to_params(function_expression, args, exec_state.mut_memory()) {
|
||||
exec_state.mut_memory().pop_env();
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
// Execute the function body using the memory we just created.
|
||||
let (result, fn_memory) = {
|
||||
let previous_memory = std::mem::replace(&mut exec_state.mod_local.memory, fn_memory);
|
||||
let result = ctx
|
||||
.exec_program(&function_expression.body, exec_state, BodyType::Block)
|
||||
.await;
|
||||
let result = result.map(|_| {
|
||||
exec_state
|
||||
.memory()
|
||||
.get(memory::RETURN_NAME, function_expression.as_source_range())
|
||||
.ok()
|
||||
.cloned()
|
||||
});
|
||||
// Restore the previous memory.
|
||||
let fn_memory = std::mem::replace(&mut exec_state.mod_local.memory, previous_memory);
|
||||
exec_state.mut_memory().pop_env();
|
||||
|
||||
(result, fn_memory)
|
||||
};
|
||||
|
||||
result.map(|_| fn_memory.return_)
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) async fn call_user_defined_function_kw(
|
||||
args: crate::std::args::KwArgs,
|
||||
memory: &ProgramMemory,
|
||||
memory: EnvironmentRef,
|
||||
function_expression: NodeRef<'_, FunctionExpression>,
|
||||
exec_state: &mut ExecState,
|
||||
ctx: &ExecutorContext,
|
||||
@ -1589,31 +1662,34 @@ pub(crate) async fn call_user_defined_function_kw(
|
||||
// Create a new environment to execute the function body in so that local
|
||||
// variables shadow variables in the parent scope. The new environment's
|
||||
// parent should be the environment of the closure.
|
||||
let mut body_memory = memory.clone();
|
||||
let body_env = body_memory.new_env_for_call(memory.current_env);
|
||||
body_memory.current_env = body_env;
|
||||
let fn_memory = assign_args_to_params_kw(function_expression, args, body_memory)?;
|
||||
exec_state.mut_memory().push_new_env_for_call(memory);
|
||||
if let Err(e) = assign_args_to_params_kw(function_expression, args, exec_state.mut_memory()) {
|
||||
exec_state.mut_memory().pop_env();
|
||||
return Err(e);
|
||||
}
|
||||
|
||||
// Execute the function body using the memory we just created.
|
||||
let (result, fn_memory) = {
|
||||
let previous_memory = std::mem::replace(&mut exec_state.mod_local.memory, fn_memory);
|
||||
let result = ctx
|
||||
.exec_program(&function_expression.body, exec_state, BodyType::Block)
|
||||
.await;
|
||||
let result = result.map(|_| {
|
||||
exec_state
|
||||
.memory()
|
||||
.get(memory::RETURN_NAME, function_expression.as_source_range())
|
||||
.ok()
|
||||
.cloned()
|
||||
});
|
||||
// Restore the previous memory.
|
||||
let fn_memory = std::mem::replace(&mut exec_state.mod_local.memory, previous_memory);
|
||||
exec_state.mut_memory().pop_env();
|
||||
|
||||
(result, fn_memory)
|
||||
};
|
||||
|
||||
result.map(|_| fn_memory.return_)
|
||||
result
|
||||
}
|
||||
|
||||
/// A function being used as a parameter into a stdlib function. This is a
|
||||
/// closure, plus everything needed to execute it.
|
||||
pub struct FunctionParam<'a> {
|
||||
pub inner: Option<&'a MemoryFunction>,
|
||||
pub memory: ProgramMemory,
|
||||
pub memory: EnvironmentRef,
|
||||
pub fn_expr: crate::parsing::ast::types::BoxNode<FunctionExpression>,
|
||||
pub meta: Vec<Metadata>,
|
||||
pub ctx: ExecutorContext,
|
||||
@ -1624,7 +1700,7 @@ impl FunctionParam<'_> {
|
||||
if let Some(inner) = self.inner {
|
||||
inner(
|
||||
args,
|
||||
self.memory.clone(),
|
||||
self.memory,
|
||||
self.fn_expr.clone(),
|
||||
self.meta.clone(),
|
||||
exec_state,
|
||||
@ -1632,7 +1708,7 @@ impl FunctionParam<'_> {
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
call_user_defined_function(args, &self.memory, self.fn_expr.as_ref(), exec_state, &self.ctx).await
|
||||
call_user_defined_function(args, self.memory, self.fn_expr.as_ref(), exec_state, &self.ctx).await
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1650,8 +1726,12 @@ impl JsonSchema for FunctionParam<'_> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::{
|
||||
execution::parse_execute,
|
||||
parsing::ast::types::{DefaultParamVal, Identifier, Parameter},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
use crate::parsing::ast::types::{DefaultParamVal, Identifier, Parameter};
|
||||
|
||||
#[test]
|
||||
fn test_assign_args_to_params() {
|
||||
@ -1690,7 +1770,7 @@ mod test {
|
||||
let mut program_memory = ProgramMemory::new();
|
||||
for (name, item) in items {
|
||||
program_memory
|
||||
.add(name.as_str(), item.clone(), SourceRange::default())
|
||||
.add(name.clone(), item.clone(), SourceRange::default())
|
||||
.unwrap();
|
||||
}
|
||||
program_memory
|
||||
@ -1775,11 +1855,26 @@ mod test {
|
||||
digest: None,
|
||||
});
|
||||
let args = args.into_iter().map(Arg::synthetic).collect();
|
||||
let actual = assign_args_to_params(func_expr, args, ProgramMemory::new());
|
||||
let mut actual = ProgramMemory::new();
|
||||
let actual = assign_args_to_params(func_expr, args, &mut actual).map(|_| actual);
|
||||
assert_eq!(
|
||||
actual, expected,
|
||||
"failed test '{test_name}':\ngot {actual:?}\nbut expected\n{expected:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn multiple_returns() {
|
||||
let program = r#"fn foo() {
|
||||
return 0
|
||||
return 42
|
||||
}
|
||||
|
||||
a = foo()
|
||||
"#;
|
||||
|
||||
let result = parse_execute(program).await;
|
||||
assert!(result.unwrap_err().to_string().contains("return"));
|
||||
}
|
||||
}
|
||||
|
@ -564,29 +564,8 @@ pub struct Solid {
|
||||
}
|
||||
|
||||
impl Solid {
|
||||
pub(crate) fn get_all_edge_cut_ids(&self) -> Vec<uuid::Uuid> {
|
||||
self.edge_cuts.iter().map(|foc| foc.id()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// An solid ID and its fillet and chamfer IDs. This is needed for lazy
|
||||
/// fillet evaluation.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct SolidLazyIds {
|
||||
pub solid_id: uuid::Uuid,
|
||||
pub sketch_id: uuid::Uuid,
|
||||
/// Chamfers or fillets on this solid.
|
||||
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
||||
pub edge_cuts: Vec<uuid::Uuid>,
|
||||
}
|
||||
|
||||
impl From<&Solid> for SolidLazyIds {
|
||||
fn from(eg: &Solid) -> Self {
|
||||
Self {
|
||||
solid_id: eg.id,
|
||||
sketch_id: eg.sketch.id,
|
||||
edge_cuts: eg.edge_cuts.iter().map(|foc| foc.id()).collect(),
|
||||
}
|
||||
pub(crate) fn get_all_edge_cut_ids(&self) -> impl Iterator<Item = uuid::Uuid> + '_ {
|
||||
self.edge_cuts.iter().map(|foc| foc.id())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ pub async fn import_foreign(
|
||||
pub struct PreImportedGeometry {
|
||||
id: Uuid,
|
||||
command: mcmd::ImportFiles,
|
||||
source_range: SourceRange,
|
||||
pub source_range: SourceRange,
|
||||
}
|
||||
|
||||
pub async fn send_to_engine(pre: PreImportedGeometry, ctxt: &ExecutorContext) -> Result<ImportedGeometry, KclError> {
|
||||
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::KclErrorDetails,
|
||||
exec::{ProgramMemory, Sketch},
|
||||
exec::Sketch,
|
||||
execution::{
|
||||
Face, Helix, ImportedGeometry, MemoryFunction, Metadata, Plane, SketchSet, Solid, SolidSet, TagIdentifier,
|
||||
},
|
||||
@ -18,6 +18,8 @@ use crate::{
|
||||
ExecState, ExecutorContext, KclError, ModuleId, SourceRange,
|
||||
};
|
||||
|
||||
use super::memory::EnvironmentRef;
|
||||
|
||||
pub type KclObjectFields = HashMap<String, KclValue>;
|
||||
|
||||
/// Any KCL value.
|
||||
@ -94,7 +96,7 @@ pub enum KclValue {
|
||||
func: Option<MemoryFunction>,
|
||||
#[schemars(skip)]
|
||||
expression: crate::parsing::ast::types::BoxNode<FunctionExpression>,
|
||||
memory: Box<ProgramMemory>,
|
||||
memory: EnvironmentRef,
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
@ -108,6 +110,12 @@ pub enum KclValue {
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
// Only used for memory management. Should never be visible outside of the memory module.
|
||||
Tombstone {
|
||||
value: (),
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
}
|
||||
|
||||
impl From<SketchSet> for KclValue {
|
||||
@ -166,6 +174,7 @@ impl From<KclValue> for Vec<SourceRange> {
|
||||
KclValue::Module { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Tombstone { .. } => unreachable!("Tombstone SourceRange"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -197,6 +206,7 @@ impl From<&KclValue> for Vec<SourceRange> {
|
||||
KclValue::Object { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::Module { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::KclNone { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::Tombstone { .. } => unreachable!("Tombstone &SourceRange"),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -224,6 +234,7 @@ impl KclValue {
|
||||
KclValue::Function { meta, .. } => meta.clone(),
|
||||
KclValue::Module { meta, .. } => meta.clone(),
|
||||
KclValue::KclNone { meta, .. } => meta.clone(),
|
||||
KclValue::Tombstone { .. } => unreachable!("Tombstone Metadata"),
|
||||
}
|
||||
}
|
||||
|
||||
@ -291,6 +302,7 @@ impl KclValue {
|
||||
KclValue::Object { .. } => "object",
|
||||
KclValue::Module { .. } => "module",
|
||||
KclValue::KclNone { .. } => "None",
|
||||
KclValue::Tombstone { .. } => "TOMBSTONE",
|
||||
}
|
||||
}
|
||||
|
||||
@ -302,6 +314,16 @@ impl KclValue {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn map_env_ref(&self, env_map: &HashMap<EnvironmentRef, EnvironmentRef>) -> Self {
|
||||
let mut result = self.clone();
|
||||
if let KclValue::Function { ref mut memory, .. } = result {
|
||||
if let Some(new) = env_map.get(memory) {
|
||||
*memory = *new;
|
||||
}
|
||||
}
|
||||
result
|
||||
}
|
||||
|
||||
/// Put the number into a KCL value.
|
||||
pub const fn from_number(f: f64, meta: Vec<Metadata>) -> Self {
|
||||
Self::Number { value: f, meta }
|
||||
@ -406,6 +428,14 @@ impl KclValue {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_sketch(&self) -> Option<&Sketch> {
|
||||
if let KclValue::Sketch { value } = self {
|
||||
Some(value)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_f64(&self) -> Option<f64> {
|
||||
if let KclValue::Number { value, meta: _ } = &self {
|
||||
Some(*value)
|
||||
@ -454,7 +484,7 @@ impl KclValue {
|
||||
Some(FnAsArg {
|
||||
func: func.as_ref(),
|
||||
expr: expression.to_owned(),
|
||||
memory: memory.to_owned(),
|
||||
memory: *memory,
|
||||
})
|
||||
}
|
||||
|
||||
@ -518,24 +548,19 @@ impl KclValue {
|
||||
} = &self
|
||||
else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "not a in memory function".to_string(),
|
||||
message: "not an in-memory function".to_string(),
|
||||
source_ranges: vec![],
|
||||
}));
|
||||
};
|
||||
if let Some(func) = func {
|
||||
func(
|
||||
args,
|
||||
closure_memory.as_ref().clone(),
|
||||
expression.clone(),
|
||||
meta.clone(),
|
||||
exec_state,
|
||||
ctx,
|
||||
)
|
||||
.await
|
||||
exec_state.mut_memory().push_new_env_for_call(*closure_memory);
|
||||
let result = func(args, *closure_memory, expression.clone(), meta.clone(), exec_state, ctx).await;
|
||||
exec_state.mut_memory().pop_env();
|
||||
result
|
||||
} else {
|
||||
crate::execution::exec_ast::call_user_defined_function(
|
||||
args,
|
||||
closure_memory.as_ref(),
|
||||
*closure_memory,
|
||||
expression.as_ref(),
|
||||
exec_state,
|
||||
&ctx,
|
||||
@ -570,7 +595,7 @@ impl KclValue {
|
||||
} else {
|
||||
crate::execution::exec_ast::call_user_defined_function_kw(
|
||||
args.kw_args,
|
||||
closure_memory.as_ref(),
|
||||
*closure_memory,
|
||||
expression.as_ref(),
|
||||
exec_state,
|
||||
&ctx,
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -3,17 +3,8 @@
|
||||
use std::{path::PathBuf, sync::Arc};
|
||||
|
||||
use anyhow::Result;
|
||||
pub use artifact::{Artifact, ArtifactCommand, ArtifactGraph, ArtifactId};
|
||||
pub use cache::bust_cache;
|
||||
use cache::OldAstState;
|
||||
pub use cad_op::Operation;
|
||||
pub use exec_ast::FunctionParam;
|
||||
pub use geometry::*;
|
||||
pub(crate) use import::{
|
||||
import_foreign, send_to_engine as send_import_to_engine, PreImportedGeometry, ZOO_COORD_SYSTEM,
|
||||
};
|
||||
use indexmap::IndexMap;
|
||||
pub use kcl_value::{KclObjectFields, KclValue, UnitAngle, UnitLen};
|
||||
use kcmc::{
|
||||
each_cmd as mcmd,
|
||||
ok_response::{output::TakeSnapshot, OkModelingCmdResponse},
|
||||
@ -21,10 +12,8 @@ use kcmc::{
|
||||
ImageFormat, ModelingCmd,
|
||||
};
|
||||
use kittycad_modeling_cmds as kcmc;
|
||||
pub use memory::ProgramMemory;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use state::{ExecState, IdGenerator, MetaSettings};
|
||||
|
||||
use crate::{
|
||||
engine::EngineManager,
|
||||
@ -41,6 +30,18 @@ use crate::{
|
||||
ExecError, KclErrorWithOutputs,
|
||||
};
|
||||
|
||||
pub use artifact::{Artifact, ArtifactCommand, ArtifactGraph, ArtifactId};
|
||||
pub use cache::{bust_cache, clear_mem_cache};
|
||||
pub use cad_op::Operation;
|
||||
pub use exec_ast::FunctionParam;
|
||||
pub use geometry::*;
|
||||
pub(crate) use import::{
|
||||
import_foreign, send_to_engine as send_import_to_engine, PreImportedGeometry, ZOO_COORD_SYSTEM,
|
||||
};
|
||||
pub use kcl_value::{KclObjectFields, KclValue, UnitAngle, UnitLen};
|
||||
pub use memory::{EnvironmentRef, ProgramMemory};
|
||||
pub use state::{ExecState, IdGenerator, MetaSettings};
|
||||
|
||||
pub(crate) mod annotations;
|
||||
mod artifact;
|
||||
pub(crate) mod cache;
|
||||
@ -53,12 +54,12 @@ mod memory;
|
||||
mod state;
|
||||
|
||||
/// Outcome of executing a program. This is used in TS.
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, ts_rs::TS)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExecOutcome {
|
||||
/// Program variable bindings of the top-level module.
|
||||
pub memory: ProgramMemory,
|
||||
/// Variables in the top-level of the root module. Note that functions will have an invalid env ref.
|
||||
pub variables: IndexMap<String, KclValue>,
|
||||
/// Operations that have been performed in execution order, for display in
|
||||
/// the Feature Tree.
|
||||
pub operations: Vec<Operation>,
|
||||
@ -133,7 +134,7 @@ impl std::hash::Hash for TagIdentifier {
|
||||
pub type MemoryFunction =
|
||||
fn(
|
||||
s: Vec<Arg>,
|
||||
memory: ProgramMemory,
|
||||
memory: EnvironmentRef,
|
||||
expression: crate::parsing::ast::types::BoxNode<FunctionExpression>,
|
||||
metadata: Vec<Metadata>,
|
||||
exec_state: &ExecState,
|
||||
@ -160,7 +161,6 @@ pub struct TagEngineInfo {
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub enum BodyType {
|
||||
Root,
|
||||
Sketch,
|
||||
Block,
|
||||
}
|
||||
|
||||
@ -496,17 +496,56 @@ impl ExecutorContext {
|
||||
pub async fn run_mock(
|
||||
&self,
|
||||
program: crate::Program,
|
||||
program_memory_override: Option<ProgramMemory>,
|
||||
use_prev_memory: bool,
|
||||
variables: IndexMap<String, KclValue>,
|
||||
) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
||||
assert!(self.is_mock());
|
||||
|
||||
let mut exec_state = ExecState::new(&self.settings);
|
||||
if let Some(program_memory_override) = program_memory_override {
|
||||
exec_state.mod_local.memory = program_memory_override;
|
||||
let mut mem = if use_prev_memory {
|
||||
cache::read_old_memory()
|
||||
.await
|
||||
.unwrap_or_else(|| exec_state.memory().clone())
|
||||
} else {
|
||||
exec_state.memory().clone()
|
||||
};
|
||||
|
||||
// Add any extra variables to memory
|
||||
let mut to_restore = Vec::new();
|
||||
for (k, v) in variables {
|
||||
crate::log::log(format!("add var: {k}"));
|
||||
to_restore.push((k.clone(), mem.get(&k, SourceRange::default()).ok().cloned()));
|
||||
mem.add(k, v, SourceRange::synthetic())
|
||||
.map_err(KclErrorWithOutputs::no_outputs)?;
|
||||
}
|
||||
|
||||
// Push a scope so that old variables can be overwritten (since we might be re-executing some
|
||||
// part of the scene).
|
||||
mem.push_new_env_for_scope();
|
||||
|
||||
*exec_state.mut_memory() = mem;
|
||||
|
||||
self.inner_run(&program.ast, &mut exec_state).await?;
|
||||
Ok(exec_state.to_wasm_outcome())
|
||||
|
||||
// Restore any temporary variables, then save any newly created variables back to
|
||||
// memory in case another run wants to use them. Note this is just saved to the preserved
|
||||
// memory, not to the exec_state which is not cached for mock execution.
|
||||
let mut mem = exec_state.memory().clone();
|
||||
|
||||
let top = mem.pop_and_preserve_env();
|
||||
for (k, v) in to_restore {
|
||||
match v {
|
||||
Some(v) => mem.insert_or_update(k, v),
|
||||
None => mem.clear(k),
|
||||
}
|
||||
}
|
||||
mem.squash_env(top);
|
||||
|
||||
cache::write_old_memory(mem).await;
|
||||
|
||||
let outcome = exec_state.to_mock_wasm_outcome();
|
||||
crate::log::log(format!("return mock {:#?}", outcome.variables));
|
||||
Ok(outcome)
|
||||
}
|
||||
|
||||
pub async fn run_with_caching(&self, program: crate::Program) -> Result<ExecOutcome, KclErrorWithOutputs> {
|
||||
@ -516,7 +555,7 @@ impl ExecutorContext {
|
||||
ast: old_ast,
|
||||
exec_state: old_state,
|
||||
settings: old_settings,
|
||||
}) = cache::read_old_ast_memory().await
|
||||
}) = cache::read_old_ast().await
|
||||
{
|
||||
let old = CacheInformation {
|
||||
ast: &old_ast,
|
||||
@ -595,7 +634,7 @@ impl ExecutorContext {
|
||||
result?;
|
||||
|
||||
// Save this as the last successful execution to the cache.
|
||||
cache::write_old_ast_memory(OldAstState {
|
||||
cache::write_old_ast(OldAstState {
|
||||
ast: program,
|
||||
exec_state: exec_state.clone(),
|
||||
settings: self.settings.clone(),
|
||||
@ -661,6 +700,14 @@ impl ExecutorContext {
|
||||
)
|
||||
})?;
|
||||
|
||||
if !self.is_mock() {
|
||||
cache::write_old_memory(exec_state.memory().clone()).await;
|
||||
}
|
||||
|
||||
crate::log::log(format!(
|
||||
"Post interpretation KCL memory stats: {:#?}",
|
||||
exec_state.memory().stats
|
||||
));
|
||||
let session_data = self.engine.get_session_data();
|
||||
Ok(session_data)
|
||||
}
|
||||
@ -1588,12 +1635,7 @@ let w = f() + f()
|
||||
ctx.run_with_caching(old_program).await.unwrap();
|
||||
|
||||
// Get the id_generator from the first execution.
|
||||
let id_generator = cache::read_old_ast_memory()
|
||||
.await
|
||||
.unwrap()
|
||||
.exec_state
|
||||
.global
|
||||
.id_generator;
|
||||
let id_generator = cache::read_old_ast().await.unwrap().exec_state.global.id_generator;
|
||||
|
||||
let code = r#"sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([62.74, 206.13], %)
|
||||
@ -1614,12 +1656,7 @@ let w = f() + f()
|
||||
// Execute the program.
|
||||
ctx.run_with_caching(program).await.unwrap();
|
||||
|
||||
let new_id_generator = cache::read_old_ast_memory()
|
||||
.await
|
||||
.unwrap()
|
||||
.exec_state
|
||||
.global
|
||||
.id_generator;
|
||||
let new_id_generator = cache::read_old_ast().await.unwrap().exec_state.global.id_generator;
|
||||
|
||||
assert_eq!(id_generator, new_id_generator);
|
||||
}
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::{
|
||||
annotations, kcl_value, Artifact, ArtifactCommand, ArtifactGraph, ArtifactId, ExecOutcome, ExecutorSettings,
|
||||
KclValue, Operation, ProgramMemory, SolidLazyIds, UnitAngle, UnitLen,
|
||||
KclValue, Operation, ProgramMemory, UnitAngle, UnitLen,
|
||||
},
|
||||
modules::{ModuleId, ModuleInfo, ModuleLoader, ModulePath, ModuleRepr},
|
||||
parsing::ast::types::NonCodeValue,
|
||||
@ -27,6 +27,8 @@ pub struct ExecState {
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GlobalState {
|
||||
/// Program variable bindings.
|
||||
pub memory: ProgramMemory,
|
||||
/// The stable artifact ID generator.
|
||||
pub id_generator: IdGenerator,
|
||||
/// Map from source file absolute path to module ID.
|
||||
@ -50,13 +52,9 @@ pub struct GlobalState {
|
||||
pub mod_loader: ModuleLoader,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModuleState {
|
||||
/// Program variable bindings.
|
||||
pub memory: ProgramMemory,
|
||||
/// Dynamic state that follows dynamic flow of the program.
|
||||
pub dynamic_state: DynamicState,
|
||||
/// The current value of the pipe operator returned from the previous
|
||||
/// expression. If we're not currently in a pipeline, this will be None.
|
||||
pub pipe_value: Option<KclValue>,
|
||||
@ -99,7 +97,11 @@ impl ExecState {
|
||||
// Fields are opt-in so that we don't accidentally leak private internal
|
||||
// state when we add more to ExecState.
|
||||
ExecOutcome {
|
||||
memory: self.mod_local.memory,
|
||||
variables: self
|
||||
.memory()
|
||||
.find_all_in_current_env(|_| true)
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
operations: self.mod_local.operations,
|
||||
artifacts: self.global.artifacts,
|
||||
artifact_commands: self.global.artifact_commands,
|
||||
@ -107,12 +109,28 @@ impl ExecState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_mock_wasm_outcome(self) -> ExecOutcome {
|
||||
// Fields are opt-in so that we don't accidentally leak private internal
|
||||
// state when we add more to ExecState.
|
||||
ExecOutcome {
|
||||
variables: self
|
||||
.memory()
|
||||
.find_all_in_current_env(|_| true)
|
||||
.map(|(k, v)| (k.clone(), v.clone()))
|
||||
.collect(),
|
||||
operations: Default::default(),
|
||||
artifacts: Default::default(),
|
||||
artifact_commands: Default::default(),
|
||||
artifact_graph: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn memory(&self) -> &ProgramMemory {
|
||||
&self.mod_local.memory
|
||||
&self.global.memory
|
||||
}
|
||||
|
||||
pub fn mut_memory(&mut self) -> &mut ProgramMemory {
|
||||
&mut self.mod_local.memory
|
||||
&mut self.global.memory
|
||||
}
|
||||
|
||||
pub fn next_uuid(&mut self) -> Uuid {
|
||||
@ -148,11 +166,29 @@ impl ExecState {
|
||||
pub fn angle_unit(&self) -> UnitAngle {
|
||||
self.mod_local.settings.default_angle_units
|
||||
}
|
||||
|
||||
pub(super) fn circular_import_error(&self, path: &ModulePath, source_range: SourceRange) -> KclError {
|
||||
KclError::ImportCycle(KclErrorDetails {
|
||||
message: format!(
|
||||
"circular import of modules is not allowed: {} -> {}",
|
||||
self.global
|
||||
.mod_loader
|
||||
.import_stack
|
||||
.iter()
|
||||
.map(|p| p.as_path().to_string_lossy())
|
||||
.collect::<Vec<_>>()
|
||||
.join(" -> "),
|
||||
path,
|
||||
),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalState {
|
||||
fn new(settings: &ExecutorSettings) -> Self {
|
||||
let mut global = GlobalState {
|
||||
memory: ProgramMemory::new(),
|
||||
id_generator: Default::default(),
|
||||
path_to_source_id: Default::default(),
|
||||
module_infos: Default::default(),
|
||||
@ -181,8 +217,6 @@ impl GlobalState {
|
||||
impl ModuleState {
|
||||
pub(super) fn new(exec_settings: &ExecutorSettings) -> Self {
|
||||
ModuleState {
|
||||
memory: Default::default(),
|
||||
dynamic_state: Default::default(),
|
||||
pipe_value: Default::default(),
|
||||
module_exports: Default::default(),
|
||||
operations: Default::default(),
|
||||
@ -239,46 +273,6 @@ impl MetaSettings {
|
||||
}
|
||||
}
|
||||
|
||||
/// Dynamic state that depends on the dynamic flow of the program, like the call
|
||||
/// stack. If the language had exceptions, for example, you could store the
|
||||
/// stack of exception handlers here.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
|
||||
pub struct DynamicState {
|
||||
pub solid_ids: Vec<SolidLazyIds>,
|
||||
}
|
||||
|
||||
impl DynamicState {
|
||||
#[must_use]
|
||||
pub(super) fn merge(&self, memory: &ProgramMemory) -> Self {
|
||||
let mut merged = self.clone();
|
||||
merged.append(memory);
|
||||
merged
|
||||
}
|
||||
|
||||
fn append(&mut self, memory: &ProgramMemory) {
|
||||
for env in &memory.environments {
|
||||
for item in env.bindings.values() {
|
||||
if let KclValue::Solid { value } = item {
|
||||
self.solid_ids.push(SolidLazyIds::from(value.as_ref()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn edge_cut_ids_on_sketch(&self, sketch_id: uuid::Uuid) -> Vec<uuid::Uuid> {
|
||||
self.solid_ids
|
||||
.iter()
|
||||
.flat_map(|eg| {
|
||||
if eg.sketch_id == sketch_id {
|
||||
eg.edge_cuts.clone()
|
||||
} else {
|
||||
Vec::new()
|
||||
}
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
||||
}
|
||||
|
||||
/// A generator for ArtifactIds that can be stable across executions.
|
||||
#[derive(Debug, Clone, Default, Deserialize, Serialize, PartialEq)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
@ -83,7 +83,9 @@ mod wasm;
|
||||
pub use coredump::CoreDump;
|
||||
pub use engine::{EngineManager, ExecutionKind};
|
||||
pub use errors::{CompilationError, ConnectionError, ExecError, KclError, KclErrorWithOutputs};
|
||||
pub use execution::{bust_cache, ExecOutcome, ExecState, ExecutorContext, ExecutorSettings, MetaSettings, Point2d};
|
||||
pub use execution::{
|
||||
bust_cache, clear_mem_cache, ExecOutcome, ExecState, ExecutorContext, ExecutorSettings, MetaSettings, Point2d,
|
||||
};
|
||||
pub use lsp::{
|
||||
copilot::Backend as CopilotLspBackend,
|
||||
kcl::{Backend as KclLspBackend, Server as KclLspServerSubCommand},
|
||||
|
@ -102,7 +102,7 @@ impl<'a> LogPerfStats<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
/// After `cancel`ing, this object will not log its stats on drop (you can still can `log_now`).
|
||||
/// After `cancel`ing, this object will not log its stats on drop (you can still `log_now`).
|
||||
pub fn cancel(&mut self) {
|
||||
self.cancelled = true;
|
||||
}
|
||||
|
@ -14,15 +14,6 @@ impl Notification for AstUpdated {
|
||||
const METHOD: &'static str = "kcl/astUpdated";
|
||||
}
|
||||
|
||||
/// A notification that the Memory has changed.
|
||||
#[derive(Debug)]
|
||||
pub enum MemoryUpdated {}
|
||||
|
||||
impl Notification for MemoryUpdated {
|
||||
type Params = crate::execution::ProgramMemory;
|
||||
const METHOD: &'static str = "kcl/memoryUpdated";
|
||||
}
|
||||
|
||||
/// Text documents are identified using a URI. On the protocol level, URIs are passed as strings.
|
||||
#[derive(Debug, Eq, PartialEq, Clone, Deserialize, Serialize, ts_rs::TS)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
|
@ -102,8 +102,6 @@ pub struct Backend {
|
||||
pub(super) token_map: DashMap<String, TokenStream>,
|
||||
/// AST maps.
|
||||
pub ast_map: DashMap<String, Node<crate::parsing::ast::types::Program>>,
|
||||
/// Memory maps.
|
||||
pub memory_map: DashMap<String, crate::execution::ProgramMemory>,
|
||||
/// Current code.
|
||||
pub code_map: DashMap<String, Vec<u8>>,
|
||||
/// Diagnostics.
|
||||
@ -181,7 +179,6 @@ impl Backend {
|
||||
workspace_folders: Default::default(),
|
||||
token_map: Default::default(),
|
||||
ast_map: Default::default(),
|
||||
memory_map: Default::default(),
|
||||
code_map: Default::default(),
|
||||
diagnostics_map: Default::default(),
|
||||
symbols_map: Default::default(),
|
||||
@ -193,7 +190,6 @@ impl Backend {
|
||||
fn remove_from_ast_maps(&self, filename: &str) {
|
||||
self.ast_map.remove(filename);
|
||||
self.symbols_map.remove(filename);
|
||||
self.memory_map.remove(filename);
|
||||
}
|
||||
}
|
||||
|
||||
@ -279,13 +275,6 @@ impl crate::lsp::backend::Backend for Backend {
|
||||
}
|
||||
};
|
||||
|
||||
// Try to get the memory for the current code.
|
||||
let has_memory = if let Some(memory) = self.memory_map.get(&filename) {
|
||||
*memory != crate::execution::ProgramMemory::default()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
// Get the previous tokens.
|
||||
let tokens_changed = if let Some(previous_tokens) = self.token_map.get(&filename) {
|
||||
*previous_tokens != tokens
|
||||
@ -293,8 +282,10 @@ impl crate::lsp::backend::Backend for Backend {
|
||||
true
|
||||
};
|
||||
|
||||
let had_diagnostics = self.has_diagnostics(params.uri.as_ref()).await;
|
||||
|
||||
// Check if the tokens are the same.
|
||||
if !tokens_changed && !force && has_memory && !self.has_diagnostics(params.uri.as_ref()).await {
|
||||
if !tokens_changed && !force && !had_diagnostics {
|
||||
// We return early here because the tokens are the same.
|
||||
return;
|
||||
}
|
||||
@ -343,7 +334,7 @@ impl crate::lsp::backend::Backend for Backend {
|
||||
None => true,
|
||||
};
|
||||
|
||||
if !ast_changed && !force && has_memory {
|
||||
if !ast_changed && !force && !had_diagnostics {
|
||||
// Return early if the ast did not change and we don't need to force.
|
||||
return;
|
||||
}
|
||||
@ -680,24 +671,13 @@ impl Backend {
|
||||
|
||||
match executor_ctx.run_with_caching(ast.clone()).await {
|
||||
Err(err) => {
|
||||
self.memory_map.remove(params.uri.as_str());
|
||||
self.add_to_diagnostics(params, &[err.error], false).await;
|
||||
|
||||
// Since we already published the diagnostics we don't really care about the error
|
||||
// string.
|
||||
Err(anyhow::anyhow!("failed to execute code"))
|
||||
}
|
||||
Ok(outcome) => {
|
||||
let memory = outcome.memory;
|
||||
self.memory_map.insert(params.uri.to_string(), memory.clone());
|
||||
|
||||
// Send the notification to the client that the memory was updated.
|
||||
self.client
|
||||
.send_notification::<custom_notifications::MemoryUpdated>(memory)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(_) => Ok(()),
|
||||
}
|
||||
}
|
||||
|
||||
@ -815,8 +795,6 @@ impl Backend {
|
||||
&self,
|
||||
params: custom_notifications::UpdateUnitsParams,
|
||||
) -> RpcResult<Option<custom_notifications::UpdateUnitsResponse>> {
|
||||
let filename = params.text_document.uri.to_string();
|
||||
|
||||
{
|
||||
let mut ctx = self.executor_ctx.write().await;
|
||||
// Borrow the executor context mutably.
|
||||
@ -831,16 +809,8 @@ impl Backend {
|
||||
.log_message(MessageType::INFO, format!("update units: {:?}", params))
|
||||
.await;
|
||||
|
||||
// Try to get the memory for the current code.
|
||||
let has_memory = if let Some(memory) = self.memory_map.get(&filename) {
|
||||
*memory != crate::execution::ProgramMemory::default()
|
||||
} else {
|
||||
false
|
||||
};
|
||||
|
||||
if executor_ctx.settings.units == params.units
|
||||
&& !self.has_diagnostics(params.text_document.uri.as_ref()).await
|
||||
&& has_memory
|
||||
{
|
||||
// Return early the units are the same.
|
||||
return Ok(None);
|
||||
|
@ -28,7 +28,6 @@ pub async fn kcl_lsp_server(execute: bool) -> Result<crate::lsp::kcl::Backend> {
|
||||
stdlib_signatures,
|
||||
token_map: Default::default(),
|
||||
ast_map: Default::default(),
|
||||
memory_map: Default::default(),
|
||||
code_map: Default::default(),
|
||||
diagnostics_map: Default::default(),
|
||||
symbols_map: Default::default(),
|
||||
|
@ -7,7 +7,6 @@ use tower_lsp::{
|
||||
};
|
||||
|
||||
use crate::{
|
||||
execution::ProgramMemory,
|
||||
lsp::test_util::{copilot_lsp_server, kcl_lsp_server},
|
||||
parsing::ast::types::{Node, Program},
|
||||
};
|
||||
@ -2163,9 +2162,6 @@ async fn test_kcl_lsp_on_change_update_ast() {
|
||||
.await;
|
||||
|
||||
assert!(ast != server.ast_map.get("file:///test.kcl").unwrap().clone());
|
||||
|
||||
// Make sure we never updated the memory since we aren't running the engine.
|
||||
assert!(server.memory_map.get("file:///test.kcl").is_none());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2186,9 +2182,6 @@ async fn kcl_test_kcl_lsp_on_change_update_memory() {
|
||||
})
|
||||
.await;
|
||||
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
|
||||
// Send change file.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
@ -2204,9 +2197,6 @@ async fn kcl_test_kcl_lsp_on_change_update_memory() {
|
||||
})
|
||||
.await;
|
||||
|
||||
// Make sure the memory is the same.
|
||||
assert_eq!(memory, server.memory_map.get("file:///test.kcl").unwrap().clone());
|
||||
|
||||
// Update the text.
|
||||
let new_text = r#"thing = 2"#.to_string();
|
||||
// Send change file.
|
||||
@ -2223,8 +2213,6 @@ async fn kcl_test_kcl_lsp_on_change_update_memory() {
|
||||
}],
|
||||
})
|
||||
.await;
|
||||
|
||||
assert!(memory != server.memory_map.get("file:///test.kcl").unwrap().clone());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 10)]
|
||||
@ -2265,9 +2253,6 @@ part001 = cube([0,0], 20)
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(ast.body.len(), 2);
|
||||
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
|
||||
// Send change file.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
@ -2283,9 +2268,6 @@ part001 = cube([0,0], 20)
|
||||
})
|
||||
.await;
|
||||
|
||||
// Make sure the memory is the same.
|
||||
assert_eq!(memory, server.memory_map.get("file:///test.kcl").unwrap().clone());
|
||||
|
||||
let units = server.executor_ctx.read().await.clone().unwrap().settings.units;
|
||||
assert_eq!(units, crate::settings::types::UnitLength::Mm);
|
||||
|
||||
@ -2303,9 +2285,6 @@ part001 = cube([0,0], 20)
|
||||
|
||||
let units = server.executor_ctx().await.clone().unwrap().settings.units;
|
||||
assert_eq!(units, crate::settings::types::UnitLength::M);
|
||||
|
||||
// Make sure it forced a memory update.
|
||||
assert!(memory != server.memory_map.get("file:///test.kcl").unwrap().clone());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2323,10 +2302,6 @@ async fn kcl_test_kcl_lsp_empty_file_execute_ok() {
|
||||
},
|
||||
})
|
||||
.await;
|
||||
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(memory, ProgramMemory::default());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2454,9 +2429,6 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Send change file.
|
||||
server
|
||||
@ -2479,9 +2451,6 @@ async fn kcl_test_kcl_lsp_full_to_empty_file_updates_ast_and_memory() {
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(ast, default_hashed);
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(memory, ProgramMemory::default());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2511,9 +2480,6 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
@ -2545,11 +2511,6 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
|
||||
.insert("file:///test.kcl".to_string(), Node::<Program>::default());
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(ast, Node::<Program>::default());
|
||||
server
|
||||
.memory_map
|
||||
.insert("file:///test.kcl".to_string(), ProgramMemory::default());
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(memory, ProgramMemory::default());
|
||||
|
||||
// Send change file, but the code is the same.
|
||||
server
|
||||
@ -2569,9 +2530,6 @@ async fn kcl_test_kcl_lsp_code_unchanged_but_has_diagnostics_reexecute() {
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
@ -2604,9 +2562,6 @@ async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute()
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
@ -2632,13 +2587,6 @@ async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute()
|
||||
// Assure we have one diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
|
||||
|
||||
// Clear ONLY the memory.
|
||||
server
|
||||
.memory_map
|
||||
.insert("file:///test.kcl".to_string(), ProgramMemory::default());
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(memory, ProgramMemory::default());
|
||||
|
||||
// Send change file, but the code is the same.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
@ -2657,9 +2605,6 @@ async fn kcl_test_kcl_lsp_code_and_ast_unchanged_but_has_diagnostics_reexecute()
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
@ -2692,9 +2637,6 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexe
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
@ -2720,13 +2662,6 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexe
|
||||
// Assure we have one diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
|
||||
|
||||
// Clear ONLY the memory.
|
||||
server
|
||||
.memory_map
|
||||
.insert("file:///test.kcl".to_string(), ProgramMemory::default());
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(memory, ProgramMemory::default());
|
||||
|
||||
let units = server.executor_ctx().await.clone().unwrap().settings.units;
|
||||
assert_eq!(units, crate::settings::types::UnitLength::Mm);
|
||||
|
||||
@ -2748,9 +2683,6 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_diagnostics_reexe
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
@ -2783,20 +2715,10 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
|
||||
// Clear ONLY the memory.
|
||||
server
|
||||
.memory_map
|
||||
.insert("file:///test.kcl".to_string(), ProgramMemory::default());
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(memory, ProgramMemory::default());
|
||||
|
||||
let units = server.executor_ctx().await.clone().unwrap().settings.units;
|
||||
assert_eq!(units, crate::settings::types::UnitLength::Mm);
|
||||
|
||||
@ -2818,9 +2740,6 @@ async fn kcl_test_kcl_lsp_code_and_ast_units_unchanged_but_has_memory_reexecute_
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
@ -2853,20 +2772,10 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
|
||||
// Clear ONLY the memory.
|
||||
server
|
||||
.memory_map
|
||||
.insert("file:///test.kcl".to_string(), ProgramMemory::default());
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(memory, ProgramMemory::default());
|
||||
|
||||
// Update the units to the _same_ units.
|
||||
let units = server.executor_ctx().await.clone().unwrap().settings.units;
|
||||
assert_eq!(units, crate::settings::types::UnitLength::Mm);
|
||||
@ -2887,20 +2796,10 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
|
||||
// Clear ONLY the memory.
|
||||
server
|
||||
.memory_map
|
||||
.insert("file:///test.kcl".to_string(), ProgramMemory::default());
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert_eq!(memory, ProgramMemory::default());
|
||||
|
||||
assert_eq!(server.can_execute().await, true);
|
||||
|
||||
// Set that we cannot execute.
|
||||
@ -2933,10 +2832,6 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != default_hashed);
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
// Now it should be the default memory.
|
||||
assert!(memory == ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
@ -2968,10 +2863,6 @@ async fn kcl_test_kcl_lsp_cant_execute_set() {
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
// Now it should NOT be the default memory.
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Assure we have no diagnostics.
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 0);
|
||||
@ -3219,9 +3110,6 @@ part001 = startSketchOn('XY')
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
|
||||
// Send change file, but the code is the same.
|
||||
server
|
||||
@ -3241,9 +3129,6 @@ part001 = startSketchOn('XY')
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
|
||||
// Assure we have diagnostics.
|
||||
|
||||
@ -3284,9 +3169,6 @@ part001 = startSketchOn('XY')
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
|
||||
// Send change file, but the code is the same.
|
||||
server
|
||||
@ -3314,9 +3196,6 @@ NEW_LINT = 1"#
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(ast != Node::<Program>::default());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
|
||||
// Assure we have diagnostics.
|
||||
|
||||
@ -3357,9 +3236,6 @@ part001 = startSketchOn('XY')
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl");
|
||||
assert!(ast.is_none());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
|
||||
// Send change file, but the code is the same.
|
||||
server
|
||||
@ -3387,9 +3263,6 @@ NEW_LINT = 1"#
|
||||
// Get the ast.
|
||||
let ast = server.ast_map.get("file:///test.kcl");
|
||||
assert!(ast.is_none());
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
|
||||
// Assure we have diagnostics.
|
||||
|
||||
@ -3439,10 +3312,6 @@ part001 = startSketchOn('XY')
|
||||
let semantic_tokens_map = server.semantic_tokens_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(!semantic_tokens_map.is_empty());
|
||||
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Send change file, but the code is the same.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
@ -3478,10 +3347,6 @@ NEW_LINT = 1"#
|
||||
let semantic_tokens_map = server.semantic_tokens_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(!semantic_tokens_map.is_empty());
|
||||
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
|
||||
// Assure we have diagnostics.
|
||||
|
||||
// Check the diagnostics.
|
||||
@ -3534,10 +3399,6 @@ part001 = startSketchOn('XY')
|
||||
let semantic_tokens_map = server.semantic_tokens_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(!semantic_tokens_map.is_empty());
|
||||
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(memory != ProgramMemory::default());
|
||||
|
||||
// Send change file, but the code is the same.
|
||||
server
|
||||
.did_change(tower_lsp::lsp_types::DidChangeTextDocumentParams {
|
||||
@ -3577,10 +3438,6 @@ part001 = startSketchOn('XY')
|
||||
let semantic_tokens_map = server.semantic_tokens_map.get("file:///test.kcl").unwrap().clone();
|
||||
assert!(!semantic_tokens_map.is_empty());
|
||||
|
||||
// Get the memory.
|
||||
let memory = server.memory_map.get("file:///test.kcl");
|
||||
assert!(memory.is_none());
|
||||
|
||||
// Assure we have diagnostics.
|
||||
|
||||
// Check the diagnostics.
|
||||
|
@ -6,7 +6,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
use crate::{
|
||||
errors::{KclError, KclErrorDetails},
|
||||
execution::PreImportedGeometry,
|
||||
execution::{EnvironmentRef, PreImportedGeometry},
|
||||
fs::{FileManager, FileSystem},
|
||||
parsing::ast::types::{ImportPath, Node, Program},
|
||||
source_range::SourceRange,
|
||||
@ -78,8 +78,7 @@ pub(crate) fn read_std(_mod_name: &str) -> Option<&'static str> {
|
||||
None
|
||||
}
|
||||
|
||||
/// Info about a module. Right now, this is pretty minimal. We hope to cache
|
||||
/// modules here in the future.
|
||||
/// Info about a module.
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub struct ModuleInfo {
|
||||
/// The ID of the module.
|
||||
@ -89,12 +88,27 @@ pub struct ModuleInfo {
|
||||
pub(crate) repr: ModuleRepr,
|
||||
}
|
||||
|
||||
impl ModuleInfo {
|
||||
pub(crate) fn take_repr(&mut self) -> ModuleRepr {
|
||||
let mut result = ModuleRepr::Dummy;
|
||||
std::mem::swap(&mut self.repr, &mut result);
|
||||
result
|
||||
}
|
||||
|
||||
pub(crate) fn restore_repr(&mut self, repr: ModuleRepr) {
|
||||
assert!(matches!(&self.repr, ModuleRepr::Dummy));
|
||||
self.repr = repr;
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
|
||||
pub enum ModuleRepr {
|
||||
Root,
|
||||
Kcl(Node<Program>),
|
||||
// AST, memory, exported names
|
||||
Kcl(Node<Program>, Option<(EnvironmentRef, Vec<String>)>),
|
||||
Foreign(PreImportedGeometry),
|
||||
Dummy,
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
|
@ -3232,6 +3232,21 @@ impl FunctionExpression {
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
pub fn dummy() -> Box<Node<Self>> {
|
||||
Box::new(Node::new(
|
||||
FunctionExpression {
|
||||
params: Vec::new(),
|
||||
body: Node::new(Program::default(), 0, 0, ModuleId::default()),
|
||||
return_type: None,
|
||||
digest: None,
|
||||
},
|
||||
0,
|
||||
0,
|
||||
ModuleId::default(),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
|
@ -103,7 +103,7 @@ async fn execute(test_name: &str, render_to_png: bool) {
|
||||
twenty_twenty::assert_image(format!("tests/{test_name}/rendered_model.png"), &png, 0.99);
|
||||
}
|
||||
assert_snapshot(test_name, "Program memory after executing", || {
|
||||
insta::assert_json_snapshot!("program_memory", exec_state.mod_local.memory, {
|
||||
insta::assert_json_snapshot!("program_memory", exec_state.memory(), {
|
||||
".environments[].**[].from[]" => rounded_redaction(4),
|
||||
".environments[].**[].to[]" => rounded_redaction(4),
|
||||
".environments[].**[].x[]" => rounded_redaction(4),
|
||||
|
@ -188,7 +188,7 @@ impl Args {
|
||||
exec_state: &'e mut ExecState,
|
||||
tag: &'a TagIdentifier,
|
||||
) -> Result<&'e crate::execution::TagEngineInfo, KclError> {
|
||||
if let KclValue::TagIdentifier(t) = exec_state.memory().get(&tag.value, self.source_range)? {
|
||||
if let KclValue::TagIdentifier(t) = exec_state.memory().get_from_call_stack(&tag.value, self.source_range)? {
|
||||
Ok(t.info.as_ref().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Tag `{}` does not have engine info", tag.value),
|
||||
@ -255,11 +255,13 @@ impl Args {
|
||||
ids.extend(
|
||||
exec_state
|
||||
.memory()
|
||||
.find_solids_on_sketch(solid.sketch.id)
|
||||
.iter()
|
||||
.flat_map(|eg| eg.get_all_edge_cut_ids()),
|
||||
.walk_call_stack()
|
||||
.filter(|v| matches!(v, KclValue::Solid { value } if value.sketch.id == sketch_id))
|
||||
.flat_map(|v| match v {
|
||||
KclValue::Solid { value } => value.get_all_edge_cut_ids(),
|
||||
_ => unreachable!(),
|
||||
}),
|
||||
);
|
||||
ids.extend(exec_state.mod_local.dynamic_state.edge_cut_ids_on_sketch(sketch_id));
|
||||
traversed_sketches.push(sketch_id);
|
||||
}
|
||||
|
||||
|
@ -19,7 +19,7 @@ pub async fn map(exec_state: &mut ExecState, args: Args) -> Result<KclValue, Kcl
|
||||
fn_expr: f.expr,
|
||||
meta: meta.clone(),
|
||||
ctx: args.ctx.clone(),
|
||||
memory: *f.memory,
|
||||
memory: f.memory,
|
||||
};
|
||||
let new_array = inner_map(array, map_fn, exec_state, &args).await?;
|
||||
Ok(KclValue::Array { value: new_array, meta })
|
||||
@ -97,7 +97,7 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
fn_expr: f.expr,
|
||||
meta: vec![args.source_range.into()],
|
||||
ctx: args.ctx.clone(),
|
||||
memory: *f.memory,
|
||||
memory: f.memory,
|
||||
};
|
||||
inner_reduce(array, start, reduce_fn, exec_state, &args).await
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ use serde::{Deserialize, Serialize};
|
||||
use crate::{
|
||||
docs::StdLibFn,
|
||||
errors::KclError,
|
||||
execution::{ExecState, KclValue, ProgramMemory},
|
||||
execution::{EnvironmentRef, ExecState, KclValue},
|
||||
parsing::ast::types::FunctionExpression,
|
||||
};
|
||||
|
||||
@ -305,5 +305,5 @@ pub enum Primitive {
|
||||
pub struct FnAsArg<'a> {
|
||||
pub func: Option<&'a crate::execution::MemoryFunction>,
|
||||
pub expr: crate::parsing::ast::types::BoxNode<FunctionExpression>,
|
||||
pub memory: Box<ProgramMemory>,
|
||||
pub memory: EnvironmentRef,
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ pub async fn pattern_transform(exec_state: &mut ExecState, args: Args) -> Result
|
||||
fn_expr: transform.expr,
|
||||
meta: vec![args.source_range.into()],
|
||||
ctx: args.ctx.clone(),
|
||||
memory: *transform.memory,
|
||||
memory: transform.memory,
|
||||
},
|
||||
extr,
|
||||
use_original,
|
||||
@ -95,7 +95,7 @@ pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Res
|
||||
fn_expr: transform.expr,
|
||||
meta: vec![args.source_range.into()],
|
||||
ctx: args.ctx.clone(),
|
||||
memory: *transform.memory,
|
||||
memory: transform.memory,
|
||||
},
|
||||
sketch,
|
||||
use_original,
|
||||
|
@ -81,37 +81,10 @@ description: Program memory after executing add_lots.kcl
|
||||
"start": 4,
|
||||
"type": "FunctionExpression"
|
||||
},
|
||||
"memory": {
|
||||
"environments": [
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
}
|
||||
},
|
||||
"parent": null
|
||||
}
|
||||
"memory": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
@ -1543,9 +1516,29 @@ description: Program memory after executing add_lots.kcl
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [
|
||||
{
|
||||
"parent_snapshot": null,
|
||||
"data": {
|
||||
"f": {
|
||||
"type": "Tombstone",
|
||||
"value": null,
|
||||
"__meta": []
|
||||
},
|
||||
"x": {
|
||||
"type": "Tombstone",
|
||||
"value": null,
|
||||
"__meta": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -438,9 +438,13 @@ description: Program memory after executing angled_line.kcl
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -160,9 +160,13 @@ description: Program memory after executing array_elem_pop.kcl
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
assertion_line: 92
|
||||
description: Program memory after executing array_elem_push.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -227,9 +225,13 @@ snapshot_kind: text
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
assertion_line: 92
|
||||
description: Program memory after executing array_range_expr.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -384,9 +382,13 @@ snapshot_kind: text
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
assertion_line: 92
|
||||
description: Program memory after executing array_range_negative_expr.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -186,9 +184,13 @@ snapshot_kind: text
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -1969,9 +1969,13 @@ description: Program memory after executing artifact_graph_example_code1.kcl
|
||||
}
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -625,9 +625,13 @@ description: Program memory after executing artifact_graph_example_code_no_3d.kc
|
||||
}
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -216,9 +216,13 @@ snapshot_kind: text
|
||||
}
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -4909,9 +4909,13 @@ description: Program memory after executing artifact_graph_sketch_on_face_etc.kc
|
||||
}
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -650,9 +650,13 @@ description: Program memory after executing basic_fillet_cube_close_opposite.kcl
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -521,9 +521,13 @@ description: Program memory after executing basic_fillet_cube_end.kcl
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -772,9 +772,13 @@ description: Program memory after executing basic_fillet_cube_next_adjacent.kcl
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -772,9 +772,13 @@ description: Program memory after executing basic_fillet_cube_previous_adjacent.
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -521,9 +521,13 @@ description: Program memory after executing basic_fillet_cube_start.kcl
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -345,9 +345,13 @@ description: Program memory after executing big_number_angle_to_match_length_x.k
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -345,9 +345,13 @@ description: Program memory after executing big_number_angle_to_match_length_y.k
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -159,9 +159,13 @@ description: Program memory after executing boolean_logical_and.kcl
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -121,9 +121,13 @@ description: Program memory after executing boolean_logical_multiple.kcl
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -159,9 +159,13 @@ description: Program memory after executing boolean_logical_or.kcl
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -159,9 +159,13 @@ snapshot_kind: text
|
||||
}
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -63466,9 +63466,13 @@ snapshot_kind: text
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -1,7 +1,6 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing comparisons.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -28,9 +27,13 @@ snapshot_kind: text
|
||||
"__meta": []
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
assertion_line: 92
|
||||
description: Program memory after executing computed_var.kcl
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
@ -187,9 +185,13 @@ snapshot_kind: text
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -696,37 +696,10 @@ description: Program memory after executing cube.kcl
|
||||
"start": 7,
|
||||
"type": "FunctionExpression"
|
||||
},
|
||||
"memory": {
|
||||
"environments": [
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
}
|
||||
},
|
||||
"parent": null
|
||||
}
|
||||
"memory": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
@ -976,9 +949,29 @@ description: Program memory after executing cube.kcl
|
||||
}
|
||||
}
|
||||
},
|
||||
"snapshots": [
|
||||
{
|
||||
"parent_snapshot": null,
|
||||
"data": {
|
||||
"cube": {
|
||||
"type": "Tombstone",
|
||||
"value": null,
|
||||
"__meta": []
|
||||
},
|
||||
"myCube": {
|
||||
"type": "Tombstone",
|
||||
"value": null,
|
||||
"__meta": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
@ -81,37 +81,10 @@ description: Program memory after executing double_map_fn.kcl
|
||||
"start": 12,
|
||||
"type": "FunctionExpression"
|
||||
},
|
||||
"memory": {
|
||||
"environments": [
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
}
|
||||
},
|
||||
"parent": null
|
||||
}
|
||||
"memory": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
@ -271,9 +244,34 @@ description: Program memory after executing double_map_fn.kcl
|
||||
]
|
||||
}
|
||||
},
|
||||
"snapshots": [
|
||||
{
|
||||
"parent_snapshot": null,
|
||||
"data": {
|
||||
"increment": {
|
||||
"type": "Tombstone",
|
||||
"value": null,
|
||||
"__meta": []
|
||||
},
|
||||
"xs": {
|
||||
"type": "Tombstone",
|
||||
"value": null,
|
||||
"__meta": []
|
||||
},
|
||||
"ys": {
|
||||
"type": "Tombstone",
|
||||
"value": null,
|
||||
"__meta": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -394,37 +394,10 @@ description: Program memory after executing function_sketch.kcl
|
||||
"start": 6,
|
||||
"type": "FunctionExpression"
|
||||
},
|
||||
"memory": {
|
||||
"environments": [
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
}
|
||||
},
|
||||
"parent": null
|
||||
}
|
||||
"memory": [
|
||||
0,
|
||||
1
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
},
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
@ -654,9 +627,29 @@ description: Program memory after executing function_sketch.kcl
|
||||
}
|
||||
}
|
||||
},
|
||||
"snapshots": [
|
||||
{
|
||||
"parent_snapshot": null,
|
||||
"data": {
|
||||
"box": {
|
||||
"type": "Tombstone",
|
||||
"value": null,
|
||||
"__meta": []
|
||||
},
|
||||
"fnBox": {
|
||||
"type": "Tombstone",
|
||||
"value": null,
|
||||
"__meta": []
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
"currentEnv": [
|
||||
0,
|
||||
0
|
||||
],
|
||||
"callStack": []
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user