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