Compare commits
16 Commits
v0.29.0
...
kcl-0.2.28
Author | SHA1 | Date | |
---|---|---|---|
f876e6ca3c | |||
60a0c811ab | |||
cab0c1e6a1 | |||
417d720b22 | |||
77293952c0 | |||
ea3d604b73 | |||
023a659491 | |||
dd3a2b14f9 | |||
424b409cc1 | |||
82a58e69c2 | |||
776b420031 | |||
1087d4223b | |||
089d6df889 | |||
efb067af58 | |||
2aa27eab01 | |||
9c47ac5b57 |
11
.github/workflows/build-apps.yml
vendored
@ -382,3 +382,14 @@ jobs:
|
|||||||
glob: '*'
|
glob: '*'
|
||||||
parent: false
|
parent: false
|
||||||
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
destination: 'dl.kittycad.io/releases/modeling-app/nightly'
|
||||||
|
|
||||||
|
- name: Tag nightly commit
|
||||||
|
if: ${{ env.IS_NIGHTLY == 'true' }}
|
||||||
|
uses: actions/github-script@v7
|
||||||
|
with:
|
||||||
|
script: |
|
||||||
|
const { VERSION } = process.env
|
||||||
|
const { owner, repo } = context.repo
|
||||||
|
const { sha } = context
|
||||||
|
const ref = `refs/tags/nightly-${VERSION}`
|
||||||
|
github.rest.git.createRef({ owner, repo, sha, ref })
|
||||||
|
@ -12,7 +12,7 @@ to other modules.
|
|||||||
|
|
||||||
```
|
```
|
||||||
// util.kcl
|
// util.kcl
|
||||||
export fn increment = (x) => {
|
export fn increment(x) {
|
||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -37,11 +37,11 @@ Multiple functions can be exported in a file.
|
|||||||
|
|
||||||
```
|
```
|
||||||
// util.kcl
|
// util.kcl
|
||||||
export fn increment = (x) => {
|
export fn increment(x) {
|
||||||
return x + 1
|
return x + 1
|
||||||
}
|
}
|
||||||
|
|
||||||
export fn decrement = (x) => {
|
export fn decrement(x) {
|
||||||
return x - 1
|
return x - 1
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
@ -54,7 +54,7 @@ We also have support for defining your own functions. Functions can take in any
|
|||||||
type of argument. Below is an example of the syntax:
|
type of argument. Below is an example of the syntax:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn myFn = (x) => {
|
fn myFn(x) {
|
||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -118,7 +118,7 @@ use the tag `rectangleSegmentA001` in any function or expression in the file.
|
|||||||
However if the code was written like this:
|
However if the code was written like this:
|
||||||
|
|
||||||
```
|
```
|
||||||
fn rect = (origin) => {
|
fn rect(origin) {
|
||||||
return startSketchOn('XZ')
|
return startSketchOn('XZ')
|
||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
@ -146,7 +146,7 @@ Tags are accessible through the sketch group they are declared in.
|
|||||||
For example the following code works.
|
For example the following code works.
|
||||||
|
|
||||||
```
|
```
|
||||||
fn rect = (origin) => {
|
fn rect(origin) {
|
||||||
return startSketchOn('XZ')
|
return startSketchOn('XZ')
|
||||||
|> startProfileAt(origin, %)
|
|> startProfileAt(origin, %)
|
||||||
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
|> angledLine([0, 191.26], %, $rectangleSegmentA001)
|
||||||
|
@ -6,6 +6,7 @@ export class ToolbarFixture {
|
|||||||
public page: Page
|
public page: Page
|
||||||
|
|
||||||
extrudeButton!: Locator
|
extrudeButton!: Locator
|
||||||
|
loftButton!: Locator
|
||||||
offsetPlaneButton!: Locator
|
offsetPlaneButton!: Locator
|
||||||
startSketchBtn!: Locator
|
startSketchBtn!: Locator
|
||||||
lineBtn!: Locator
|
lineBtn!: Locator
|
||||||
@ -26,6 +27,7 @@ export class ToolbarFixture {
|
|||||||
reConstruct = (page: Page) => {
|
reConstruct = (page: Page) => {
|
||||||
this.page = page
|
this.page = page
|
||||||
this.extrudeButton = page.getByTestId('extrude')
|
this.extrudeButton = page.getByTestId('extrude')
|
||||||
|
this.loftButton = page.getByTestId('loft')
|
||||||
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
this.offsetPlaneButton = page.getByTestId('plane-offset')
|
||||||
this.startSketchBtn = page.getByTestId('sketch')
|
this.startSketchBtn = page.getByTestId('sketch')
|
||||||
this.lineBtn = page.getByTestId('line')
|
this.lineBtn = page.getByTestId('line')
|
||||||
|
@ -677,3 +677,94 @@ test(`Offset plane point-and-click`, async ({
|
|||||||
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
await scene.expectPixelColor([74, 74, 74], testPoint, 15)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const loftPointAndClickCases = [
|
||||||
|
{ shouldPreselect: true },
|
||||||
|
{ shouldPreselect: false },
|
||||||
|
]
|
||||||
|
loftPointAndClickCases.forEach(({ shouldPreselect }) => {
|
||||||
|
test(`Loft point-and-click (preselected sketches: ${shouldPreselect})`, async ({
|
||||||
|
app,
|
||||||
|
page,
|
||||||
|
scene,
|
||||||
|
editor,
|
||||||
|
toolbar,
|
||||||
|
cmdBar,
|
||||||
|
}) => {
|
||||||
|
const initialCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 30 }, %)
|
||||||
|
plane001 = offsetPlane('XZ', 50)
|
||||||
|
sketch002 = startSketchOn(plane001)
|
||||||
|
|> circle({ center = [0, 0], radius = 20 }, %)
|
||||||
|
`
|
||||||
|
await app.initialise(initialCode)
|
||||||
|
|
||||||
|
// One dumb hardcoded screen pixel value
|
||||||
|
const testPoint = { x: 575, y: 200 }
|
||||||
|
const [clickOnSketch1] = scene.makeMouseHelpers(testPoint.x, testPoint.y)
|
||||||
|
const [clickOnSketch2] = scene.makeMouseHelpers(
|
||||||
|
testPoint.x,
|
||||||
|
testPoint.y + 80
|
||||||
|
)
|
||||||
|
const loftDeclaration = 'loft001 = loft([sketch001, sketch002])'
|
||||||
|
|
||||||
|
await test.step(`Look for the white of the sketch001 shape`, async () => {
|
||||||
|
await scene.expectPixelColor([254, 254, 254], testPoint, 15)
|
||||||
|
})
|
||||||
|
|
||||||
|
async function selectSketches() {
|
||||||
|
await clickOnSketch1()
|
||||||
|
await page.keyboard.down('Shift')
|
||||||
|
await clickOnSketch2()
|
||||||
|
await app.page.waitForTimeout(500)
|
||||||
|
await page.keyboard.up('Shift')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!shouldPreselect) {
|
||||||
|
await test.step(`Go through the command bar flow without preselected sketches`, async () => {
|
||||||
|
await toolbar.loftButton.click()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'arguments',
|
||||||
|
currentArgKey: 'selection',
|
||||||
|
currentArgValue: '',
|
||||||
|
headerArguments: { Selection: '' },
|
||||||
|
highlightedHeaderArg: 'selection',
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await selectSketches()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: { Selection: '2 faces' },
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
await test.step(`Preselect the two sketches`, async () => {
|
||||||
|
await selectSketches()
|
||||||
|
})
|
||||||
|
|
||||||
|
await test.step(`Go through the command bar flow with preselected sketches`, async () => {
|
||||||
|
await toolbar.loftButton.click()
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
await cmdBar.expectState({
|
||||||
|
stage: 'review',
|
||||||
|
headerArguments: { Selection: '2 faces' },
|
||||||
|
commandName: 'Loft',
|
||||||
|
})
|
||||||
|
await cmdBar.progressCmdBar()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
await test.step(`Confirm code is added to the editor, scene has changed`, async () => {
|
||||||
|
await editor.expectEditor.toContain(loftDeclaration)
|
||||||
|
await editor.expectState({
|
||||||
|
diagnostics: [],
|
||||||
|
activeLines: [loftDeclaration],
|
||||||
|
highlightedCode: '',
|
||||||
|
})
|
||||||
|
await scene.expectPixelColor([89, 89, 89], testPoint, 15)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
Before Width: | Height: | Size: 38 KiB After Width: | Height: | Size: 38 KiB |
Before Width: | Height: | Size: 39 KiB After Width: | Height: | Size: 39 KiB |
@ -192,7 +192,7 @@
|
|||||||
"eslint-plugin-css-modules": "^2.12.0",
|
"eslint-plugin-css-modules": "^2.12.0",
|
||||||
"eslint-plugin-import": "^2.30.0",
|
"eslint-plugin-import": "^2.30.0",
|
||||||
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
"eslint-plugin-suggest-no-throw": "^1.0.0",
|
||||||
"happy-dom": "^15.10.2",
|
"happy-dom": "^15.11.7",
|
||||||
"http-server": "^14.1.1",
|
"http-server": "^14.1.1",
|
||||||
"husky": "^9.1.5",
|
"husky": "^9.1.5",
|
||||||
"kill-port": "^2.0.1",
|
"kill-port": "^2.0.1",
|
||||||
@ -212,7 +212,7 @@
|
|||||||
"vite-tsconfig-paths": "^4.3.2",
|
"vite-tsconfig-paths": "^4.3.2",
|
||||||
"vitest": "^1.6.0",
|
"vitest": "^1.6.0",
|
||||||
"vitest-webgl-canvas-mock": "^1.1.0",
|
"vitest-webgl-canvas-mock": "^1.1.0",
|
||||||
"wasm-pack": "^0.13.0",
|
"wasm-pack": "^0.13.1",
|
||||||
"ws": "^8.17.0",
|
"ws": "^8.17.0",
|
||||||
"yarn": "^1.22.22"
|
"yarn": "^1.22.22"
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,8 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
export VERSION=$(date +'%-y.%-m.%-d')
|
export VERSION=$(date +'%-y.%-m.%-d')
|
||||||
|
export TAG="nightly-v$VERSION"
|
||||||
|
export PREVIOUS_TAG=$(git describe --tags --match="nightly-v[0-9]*" --abbrev=0)
|
||||||
export COMMIT=$(git rev-parse --short HEAD)
|
export COMMIT=$(git rev-parse --short HEAD)
|
||||||
|
|
||||||
# package.json
|
# package.json
|
||||||
@ -13,7 +15,7 @@ yq -i '.publish[0].url = "https://dl.zoo.dev/releases/modeling-app/nightly"' ele
|
|||||||
yq -i '.appId = "dev.zoo.modeling-app-nightly"' electron-builder.yml
|
yq -i '.appId = "dev.zoo.modeling-app-nightly"' electron-builder.yml
|
||||||
|
|
||||||
# Release notes
|
# Release notes
|
||||||
echo "Nightly build $VERSION (commit $COMMIT)" > release-notes.md
|
./scripts/get-nightly-changelog.sh > release-notes.md
|
||||||
|
|
||||||
# icons
|
# icons
|
||||||
cp assets/icon-nightly.png assets/icon.png
|
cp assets/icon-nightly.png assets/icon.png
|
||||||
|
5
scripts/get-nightly-changelog.sh
Executable file
@ -0,0 +1,5 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "## What's Changed"
|
||||||
|
git log ${PREVIOUS_TAG}..HEAD --oneline --pretty=format:%s | grep -v Bump | awk '{print "* "toupper(substr($0,0,1))substr($0,2)}'
|
||||||
|
echo ""
|
||||||
|
echo "**Full Changelog**: https://github.com/KittyCAD/modeling-app/compare/${PREVIOUS_TAG}...${TAG}"
|
@ -273,14 +273,26 @@ export class CameraControls {
|
|||||||
camSettings.center.y,
|
camSettings.center.y,
|
||||||
camSettings.center.z
|
camSettings.center.z
|
||||||
)
|
)
|
||||||
const quat = new Quaternion(
|
const orientation = new Quaternion(
|
||||||
camSettings.orientation.x,
|
camSettings.orientation.x,
|
||||||
camSettings.orientation.y,
|
camSettings.orientation.y,
|
||||||
camSettings.orientation.z,
|
camSettings.orientation.z,
|
||||||
camSettings.orientation.w
|
camSettings.orientation.w
|
||||||
).invert()
|
).invert()
|
||||||
|
|
||||||
this.camera.up.copy(new Vector3(0, 1, 0).applyQuaternion(quat))
|
const newUp = new Vector3(
|
||||||
|
camSettings.up.x,
|
||||||
|
camSettings.up.y,
|
||||||
|
camSettings.up.z
|
||||||
|
)
|
||||||
|
this.camera.quaternion.set(
|
||||||
|
orientation.x,
|
||||||
|
orientation.y,
|
||||||
|
orientation.z,
|
||||||
|
orientation.w
|
||||||
|
)
|
||||||
|
this.camera.up.copy(newUp)
|
||||||
|
this.camera.updateProjectionMatrix()
|
||||||
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
if (this.camera instanceof PerspectiveCamera && camSettings.ortho) {
|
||||||
this.useOrthographicCamera()
|
this.useOrthographicCamera()
|
||||||
}
|
}
|
||||||
@ -1164,7 +1176,7 @@ export class CameraControls {
|
|||||||
this.camera.updateProjectionMatrix()
|
this.camera.updateProjectionMatrix()
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.syncDirection === 'clientToEngine' || forceUpdate)
|
if (this.syncDirection === 'clientToEngine' || forceUpdate) {
|
||||||
this.throttledUpdateEngineCamera({
|
this.throttledUpdateEngineCamera({
|
||||||
quaternion: this.camera.quaternion,
|
quaternion: this.camera.quaternion,
|
||||||
position: this.camera.position,
|
position: this.camera.position,
|
||||||
@ -1172,6 +1184,7 @@ export class CameraControls {
|
|||||||
isPerspective: this.isPerspective,
|
isPerspective: this.isPerspective,
|
||||||
target: this.target,
|
target: this.target,
|
||||||
})
|
})
|
||||||
|
}
|
||||||
this.deferReactUpdate(this.reactCameraProperties)
|
this.deferReactUpdate(this.reactCameraProperties)
|
||||||
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
Object.values(this._camChangeCallbacks).forEach((cb) => cb())
|
||||||
}
|
}
|
||||||
|
@ -50,6 +50,7 @@ import {
|
|||||||
isSketchPipe,
|
isSketchPipe,
|
||||||
Selections,
|
Selections,
|
||||||
updateSelections,
|
updateSelections,
|
||||||
|
canLoftSelection,
|
||||||
} from 'lib/selections'
|
} from 'lib/selections'
|
||||||
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
import { applyConstraintIntersect } from './Toolbar/Intersect'
|
||||||
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
import { applyConstraintAbsDistance } from './Toolbar/SetAbsDistance'
|
||||||
@ -82,7 +83,7 @@ import { getVarNameModal } from 'hooks/useToolbarGuards'
|
|||||||
import { err, reportRejection, trap } from 'lib/trap'
|
import { err, reportRejection, trap } from 'lib/trap'
|
||||||
import { useCommandsContext } from 'hooks/useCommandsContext'
|
import { useCommandsContext } from 'hooks/useCommandsContext'
|
||||||
import { modelingMachineEvent } from 'editor/manager'
|
import { modelingMachineEvent } from 'editor/manager'
|
||||||
import { hasValidEdgeTreatmentSelection } from 'lang/modifyAst/addFillet'
|
import { hasValidEdgeTreatmentSelection } from 'lang/modifyAst/addEdgeTreatment'
|
||||||
import {
|
import {
|
||||||
ExportIntent,
|
ExportIntent,
|
||||||
EngineConnectionStateType,
|
EngineConnectionStateType,
|
||||||
@ -569,6 +570,21 @@ export const ModelingMachineProvider = ({
|
|||||||
if (err(canSweep)) return false
|
if (err(canSweep)) return false
|
||||||
return canSweep
|
return canSweep
|
||||||
},
|
},
|
||||||
|
'has valid loft selection': ({ context: { selectionRanges } }) => {
|
||||||
|
const hasNoSelection =
|
||||||
|
selectionRanges.graphSelections.length === 0 ||
|
||||||
|
isRangeBetweenCharacters(selectionRanges) ||
|
||||||
|
isSelectionLastLine(selectionRanges, codeManager.code)
|
||||||
|
|
||||||
|
if (hasNoSelection) {
|
||||||
|
const count = 2
|
||||||
|
return doesSceneHaveSweepableSketch(kclManager.ast, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
const canLoft = canLoftSelection(selectionRanges)
|
||||||
|
if (err(canLoft)) return false
|
||||||
|
return canLoft
|
||||||
|
},
|
||||||
'has valid selection for deletion': ({
|
'has valid selection for deletion': ({
|
||||||
context: { selectionRanges },
|
context: { selectionRanges },
|
||||||
}) => {
|
}) => {
|
||||||
|
@ -346,6 +346,37 @@ export function extrudeSketch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function loftSketches(
|
||||||
|
node: Node<Program>,
|
||||||
|
declarators: VariableDeclarator[]
|
||||||
|
): {
|
||||||
|
modifiedAst: Node<Program>
|
||||||
|
pathToNode: PathToNode
|
||||||
|
} {
|
||||||
|
const modifiedAst = structuredClone(node)
|
||||||
|
const name = findUniqueName(node, KCL_DEFAULT_CONSTANT_PREFIXES.LOFT)
|
||||||
|
const elements = declarators.map((d) => createIdentifier(d.id.name))
|
||||||
|
const loft = createCallExpressionStdLib('loft', [
|
||||||
|
createArrayExpression(elements),
|
||||||
|
])
|
||||||
|
const declaration = createVariableDeclaration(name, loft)
|
||||||
|
modifiedAst.body.push(declaration)
|
||||||
|
const pathToNode: PathToNode = [
|
||||||
|
['body', ''],
|
||||||
|
[modifiedAst.body.length - 1, 'index'],
|
||||||
|
['declarations', 'VariableDeclaration'],
|
||||||
|
['0', 'index'],
|
||||||
|
['init', 'VariableDeclarator'],
|
||||||
|
['arguments', 'CallExpression'],
|
||||||
|
[0, 'index'],
|
||||||
|
]
|
||||||
|
|
||||||
|
return {
|
||||||
|
modifiedAst,
|
||||||
|
pathToNode,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function revolveSketch(
|
export function revolveSketch(
|
||||||
node: Node<Program>,
|
node: Node<Program>,
|
||||||
pathToNode: PathToNode,
|
pathToNode: PathToNode,
|
||||||
|
@ -18,7 +18,7 @@ import {
|
|||||||
FilletParameters,
|
FilletParameters,
|
||||||
ChamferParameters,
|
ChamferParameters,
|
||||||
EdgeTreatmentParameters,
|
EdgeTreatmentParameters,
|
||||||
} from './addFillet'
|
} from './addEdgeTreatment'
|
||||||
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
import { getNodeFromPath, getNodePathFromSourceRange } from '../queryAst'
|
||||||
import { createLiteral } from 'lang/modifyAst'
|
import { createLiteral } from 'lang/modifyAst'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
@ -628,6 +628,18 @@ sketch002 = startSketchOn(extrude001, $seg01)
|
|||||||
const extrudable = doesSceneHaveSweepableSketch(ast)
|
const extrudable = doesSceneHaveSweepableSketch(ast)
|
||||||
expect(extrudable).toBeTruthy()
|
expect(extrudable).toBeTruthy()
|
||||||
})
|
})
|
||||||
|
it('finds sketch001 and sketch002 pipes to be lofted', async () => {
|
||||||
|
const exampleCode = `sketch001 = startSketchOn('XZ')
|
||||||
|
|> circle({ center = [0, 0], radius = 1 }, %)
|
||||||
|
plane001 = offsetPlane('XZ', 2)
|
||||||
|
sketch002 = startSketchOn(plane001)
|
||||||
|
|> circle({ center = [0, 0], radius = 3 }, %)
|
||||||
|
`
|
||||||
|
const ast = parse(exampleCode)
|
||||||
|
if (err(ast)) throw ast
|
||||||
|
const extrudable = doesSceneHaveSweepableSketch(ast, 2)
|
||||||
|
expect(extrudable).toBeTruthy()
|
||||||
|
})
|
||||||
it('find sketch002 NOT pipe to be extruded', async () => {
|
it('find sketch002 NOT pipe to be extruded', async () => {
|
||||||
const exampleCode = `sketch001 = startSketchOn('XZ')
|
const exampleCode = `sketch001 = startSketchOn('XZ')
|
||||||
|> startProfileAt([3.29, 7.86], %)
|
|> startProfileAt([3.29, 7.86], %)
|
||||||
|
@ -975,7 +975,9 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
|||||||
if (
|
if (
|
||||||
node.type === 'CallExpression' &&
|
node.type === 'CallExpression' &&
|
||||||
node.callee.type === 'Identifier' &&
|
node.callee.type === 'Identifier' &&
|
||||||
(node.callee.name === 'extrude' || node.callee.name === 'revolve') &&
|
(node.callee.name === 'extrude' ||
|
||||||
|
node.callee.name === 'revolve' ||
|
||||||
|
node.callee.name === 'loft') &&
|
||||||
node.arguments?.[1]?.type === 'Identifier' &&
|
node.arguments?.[1]?.type === 'Identifier' &&
|
||||||
node.arguments[1].name === varDec.id.name
|
node.arguments[1].name === varDec.id.name
|
||||||
) {
|
) {
|
||||||
@ -988,7 +990,7 @@ export function hasSketchPipeBeenExtruded(selection: Selection, ast: Program) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** File must contain at least one sketch that has not been extruded already */
|
/** File must contain at least one sketch that has not been extruded already */
|
||||||
export function doesSceneHaveSweepableSketch(ast: Node<Program>) {
|
export function doesSceneHaveSweepableSketch(ast: Node<Program>, count = 1) {
|
||||||
const theMap: any = {}
|
const theMap: any = {}
|
||||||
traverse(ast as any, {
|
traverse(ast as any, {
|
||||||
enter(node) {
|
enter(node) {
|
||||||
@ -1037,7 +1039,7 @@ export function doesSceneHaveSweepableSketch(ast: Node<Program>) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
return Object.keys(theMap).length > 0
|
return Object.keys(theMap).length >= count
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getObjExprProperty(
|
export function getObjExprProperty(
|
||||||
|
@ -11,8 +11,8 @@ Map {
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
"range": [
|
"range": [
|
||||||
37,
|
12,
|
||||||
64,
|
31,
|
||||||
0,
|
0,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
@ -31,6 +31,9 @@ export type ModelingCommandSchema = {
|
|||||||
// result: (typeof EXTRUSION_RESULTS)[number]
|
// result: (typeof EXTRUSION_RESULTS)[number]
|
||||||
distance: KclCommandValue
|
distance: KclCommandValue
|
||||||
}
|
}
|
||||||
|
Loft: {
|
||||||
|
selection: Selections
|
||||||
|
}
|
||||||
Revolve: {
|
Revolve: {
|
||||||
selection: Selections
|
selection: Selections
|
||||||
angle: KclCommandValue
|
angle: KclCommandValue
|
||||||
@ -260,6 +263,20 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Loft: {
|
||||||
|
description: 'Create a 3D body by blending between two or more sketches',
|
||||||
|
icon: 'loft',
|
||||||
|
needsReview: true,
|
||||||
|
args: {
|
||||||
|
selection: {
|
||||||
|
inputType: 'selection',
|
||||||
|
selectionTypes: ['solid2D'],
|
||||||
|
multiple: true,
|
||||||
|
required: true,
|
||||||
|
skip: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
// TODO: Update this configuration, copied from extrude for MVP of revolve, specifically the args.selection
|
// TODO: Update this configuration, copied from extrude for MVP of revolve, specifically the args.selection
|
||||||
Revolve: {
|
Revolve: {
|
||||||
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
description: 'Create a 3D body by rotating a sketch region about an axis.',
|
||||||
|
@ -52,6 +52,7 @@ export const ONBOARDING_PROJECT_NAME = 'Tutorial Project $nn'
|
|||||||
export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
export const KCL_DEFAULT_CONSTANT_PREFIXES = {
|
||||||
SKETCH: 'sketch',
|
SKETCH: 'sketch',
|
||||||
EXTRUDE: 'extrude',
|
EXTRUDE: 'extrude',
|
||||||
|
LOFT: 'loft',
|
||||||
SEGMENT: 'seg',
|
SEGMENT: 'seg',
|
||||||
REVOLVE: 'revolve',
|
REVOLVE: 'revolve',
|
||||||
PLANE: 'plane',
|
PLANE: 'plane',
|
||||||
|
@ -529,6 +529,10 @@ function nodeHasExtrude(node: CommonASTNode) {
|
|||||||
doesPipeHaveCallExp({
|
doesPipeHaveCallExp({
|
||||||
calleeName: 'revolve',
|
calleeName: 'revolve',
|
||||||
...node,
|
...node,
|
||||||
|
}) ||
|
||||||
|
doesPipeHaveCallExp({
|
||||||
|
calleeName: 'loft',
|
||||||
|
...node,
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -559,6 +563,22 @@ export function canSweepSelection(selection: Selections) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function canLoftSelection(selection: Selections) {
|
||||||
|
const commonNodes = selection.graphSelections.map((_, i) =>
|
||||||
|
buildCommonNodeFromSelection(selection, i)
|
||||||
|
)
|
||||||
|
return (
|
||||||
|
!!isCursorInSketchCommandRange(
|
||||||
|
engineCommandManager.artifactGraph,
|
||||||
|
selection
|
||||||
|
) &&
|
||||||
|
commonNodes.length > 1 &&
|
||||||
|
commonNodes.every((n) => !hasSketchPipeBeenExtruded(n.selection, n.ast)) &&
|
||||||
|
commonNodes.every((n) => nodeHasClose(n) || nodeHasCircle(n)) &&
|
||||||
|
commonNodes.every((n) => !nodeHasExtrude(n))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// This accounts for non-geometry selections under "other"
|
// This accounts for non-geometry selections under "other"
|
||||||
export type ResolvedSelectionType = Artifact['type'] | 'other'
|
export type ResolvedSelectionType = Artifact['type'] | 'other'
|
||||||
export type SelectionCountsByType = Map<ResolvedSelectionType, number>
|
export type SelectionCountsByType = Map<ResolvedSelectionType, number>
|
||||||
|
@ -139,9 +139,14 @@ export const toolbarConfig: Record<ToolbarModeName, ToolbarMode> = {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'loft',
|
id: 'loft',
|
||||||
onClick: () => console.error('Loft not yet implemented'),
|
onClick: ({ commandBarSend }) =>
|
||||||
|
commandBarSend({
|
||||||
|
type: 'Find and select command',
|
||||||
|
data: { name: 'Loft', groupId: 'modeling' },
|
||||||
|
}),
|
||||||
|
disabled: (state) => !state.can({ type: 'Loft' }),
|
||||||
icon: 'loft',
|
icon: 'loft',
|
||||||
status: 'kcl-only',
|
status: 'available',
|
||||||
title: 'Loft',
|
title: 'Loft',
|
||||||
hotkey: 'L',
|
hotkey: 'L',
|
||||||
description:
|
description:
|
||||||
|
@ -44,13 +44,14 @@ import {
|
|||||||
addOffsetPlane,
|
addOffsetPlane,
|
||||||
deleteFromSelection,
|
deleteFromSelection,
|
||||||
extrudeSketch,
|
extrudeSketch,
|
||||||
|
loftSketches,
|
||||||
revolveSketch,
|
revolveSketch,
|
||||||
} from 'lang/modifyAst'
|
} from 'lang/modifyAst'
|
||||||
import {
|
import {
|
||||||
applyEdgeTreatmentToSelection,
|
applyEdgeTreatmentToSelection,
|
||||||
EdgeTreatmentType,
|
EdgeTreatmentType,
|
||||||
FilletParameters,
|
FilletParameters,
|
||||||
} from 'lang/modifyAst/addFillet'
|
} from 'lang/modifyAst/addEdgeTreatment'
|
||||||
import { getNodeFromPath } from '../lang/queryAst'
|
import { getNodeFromPath } from '../lang/queryAst'
|
||||||
import {
|
import {
|
||||||
applyConstraintEqualAngle,
|
applyConstraintEqualAngle,
|
||||||
@ -256,6 +257,7 @@ export type ModelingMachineEvent =
|
|||||||
| { type: 'Export'; data: ModelingCommandSchema['Export'] }
|
| { type: 'Export'; data: ModelingCommandSchema['Export'] }
|
||||||
| { type: 'Make'; data: ModelingCommandSchema['Make'] }
|
| { type: 'Make'; data: ModelingCommandSchema['Make'] }
|
||||||
| { type: 'Extrude'; data?: ModelingCommandSchema['Extrude'] }
|
| { type: 'Extrude'; data?: ModelingCommandSchema['Extrude'] }
|
||||||
|
| { type: 'Loft'; data?: ModelingCommandSchema['Loft'] }
|
||||||
| { type: 'Revolve'; data?: ModelingCommandSchema['Revolve'] }
|
| { type: 'Revolve'; data?: ModelingCommandSchema['Revolve'] }
|
||||||
| { type: 'Fillet'; data?: ModelingCommandSchema['Fillet'] }
|
| { type: 'Fillet'; data?: ModelingCommandSchema['Fillet'] }
|
||||||
| { type: 'Offset plane'; data: ModelingCommandSchema['Offset plane'] }
|
| { type: 'Offset plane'; data: ModelingCommandSchema['Offset plane'] }
|
||||||
@ -387,6 +389,7 @@ export const modelingMachine = setup({
|
|||||||
guards: {
|
guards: {
|
||||||
'Selection is on face': () => false,
|
'Selection is on face': () => false,
|
||||||
'has valid sweep selection': () => false,
|
'has valid sweep selection': () => false,
|
||||||
|
'has valid loft selection': () => false,
|
||||||
'has valid edge treatment selection': () => false,
|
'has valid edge treatment selection': () => false,
|
||||||
'Has exportable geometry': () => false,
|
'Has exportable geometry': () => false,
|
||||||
'has valid selection for deletion': () => false,
|
'has valid selection for deletion': () => false,
|
||||||
@ -1529,6 +1532,50 @@ export const modelingMachine = setup({
|
|||||||
updateAstResult.newAst
|
updateAstResult.newAst
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (updateAstResult?.selections) {
|
||||||
|
editorManager.selectRange(updateAstResult?.selections)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
loftAstMod: fromPromise(
|
||||||
|
async ({
|
||||||
|
input,
|
||||||
|
}: {
|
||||||
|
input: ModelingCommandSchema['Loft'] | undefined
|
||||||
|
}) => {
|
||||||
|
if (!input) return new Error('No input provided')
|
||||||
|
// Extract inputs
|
||||||
|
const ast = kclManager.ast
|
||||||
|
const { selection } = input
|
||||||
|
const declarators = selection.graphSelections.flatMap((s) => {
|
||||||
|
const path = getNodePathFromSourceRange(ast, s?.codeRef.range)
|
||||||
|
const nodeFromPath = getNodeFromPath<VariableDeclarator>(
|
||||||
|
ast,
|
||||||
|
path,
|
||||||
|
'VariableDeclarator'
|
||||||
|
)
|
||||||
|
return err(nodeFromPath) ? [] : nodeFromPath.node
|
||||||
|
})
|
||||||
|
|
||||||
|
// TODO: add better validation on selection
|
||||||
|
if (!(declarators && declarators.length > 1)) {
|
||||||
|
trap('Not enough sketches selected')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Perform the loft
|
||||||
|
const loftSketchesRes = loftSketches(ast, declarators)
|
||||||
|
const updateAstResult = await kclManager.updateAst(
|
||||||
|
loftSketchesRes.modifiedAst,
|
||||||
|
true,
|
||||||
|
{
|
||||||
|
focusPath: [loftSketchesRes.pathToNode],
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
await codeManager.updateEditorWithAstAndWriteToFile(
|
||||||
|
updateAstResult.newAst
|
||||||
|
)
|
||||||
|
|
||||||
if (updateAstResult?.selections) {
|
if (updateAstResult?.selections) {
|
||||||
editorManager.selectRange(updateAstResult?.selections)
|
editorManager.selectRange(updateAstResult?.selections)
|
||||||
}
|
}
|
||||||
@ -1570,6 +1617,11 @@ export const modelingMachine = setup({
|
|||||||
reenter: false,
|
reenter: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
Loft: {
|
||||||
|
target: 'Applying loft',
|
||||||
|
reenter: true,
|
||||||
|
},
|
||||||
|
|
||||||
Fillet: {
|
Fillet: {
|
||||||
target: 'idle',
|
target: 'idle',
|
||||||
guard: 'has valid edge treatment selection',
|
guard: 'has valid edge treatment selection',
|
||||||
@ -2318,6 +2370,19 @@ export const modelingMachine = setup({
|
|||||||
onError: ['idle'],
|
onError: ['idle'],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'Applying loft': {
|
||||||
|
invoke: {
|
||||||
|
src: 'loftAstMod',
|
||||||
|
id: 'loftAstMod',
|
||||||
|
input: ({ event }) => {
|
||||||
|
if (event.type !== 'Loft') return undefined
|
||||||
|
return event.data
|
||||||
|
},
|
||||||
|
onDone: ['idle'],
|
||||||
|
onError: ['idle'],
|
||||||
|
},
|
||||||
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
initial: 'idle',
|
initial: 'idle',
|
||||||
|
31
src/wasm-lib/Cargo.lock
generated
@ -1182,9 +1182,9 @@ checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.0"
|
version = "0.15.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1e087f84d4f86bf4b218b927129862374b72199ae7d8657835f1e89000eea4fb"
|
checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "heck"
|
name = "heck"
|
||||||
@ -1589,7 +1589,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"equivalent",
|
"equivalent",
|
||||||
"hashbrown 0.15.0",
|
"hashbrown 0.15.2",
|
||||||
"serde",
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1706,7 +1706,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
version = "0.2.26"
|
version = "0.2.28"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"approx 0.5.1",
|
"approx 0.5.1",
|
||||||
@ -1800,9 +1800,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "kittycad"
|
name = "kittycad"
|
||||||
version = "0.3.25"
|
version = "0.3.28"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f6359cc0a1bbccbcf78775eea17a033cf2aa89d3fe6a9784f8ce94e5f882c185"
|
checksum = "933cb5f77624386c87d296e3fd493daf50156d1cbfa03b9f333a6d4da2896369"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -1831,7 +1831,7 @@ dependencies = [
|
|||||||
"serde_bytes",
|
"serde_bytes",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_urlencoded",
|
"serde_urlencoded",
|
||||||
"thiserror 1.0.68",
|
"thiserror 2.0.0",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
@ -2921,9 +2921,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest-conditional-middleware"
|
name = "reqwest-conditional-middleware"
|
||||||
version = "0.3.0"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "b1663d9d4fbb6e3900f91455d6d7833301c91ae3c7fc6e116fd7acd40e478a93"
|
checksum = "f67ad7fdf5c0a015763fcd164bee294b13fb7b6f89f1b55961d40f00c3e32d6b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"http 1.1.0",
|
"http 1.1.0",
|
||||||
@ -2933,9 +2933,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest-middleware"
|
name = "reqwest-middleware"
|
||||||
version = "0.3.3"
|
version = "0.4.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "562ceb5a604d3f7c885a792d42c199fd8af239d0a51b2fa6a78aafa092452b04"
|
checksum = "d1ccd3b55e711f91a9885a2fa6fbbb2e39db1776420b062efc058c6410f7e5e3"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -2948,9 +2948,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest-retry"
|
name = "reqwest-retry"
|
||||||
version = "0.6.1"
|
version = "0.7.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a83df1aaec00176d0fabb65dea13f832d2a446ca99107afc17c5d2d4981221d0"
|
checksum = "29c73e4195a6bfbcb174b790d9b3407ab90646976c55de58a6515da25d851178"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
@ -2962,6 +2962,7 @@ dependencies = [
|
|||||||
"reqwest",
|
"reqwest",
|
||||||
"reqwest-middleware",
|
"reqwest-middleware",
|
||||||
"retry-policies",
|
"retry-policies",
|
||||||
|
"thiserror 1.0.68",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"wasm-timer",
|
"wasm-timer",
|
||||||
@ -2969,9 +2970,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "reqwest-tracing"
|
name = "reqwest-tracing"
|
||||||
version = "0.5.3"
|
version = "0.5.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bfdd9bfa64c72233d8dd99ab7883efcdefe9e16d46488ecb9228b71a2e2ceb45"
|
checksum = "ff82cf5730a1311fb9413b0bc2b8e743e0157cd73f010ab4ec374a923873b6a2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-trait",
|
"async-trait",
|
||||||
|
@ -74,8 +74,8 @@ members = [
|
|||||||
|
|
||||||
[workspace.dependencies]
|
[workspace.dependencies]
|
||||||
http = "1"
|
http = "1"
|
||||||
kittycad = { version = "0.3.25", default-features = false, features = ["js", "requests"] }
|
kittycad = { version = "0.3.28", default-features = false, features = ["js", "requests"] }
|
||||||
kittycad-modeling-cmds = { version = "0.2.76", features = ["websocket"] }
|
kittycad-modeling-cmds = { version = "0.2.77", features = ["websocket"] }
|
||||||
|
|
||||||
[[test]]
|
[[test]]
|
||||||
name = "executor"
|
name = "executor"
|
||||||
|
@ -171,7 +171,7 @@ fn do_stdlib_inner(
|
|||||||
code_blocks.iter().map(|cb| {
|
code_blocks.iter().map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
|
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
}).collect::<Vec<String>>()
|
}).collect::<Vec<String>>()
|
||||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for SomeFn {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -150,7 +150,7 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -150,7 +150,7 @@ impl crate::docs::StdLibFn for MyFunc {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -158,7 +158,7 @@ impl crate::docs::StdLibFn for LineTo {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -150,7 +150,7 @@ impl crate::docs::StdLibFn for Min {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Import {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Import {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Import {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -113,7 +113,7 @@ impl crate::docs::StdLibFn for Show {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -108,7 +108,7 @@ impl crate::docs::StdLibFn for SomeFunction {
|
|||||||
.iter()
|
.iter()
|
||||||
.map(|cb| {
|
.map(|cb| {
|
||||||
let program = crate::Program::parse(cb).unwrap();
|
let program = crate::Program::parse(cb).unwrap();
|
||||||
let mut options: crate::ast::types::FormatOptions = Default::default();
|
let mut options: crate::parsing::ast::types::FormatOptions = Default::default();
|
||||||
options.insert_final_newline = false;
|
options.insert_final_newline = false;
|
||||||
program.ast.recast(&options, 0)
|
program.ast.recast(&options, 0)
|
||||||
})
|
})
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "kcl-lib"
|
name = "kcl-lib"
|
||||||
description = "KittyCAD Language implementation and tools"
|
description = "KittyCAD Language implementation and tools"
|
||||||
version = "0.2.26"
|
version = "0.2.28"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
license = "MIT"
|
license = "MIT"
|
||||||
repository = "https://github.com/KittyCAD/modeling-app"
|
repository = "https://github.com/KittyCAD/modeling-app"
|
||||||
@ -16,7 +16,10 @@ async-recursion = "1.1.1"
|
|||||||
async-trait = "0.1.83"
|
async-trait = "0.1.83"
|
||||||
base64 = "0.22.1"
|
base64 = "0.22.1"
|
||||||
chrono = "0.4.38"
|
chrono = "0.4.38"
|
||||||
clap = { version = "4.5.21", default-features = false, optional = true, features = ["std", "derive"] }
|
clap = { version = "4.5.21", default-features = false, optional = true, features = [
|
||||||
|
"std",
|
||||||
|
"derive",
|
||||||
|
] }
|
||||||
convert_case = "0.6.0"
|
convert_case = "0.6.0"
|
||||||
dashmap = "6.1.0"
|
dashmap = "6.1.0"
|
||||||
databake = { version = "0.1.8", features = ["derive"] }
|
databake = { version = "0.1.8", features = ["derive"] }
|
||||||
@ -38,16 +41,32 @@ miette = "7.2.0"
|
|||||||
mime_guess = "2.0.5"
|
mime_guess = "2.0.5"
|
||||||
parse-display = "0.9.1"
|
parse-display = "0.9.1"
|
||||||
pyo3 = { version = "0.22.6", optional = true }
|
pyo3 = { version = "0.22.6", optional = true }
|
||||||
reqwest = { version = "0.12", default-features = false, features = ["stream", "rustls-tls"] }
|
reqwest = { version = "0.12", default-features = false, features = [
|
||||||
|
"stream",
|
||||||
|
"rustls-tls",
|
||||||
|
] }
|
||||||
ropey = "1.6.1"
|
ropey = "1.6.1"
|
||||||
schemars = { version = "0.8.17", features = ["impl_json_schema", "indexmap2", "url", "uuid1", "preserve_order"] }
|
schemars = { version = "0.8.17", features = [
|
||||||
|
"impl_json_schema",
|
||||||
|
"indexmap2",
|
||||||
|
"url",
|
||||||
|
"uuid1",
|
||||||
|
"preserve_order",
|
||||||
|
] }
|
||||||
serde = { version = "1.0.214", features = ["derive"] }
|
serde = { version = "1.0.214", features = ["derive"] }
|
||||||
serde_json = "1.0.128"
|
serde_json = "1.0.128"
|
||||||
sha2 = "0.10.8"
|
sha2 = "0.10.8"
|
||||||
tabled = { version = "0.15.0", optional = true }
|
tabled = { version = "0.15.0", optional = true }
|
||||||
thiserror = "2.0.0"
|
thiserror = "2.0.0"
|
||||||
toml = "0.8.19"
|
toml = "0.8.19"
|
||||||
ts-rs = { version = "10.0.0", features = ["uuid-impl", "url-impl", "chrono-impl", "indexmap-impl", "no-serde-warnings", "serde-json-impl"] }
|
ts-rs = { version = "10.0.0", features = [
|
||||||
|
"uuid-impl",
|
||||||
|
"url-impl",
|
||||||
|
"chrono-impl",
|
||||||
|
"indexmap-impl",
|
||||||
|
"no-serde-warnings",
|
||||||
|
"serde-json-impl",
|
||||||
|
] }
|
||||||
url = { version = "2.5.3", features = ["serde"] }
|
url = { version = "2.5.3", features = ["serde"] }
|
||||||
urlencoding = "2.1.3"
|
urlencoding = "2.1.3"
|
||||||
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
|
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
|
||||||
@ -59,7 +78,9 @@ zip = { version = "2.0.0", default-features = false }
|
|||||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||||
js-sys = { version = "0.3.72" }
|
js-sys = { version = "0.3.72" }
|
||||||
tokio = { version = "1.41.1", features = ["sync", "time"] }
|
tokio = { version = "1.41.1", features = ["sync", "time"] }
|
||||||
tower-lsp = { version = "0.20.0", default-features = false, features = ["runtime-agnostic"] }
|
tower-lsp = { version = "0.20.0", default-features = false, features = [
|
||||||
|
"runtime-agnostic",
|
||||||
|
] }
|
||||||
wasm-bindgen = "0.2.91"
|
wasm-bindgen = "0.2.91"
|
||||||
wasm-bindgen-futures = "0.4.44"
|
wasm-bindgen-futures = "0.4.44"
|
||||||
web-sys = { version = "0.3.72", features = ["console"] }
|
web-sys = { version = "0.3.72", features = ["console"] }
|
||||||
@ -68,7 +89,9 @@ web-sys = { version = "0.3.72", features = ["console"] }
|
|||||||
approx = "0.5"
|
approx = "0.5"
|
||||||
bson = { version = "2.13.0", features = ["uuid-1", "chrono"] }
|
bson = { version = "2.13.0", features = ["uuid-1", "chrono"] }
|
||||||
tokio = { version = "1.41.1", features = ["full"] }
|
tokio = { version = "1.41.1", features = ["full"] }
|
||||||
tokio-tungstenite = { version = "0.24.0", features = ["rustls-tls-native-roots"] }
|
tokio-tungstenite = { version = "0.24.0", features = [
|
||||||
|
"rustls-tls-native-roots",
|
||||||
|
] }
|
||||||
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
tower-lsp = { version = "0.20.0", features = ["proposed"] }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
|
@ -1,2 +0,0 @@
|
|||||||
pub mod modify;
|
|
||||||
pub mod types;
|
|
@ -797,7 +797,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_serialize_function() {
|
fn test_serialize_function() {
|
||||||
let some_function = crate::ast::types::Function::StdLib {
|
let some_function = crate::parsing::ast::types::Function::StdLib {
|
||||||
func: Box::new(crate::std::sketch::Line),
|
func: Box::new(crate::std::sketch::Line),
|
||||||
};
|
};
|
||||||
let serialized = serde_json::to_string(&some_function).unwrap();
|
let serialized = serde_json::to_string(&some_function).unwrap();
|
||||||
@ -807,11 +807,11 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_deserialize_function() {
|
fn test_deserialize_function() {
|
||||||
let some_function_string = r#"{"type":"StdLib","func":{"name":"line","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{},"schemaDefinitions":{}},"args":[],"unpublished":false,"deprecated":false, "examples": []}}"#;
|
let some_function_string = r#"{"type":"StdLib","func":{"name":"line","summary":"","description":"","tags":[],"returnValue":{"type":"","required":false,"name":"","schema":{},"schemaDefinitions":{}},"args":[],"unpublished":false,"deprecated":false, "examples": []}}"#;
|
||||||
let some_function: crate::ast::types::Function = serde_json::from_str(some_function_string).unwrap();
|
let some_function: crate::parsing::ast::types::Function = serde_json::from_str(some_function_string).unwrap();
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
some_function,
|
some_function,
|
||||||
crate::ast::types::Function::StdLib {
|
crate::parsing::ast::types::Function::StdLib {
|
||||||
func: Box::new(crate::std::sketch::Line)
|
func: Box::new(crate::std::sketch::Line)
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
@ -25,10 +25,12 @@ type Point3D = kcmc::shared::Point3d<f64>;
|
|||||||
|
|
||||||
pub use crate::kcl_value::KclValue;
|
pub use crate::kcl_value::KclValue;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, Node, NodeRef, TagDeclarator, TagNode},
|
|
||||||
engine::{EngineManager, ExecutionKind},
|
engine::{EngineManager, ExecutionKind},
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
fs::{FileManager, FileSystem},
|
fs::{FileManager, FileSystem},
|
||||||
|
parsing::ast::types::{
|
||||||
|
BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, Node, NodeRef, TagDeclarator, TagNode,
|
||||||
|
},
|
||||||
settings::types::UnitLength,
|
settings::types::UnitLength,
|
||||||
source_range::{ModuleId, SourceRange},
|
source_range::{ModuleId, SourceRange},
|
||||||
std::{args::Arg, StdLib},
|
std::{args::Arg, StdLib},
|
||||||
@ -733,7 +735,7 @@ pub type MemoryFunction =
|
|||||||
fn(
|
fn(
|
||||||
s: Vec<Arg>,
|
s: Vec<Arg>,
|
||||||
memory: ProgramMemory,
|
memory: ProgramMemory,
|
||||||
expression: crate::ast::types::BoxNode<FunctionExpression>,
|
expression: crate::parsing::ast::types::BoxNode<FunctionExpression>,
|
||||||
metadata: Vec<Metadata>,
|
metadata: Vec<Metadata>,
|
||||||
exec_state: &ExecState,
|
exec_state: &ExecState,
|
||||||
ctx: ExecutorContext,
|
ctx: ExecutorContext,
|
||||||
@ -1842,7 +1844,7 @@ impl ExecutorContext {
|
|||||||
#[async_recursion]
|
#[async_recursion]
|
||||||
pub(crate) async fn inner_execute<'a>(
|
pub(crate) async fn inner_execute<'a>(
|
||||||
&'a self,
|
&'a self,
|
||||||
program: NodeRef<'a, crate::ast::types::Program>,
|
program: NodeRef<'a, crate::parsing::ast::types::Program>,
|
||||||
exec_state: &mut ExecState,
|
exec_state: &mut ExecState,
|
||||||
body_type: BodyType,
|
body_type: BodyType,
|
||||||
) -> Result<Option<KclValue>, KclError> {
|
) -> Result<Option<KclValue>, KclError> {
|
||||||
@ -1883,7 +1885,7 @@ impl ExecutorContext {
|
|||||||
let module_id = exec_state.add_module(resolved_path.clone());
|
let module_id = exec_state.add_module(resolved_path.clone());
|
||||||
let source = self.fs.read_to_string(&resolved_path, source_range).await?;
|
let source = self.fs.read_to_string(&resolved_path, source_range).await?;
|
||||||
// TODO handle parsing errors properly
|
// TODO handle parsing errors properly
|
||||||
let program = crate::parser::parse_str(&source, module_id).parse_errs_as_err()?;
|
let program = crate::parsing::parse_str(&source, module_id).parse_errs_as_err()?;
|
||||||
let (module_memory, module_exports) = {
|
let (module_memory, module_exports) = {
|
||||||
exec_state.import_stack.push(resolved_path.clone());
|
exec_state.import_stack.push(resolved_path.clone());
|
||||||
let original_execution = self.engine.replace_execution_kind(ExecutionKind::Isolated);
|
let original_execution = self.engine.replace_execution_kind(ExecutionKind::Isolated);
|
||||||
@ -2239,7 +2241,7 @@ mod tests {
|
|||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ast::types::{Identifier, Node, Parameter};
|
use crate::parsing::ast::types::{Identifier, Node, Parameter};
|
||||||
|
|
||||||
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
|
pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
|
||||||
let program = Program::parse(code)?;
|
let program = Program::parse(code)?;
|
||||||
@ -3088,7 +3090,7 @@ let w = f() + f()
|
|||||||
let func_expr = &Node::no_src(FunctionExpression {
|
let func_expr = &Node::no_src(FunctionExpression {
|
||||||
params,
|
params,
|
||||||
body: Node {
|
body: Node {
|
||||||
inner: crate::ast::types::Program {
|
inner: crate::parsing::ast::types::Program {
|
||||||
body: Vec::new(),
|
body: Vec::new(),
|
||||||
non_code_meta: Default::default(),
|
non_code_meta: Default::default(),
|
||||||
shebang: None,
|
shebang: None,
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::FunctionExpression,
|
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
executor::{
|
executor::{
|
||||||
call_user_defined_function, ExecState, ExecutorContext, KclValue, MemoryFunction, Metadata, ProgramMemory,
|
call_user_defined_function, ExecState, ExecutorContext, KclValue, MemoryFunction, Metadata, ProgramMemory,
|
||||||
},
|
},
|
||||||
|
parsing::ast::types::FunctionExpression,
|
||||||
std::args::Arg,
|
std::args::Arg,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ use crate::{
|
|||||||
pub struct FunctionParam<'a> {
|
pub struct FunctionParam<'a> {
|
||||||
pub inner: Option<&'a MemoryFunction>,
|
pub inner: Option<&'a MemoryFunction>,
|
||||||
pub memory: ProgramMemory,
|
pub memory: ProgramMemory,
|
||||||
pub fn_expr: crate::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,
|
||||||
}
|
}
|
||||||
|
@ -5,10 +5,10 @@ use schemars::JsonSchema;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{FunctionExpression, KclNone, TagDeclarator, TagNode},
|
|
||||||
errors::KclErrorDetails,
|
errors::KclErrorDetails,
|
||||||
exec::{ProgramMemory, Sketch},
|
exec::{ProgramMemory, Sketch},
|
||||||
executor::{Face, ImportedGeometry, MemoryFunction, Metadata, Plane, SketchSet, Solid, SolidSet, TagIdentifier},
|
executor::{Face, ImportedGeometry, MemoryFunction, Metadata, Plane, SketchSet, Solid, SolidSet, TagIdentifier},
|
||||||
|
parsing::ast::types::{FunctionExpression, KclNone, TagDeclarator, TagNode},
|
||||||
std::{args::Arg, FnAsArg},
|
std::{args::Arg, FnAsArg},
|
||||||
ExecState, ExecutorContext, KclError, SourceRange,
|
ExecState, ExecutorContext, KclError, SourceRange,
|
||||||
};
|
};
|
||||||
@ -56,7 +56,7 @@ pub enum KclValue {
|
|||||||
meta: Vec<Metadata>,
|
meta: Vec<Metadata>,
|
||||||
},
|
},
|
||||||
TagIdentifier(Box<TagIdentifier>),
|
TagIdentifier(Box<TagIdentifier>),
|
||||||
TagDeclarator(crate::ast::types::BoxNode<TagDeclarator>),
|
TagDeclarator(crate::parsing::ast::types::BoxNode<TagDeclarator>),
|
||||||
Plane(Box<Plane>),
|
Plane(Box<Plane>),
|
||||||
Face(Box<Face>),
|
Face(Box<Face>),
|
||||||
Sketch {
|
Sketch {
|
||||||
@ -75,7 +75,7 @@ pub enum KclValue {
|
|||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
func: Option<MemoryFunction>,
|
func: Option<MemoryFunction>,
|
||||||
#[schemars(skip)]
|
#[schemars(skip)]
|
||||||
expression: crate::ast::types::BoxNode<FunctionExpression>,
|
expression: crate::parsing::ast::types::BoxNode<FunctionExpression>,
|
||||||
memory: Box<ProgramMemory>,
|
memory: Box<ProgramMemory>,
|
||||||
#[serde(rename = "__meta")]
|
#[serde(rename = "__meta")]
|
||||||
meta: Vec<Metadata>,
|
meta: Vec<Metadata>,
|
||||||
|
@ -56,7 +56,6 @@ macro_rules! eprint {
|
|||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static ALLOC: dhat::Alloc = dhat::Alloc;
|
static ALLOC: dhat::Alloc = dhat::Alloc;
|
||||||
|
|
||||||
mod ast;
|
|
||||||
mod coredump;
|
mod coredump;
|
||||||
mod docs;
|
mod docs;
|
||||||
mod engine;
|
mod engine;
|
||||||
@ -68,7 +67,7 @@ mod kcl_value;
|
|||||||
pub mod lint;
|
pub mod lint;
|
||||||
mod log;
|
mod log;
|
||||||
mod lsp;
|
mod lsp;
|
||||||
mod parser;
|
mod parsing;
|
||||||
mod settings;
|
mod settings;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod simulation_tests;
|
mod simulation_tests;
|
||||||
@ -77,13 +76,11 @@ mod std;
|
|||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
pub mod test_server;
|
pub mod test_server;
|
||||||
mod thread;
|
mod thread;
|
||||||
mod token;
|
|
||||||
mod unparser;
|
mod unparser;
|
||||||
mod walk;
|
mod walk;
|
||||||
#[cfg(target_arch = "wasm32")]
|
#[cfg(target_arch = "wasm32")]
|
||||||
mod wasm;
|
mod wasm;
|
||||||
|
|
||||||
pub use ast::{modify::modify_ast_for_sketch, types::FormatOptions};
|
|
||||||
pub use coredump::CoreDump;
|
pub use coredump::CoreDump;
|
||||||
pub use engine::{EngineManager, ExecutionKind};
|
pub use engine::{EngineManager, ExecutionKind};
|
||||||
pub use errors::{ConnectionError, ExecError, KclError};
|
pub use errors::{ConnectionError, ExecError, KclError};
|
||||||
@ -92,6 +89,7 @@ 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},
|
||||||
};
|
};
|
||||||
|
pub use parsing::ast::{modify::modify_ast_for_sketch, types::FormatOptions};
|
||||||
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
|
pub use settings::types::{project::ProjectConfiguration, Configuration, UnitLength};
|
||||||
pub use source_range::{ModuleId, SourceRange};
|
pub use source_range::{ModuleId, SourceRange};
|
||||||
|
|
||||||
@ -127,7 +125,7 @@ use crate::log::{log, logln};
|
|||||||
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)]
|
||||||
pub struct Program {
|
pub struct Program {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
ast: ast::types::Node<ast::types::Program>,
|
ast: parsing::ast::types::Node<parsing::ast::types::Program>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(any(test, feature = "lsp-test-util"))]
|
#[cfg(any(test, feature = "lsp-test-util"))]
|
||||||
@ -138,14 +136,14 @@ pub use lsp::test_util::kcl_lsp_server;
|
|||||||
impl Program {
|
impl Program {
|
||||||
pub fn parse(input: &str) -> Result<Program, KclError> {
|
pub fn parse(input: &str) -> Result<Program, KclError> {
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let tokens = token::lexer(input, module_id)?;
|
let tokens = parsing::token::lexer(input, module_id)?;
|
||||||
// TODO handle parsing errors properly
|
// TODO handle parsing errors properly
|
||||||
let ast = parser::parse_tokens(tokens).parse_errs_as_err()?;
|
let ast = parsing::parse_tokens(tokens).parse_errs_as_err()?;
|
||||||
|
|
||||||
Ok(Program { ast })
|
Ok(Program { ast })
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn compute_digest(&mut self) -> ast::types::digest::Digest {
|
pub fn compute_digest(&mut self) -> parsing::ast::digest::Digest {
|
||||||
self.ast.compute_digest()
|
self.ast.compute_digest()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,8 +165,8 @@ impl Program {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<ast::types::Node<ast::types::Program>> for Program {
|
impl From<parsing::ast::types::Node<parsing::ast::types::Program>> for Program {
|
||||||
fn from(ast: ast::types::Node<ast::types::Program>) -> Program {
|
fn from(ast: parsing::ast::types::Node<parsing::ast::types::Program>) -> Program {
|
||||||
Self { ast }
|
Self { ast }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,8 +2,8 @@ use anyhow::Result;
|
|||||||
use convert_case::Casing;
|
use convert_case::Casing;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{ObjectProperty, VariableDeclarator},
|
|
||||||
lint::rule::{def_finding, Discovered, Finding},
|
lint::rule::{def_finding, Discovered, Finding},
|
||||||
|
parsing::ast::types::{ObjectProperty, VariableDeclarator},
|
||||||
walk::Node,
|
walk::Node,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
@ -3,8 +3,8 @@ use std::collections::HashMap;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{BinaryPart, Expr, LiteralValue, ObjectExpression, UnaryOperator},
|
|
||||||
lint::rule::{def_finding, Discovered, Finding},
|
lint::rule::{def_finding, Discovered, Finding},
|
||||||
|
parsing::ast::types::{BinaryPart, Expr, LiteralValue, ObjectExpression, UnaryOperator},
|
||||||
walk::Node,
|
walk::Node,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
@ -3,9 +3,9 @@ use std::sync::Arc;
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{CallExpression, NodeRef},
|
|
||||||
docs::StdLibFn,
|
docs::StdLibFn,
|
||||||
lint::rule::{def_finding, Discovered, Finding},
|
lint::rule::{def_finding, Discovered, Finding},
|
||||||
|
parsing::ast::types::{CallExpression, NodeRef},
|
||||||
std::{FunctionKind, StdLib},
|
std::{FunctionKind, StdLib},
|
||||||
walk::Node,
|
walk::Node,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
|
@ -30,16 +30,16 @@ where
|
|||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
pub struct Discovered {
|
pub struct Discovered {
|
||||||
/// Zoo Lint Finding information.
|
/// Zoo Lint Finding information.
|
||||||
pub(super) finding: Finding,
|
pub finding: Finding,
|
||||||
|
|
||||||
/// Further information about the specific finding.
|
/// Further information about the specific finding.
|
||||||
pub(super) description: String,
|
pub description: String,
|
||||||
|
|
||||||
/// Source code location.
|
/// Source code location.
|
||||||
pub(super) pos: SourceRange,
|
pub pos: SourceRange,
|
||||||
|
|
||||||
/// Is this discovered issue overridden by the programmer?
|
/// Is this discovered issue overridden by the programmer?
|
||||||
pub(super) overridden: bool,
|
pub overridden: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "pyo3")]
|
#[cfg(feature = "pyo3")]
|
||||||
@ -182,7 +182,7 @@ mod test {
|
|||||||
|
|
||||||
macro_rules! assert_no_finding {
|
macro_rules! assert_no_finding {
|
||||||
( $check:expr, $finding:expr, $kcl:expr ) => {
|
( $check:expr, $finding:expr, $kcl:expr ) => {
|
||||||
let prog = $crate::parser::top_level_parse($kcl).unwrap();
|
let prog = $crate::parsing::top_level_parse($kcl).unwrap();
|
||||||
for discovered_finding in prog.lint($check).unwrap() {
|
for discovered_finding in prog.lint($check).unwrap() {
|
||||||
if discovered_finding.finding == $finding {
|
if discovered_finding.finding == $finding {
|
||||||
assert!(false, "Finding {:?} was emitted", $finding.code);
|
assert!(false, "Finding {:?} was emitted", $finding.code);
|
||||||
@ -193,7 +193,7 @@ mod test {
|
|||||||
|
|
||||||
macro_rules! assert_finding {
|
macro_rules! assert_finding {
|
||||||
( $check:expr, $finding:expr, $kcl:expr ) => {
|
( $check:expr, $finding:expr, $kcl:expr ) => {
|
||||||
let prog = $crate::parser::top_level_parse($kcl).unwrap();
|
let prog = $crate::parsing::top_level_parse($kcl).unwrap();
|
||||||
|
|
||||||
for discovered_finding in prog.lint($check).unwrap() {
|
for discovered_finding in prog.lint($check).unwrap() {
|
||||||
if discovered_finding.finding == $finding {
|
if discovered_finding.finding == $finding {
|
||||||
|
@ -3,14 +3,14 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use tower_lsp::lsp_types::notification::Notification;
|
use tower_lsp::lsp_types::notification::Notification;
|
||||||
|
|
||||||
use crate::{ast::types::Node, settings::types::UnitLength};
|
use crate::{parsing::ast::types::Node, settings::types::UnitLength};
|
||||||
|
|
||||||
/// A notification that the AST has changed.
|
/// A notification that the AST has changed.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum AstUpdated {}
|
pub enum AstUpdated {}
|
||||||
|
|
||||||
impl Notification for AstUpdated {
|
impl Notification for AstUpdated {
|
||||||
type Params = Node<crate::ast::types::Program>;
|
type Params = Node<crate::parsing::ast::types::Program>;
|
||||||
const METHOD: &'static str = "kcl/astUpdated";
|
const METHOD: &'static str = "kcl/astUpdated";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,10 +41,12 @@ use tower_lsp::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{Expr, Node, VariableKind},
|
|
||||||
lsp::{backend::Backend as _, util::IntoDiagnostic},
|
lsp::{backend::Backend as _, util::IntoDiagnostic},
|
||||||
parser::PIPE_OPERATOR,
|
parsing::{
|
||||||
|
ast::types::{Expr, Node, VariableKind},
|
||||||
token::TokenType,
|
token::TokenType,
|
||||||
|
PIPE_OPERATOR,
|
||||||
|
},
|
||||||
ExecState, ModuleId, Program, SourceRange,
|
ExecState, ModuleId, Program, SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -98,9 +100,9 @@ pub struct Backend {
|
|||||||
/// The stdlib signatures for the language.
|
/// The stdlib signatures for the language.
|
||||||
pub stdlib_signatures: HashMap<String, SignatureHelp>,
|
pub stdlib_signatures: HashMap<String, SignatureHelp>,
|
||||||
/// Token maps.
|
/// Token maps.
|
||||||
pub token_map: DashMap<String, Vec<crate::token::Token>>,
|
pub token_map: DashMap<String, Vec<crate::parsing::token::Token>>,
|
||||||
/// AST maps.
|
/// AST maps.
|
||||||
pub ast_map: DashMap<String, Node<crate::ast::types::Program>>,
|
pub ast_map: DashMap<String, Node<crate::parsing::ast::types::Program>>,
|
||||||
/// Memory maps.
|
/// Memory maps.
|
||||||
pub memory_map: DashMap<String, crate::executor::ProgramMemory>,
|
pub memory_map: DashMap<String, crate::executor::ProgramMemory>,
|
||||||
/// Current code.
|
/// Current code.
|
||||||
@ -257,7 +259,7 @@ impl crate::lsp::backend::Backend for Backend {
|
|||||||
|
|
||||||
// Lets update the tokens.
|
// Lets update the tokens.
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let tokens = match crate::token::lexer(¶ms.text, module_id) {
|
let tokens = match crate::parsing::token::lexer(¶ms.text, module_id) {
|
||||||
Ok(tokens) => tokens,
|
Ok(tokens) => tokens,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.add_to_diagnostics(¶ms, &[err], true).await;
|
self.add_to_diagnostics(¶ms, &[err], true).await;
|
||||||
@ -298,7 +300,7 @@ impl crate::lsp::backend::Backend for Backend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Lets update the ast.
|
// Lets update the ast.
|
||||||
let result = crate::parser::parse_tokens(tokens.clone());
|
let result = crate::parsing::parse_tokens(tokens.clone());
|
||||||
// TODO handle parse errors properly
|
// TODO handle parse errors properly
|
||||||
let mut ast = match result.parse_errs_as_err() {
|
let mut ast = match result.parse_errs_as_err() {
|
||||||
Ok(ast) => ast,
|
Ok(ast) => ast,
|
||||||
@ -376,7 +378,7 @@ impl Backend {
|
|||||||
self.executor_ctx.read().await
|
self.executor_ctx.read().await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn update_semantic_tokens(&self, tokens: &[crate::token::Token], params: &TextDocumentItem) {
|
async fn update_semantic_tokens(&self, tokens: &[crate::parsing::token::Token], params: &TextDocumentItem) {
|
||||||
// Update the semantic tokens map.
|
// Update the semantic tokens map.
|
||||||
let mut semantic_tokens = vec![];
|
let mut semantic_tokens = vec![];
|
||||||
let mut last_position = Position::new(0, 0);
|
let mut last_position = Position::new(0, 0);
|
||||||
@ -1036,7 +1038,7 @@ impl LanguageServer for Backend {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match hover {
|
match hover {
|
||||||
crate::ast::types::Hover::Function { name, range } => {
|
crate::parsing::ast::types::Hover::Function { name, range } => {
|
||||||
// Get the docs for this function.
|
// Get the docs for this function.
|
||||||
let Some(completion) = self.stdlib_completions.get(&name) else {
|
let Some(completion) = self.stdlib_completions.get(&name) else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -1071,8 +1073,8 @@ impl LanguageServer for Backend {
|
|||||||
range: Some(range),
|
range: Some(range),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
crate::ast::types::Hover::Signature { .. } => Ok(None),
|
crate::parsing::ast::types::Hover::Signature { .. } => Ok(None),
|
||||||
crate::ast::types::Hover::Comment { value, range } => Ok(Some(Hover {
|
crate::parsing::ast::types::Hover::Comment { value, range } => Ok(Some(Hover {
|
||||||
contents: HoverContents::Markup(MarkupContent {
|
contents: HoverContents::Markup(MarkupContent {
|
||||||
kind: MarkupKind::Markdown,
|
kind: MarkupKind::Markdown,
|
||||||
value,
|
value,
|
||||||
@ -1230,7 +1232,7 @@ impl LanguageServer for Backend {
|
|||||||
};
|
};
|
||||||
|
|
||||||
match hover {
|
match hover {
|
||||||
crate::ast::types::Hover::Function { name, range: _ } => {
|
crate::parsing::ast::types::Hover::Function { name, range: _ } => {
|
||||||
// Get the docs for this function.
|
// Get the docs for this function.
|
||||||
let Some(signature) = self.stdlib_signatures.get(&name) else {
|
let Some(signature) = self.stdlib_signatures.get(&name) else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -1238,7 +1240,7 @@ impl LanguageServer for Backend {
|
|||||||
|
|
||||||
Ok(Some(signature.clone()))
|
Ok(Some(signature.clone()))
|
||||||
}
|
}
|
||||||
crate::ast::types::Hover::Signature {
|
crate::parsing::ast::types::Hover::Signature {
|
||||||
name,
|
name,
|
||||||
parameter_index,
|
parameter_index,
|
||||||
range: _,
|
range: _,
|
||||||
@ -1253,7 +1255,7 @@ impl LanguageServer for Backend {
|
|||||||
|
|
||||||
Ok(Some(signature))
|
Ok(Some(signature))
|
||||||
}
|
}
|
||||||
crate::ast::types::Hover::Comment { value: _, range: _ } => {
|
crate::parsing::ast::types::Hover::Comment { value: _, range: _ } => {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1302,12 +1304,12 @@ impl LanguageServer for Backend {
|
|||||||
// I don't know if we need to do this again since it should be updated in the context.
|
// I don't know if we need to do this again since it should be updated in the context.
|
||||||
// But I figure better safe than sorry since this will write back out to the file.
|
// But I figure better safe than sorry since this will write back out to the file.
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let Ok(ast) = crate::parser::parse_str(current_code, module_id).parse_errs_as_err() else {
|
let Ok(ast) = crate::parsing::parse_str(current_code, module_id).parse_errs_as_err() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
// Now recast it.
|
// Now recast it.
|
||||||
let recast = ast.recast(
|
let recast = ast.recast(
|
||||||
&crate::ast::types::FormatOptions {
|
&crate::parsing::ast::types::FormatOptions {
|
||||||
tab_size: params.options.tab_size as usize,
|
tab_size: params.options.tab_size as usize,
|
||||||
insert_final_newline: params.options.insert_final_newline.unwrap_or(false),
|
insert_final_newline: params.options.insert_final_newline.unwrap_or(false),
|
||||||
use_tabs: !params.options.insert_spaces,
|
use_tabs: !params.options.insert_spaces,
|
||||||
@ -1336,7 +1338,7 @@ impl LanguageServer for Backend {
|
|||||||
// I don't know if we need to do this again since it should be updated in the context.
|
// I don't know if we need to do this again since it should be updated in the context.
|
||||||
// But I figure better safe than sorry since this will write back out to the file.
|
// But I figure better safe than sorry since this will write back out to the file.
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let Ok(mut ast) = crate::parser::parse_str(current_code, module_id).parse_errs_as_err() else {
|
let Ok(mut ast) = crate::parsing::parse_str(current_code, module_id).parse_errs_as_err() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -7,9 +7,9 @@ use tower_lsp::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{Node, Program},
|
|
||||||
executor::ProgramMemory,
|
executor::ProgramMemory,
|
||||||
lsp::test_util::{copilot_lsp_server, kcl_lsp_server},
|
lsp::test_util::{copilot_lsp_server, kcl_lsp_server},
|
||||||
|
parsing::ast::types::{Node, Program},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 12)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 12)]
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
macro_rules! parse_and_lex {
|
|
||||||
($func_name:ident, $test_kcl_program:expr) => {
|
|
||||||
#[test]
|
|
||||||
fn $func_name() {
|
|
||||||
let _ = crate::parser::top_level_parse($test_kcl_program);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
parse_and_lex!(crash_eof_1, "{\"ގގ\0\0\0\"\".");
|
|
||||||
parse_and_lex!(crash_eof_2, "(/=e\"\u{616}ݝ\"\"");
|
|
||||||
}
|
|
@ -1,9 +1,10 @@
|
|||||||
use sha2::{Digest as DigestTrait, Sha256};
|
use sha2::{Digest as DigestTrait, Sha256};
|
||||||
|
|
||||||
use super::{
|
use super::types::{ItemVisibility, VariableKind};
|
||||||
|
use crate::parsing::ast::types::{
|
||||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, CallExpressionKw,
|
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryPart, BodyItem, CallExpression, CallExpressionKw,
|
||||||
ElseIf, Expr, ExpressionStatement, FnArgType, FunctionExpression, Identifier, IfExpression, ImportItem,
|
CommentStyle, ElseIf, Expr, ExpressionStatement, FnArgType, FunctionExpression, Identifier, IfExpression,
|
||||||
ImportStatement, Literal, LiteralIdentifier, MemberExpression, MemberObject, NonCodeMeta, NonCodeNode,
|
ImportItem, ImportStatement, Literal, LiteralIdentifier, MemberExpression, MemberObject, NonCodeMeta, NonCodeNode,
|
||||||
NonCodeValue, ObjectExpression, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program,
|
NonCodeValue, ObjectExpression, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program,
|
||||||
ReturnStatement, TagDeclarator, UnaryExpression, VariableDeclaration, VariableDeclarator,
|
ReturnStatement, TagDeclarator, UnaryExpression, VariableDeclaration, VariableDeclarator,
|
||||||
};
|
};
|
||||||
@ -209,6 +210,15 @@ impl ReturnStatement {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl CommentStyle {
|
||||||
|
fn digestable_id(&self) -> [u8; 2] {
|
||||||
|
match &self {
|
||||||
|
CommentStyle::Line => *b"//",
|
||||||
|
CommentStyle::Block => *b"/*",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl NonCodeNode {
|
impl NonCodeNode {
|
||||||
compute_digest!(|slf, hasher| {
|
compute_digest!(|slf, hasher| {
|
||||||
match &slf.value {
|
match &slf.value {
|
||||||
@ -264,6 +274,24 @@ impl VariableDeclaration {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl VariableKind {
|
||||||
|
fn digestable_id(&self) -> [u8; 1] {
|
||||||
|
match self {
|
||||||
|
VariableKind::Const => [2],
|
||||||
|
VariableKind::Fn => [3],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItemVisibility {
|
||||||
|
fn digestable_id(&self) -> [u8; 1] {
|
||||||
|
match self {
|
||||||
|
ItemVisibility::Default => [0],
|
||||||
|
ItemVisibility::Export => [1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl VariableDeclarator {
|
impl VariableDeclarator {
|
||||||
compute_digest!(|slf, hasher| {
|
compute_digest!(|slf, hasher| {
|
||||||
hasher.update(slf.id.compute_digest());
|
hasher.update(slf.id.compute_digest());
|
||||||
@ -406,3 +434,31 @@ impl ElseIf {
|
|||||||
hasher.update(slf.then_val.compute_digest());
|
hasher.update(slf.then_val.compute_digest());
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod test {
|
||||||
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
|
async fn test_parse_digest() {
|
||||||
|
let prog1_string = r#"startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> line([5, 5], %)
|
||||||
|
"#;
|
||||||
|
let prog1_digest = crate::parsing::top_level_parse(prog1_string).unwrap().compute_digest();
|
||||||
|
|
||||||
|
let prog2_string = r#"startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 2], %)
|
||||||
|
|> line([5, 5], %)
|
||||||
|
"#;
|
||||||
|
let prog2_digest = crate::parsing::top_level_parse(prog2_string).unwrap().compute_digest();
|
||||||
|
|
||||||
|
assert!(prog1_digest != prog2_digest);
|
||||||
|
|
||||||
|
let prog3_string = r#"startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
||||||
|
|> line([5, 5], %)
|
||||||
|
"#;
|
||||||
|
let prog3_digest = crate::parsing::top_level_parse(prog3_string).unwrap().compute_digest();
|
||||||
|
|
||||||
|
assert_eq!(prog1_digest, prog3_digest);
|
||||||
|
}
|
||||||
|
}
|
@ -2,14 +2,14 @@ use std::collections::HashMap;
|
|||||||
|
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
|
|
||||||
use super::{
|
|
||||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, CallExpression,
|
|
||||||
CallExpressionKw, Expr, IfExpression, KclNone, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject,
|
|
||||||
Node, ObjectExpression, TagDeclarator, UnaryExpression, UnaryOperator,
|
|
||||||
};
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{BodyType, ExecState, ExecutorContext, KclValue, Metadata, StatementKind, TagEngineInfo, TagIdentifier},
|
executor::{BodyType, ExecState, ExecutorContext, KclValue, Metadata, StatementKind, TagEngineInfo, TagIdentifier},
|
||||||
|
parsing::ast::types::{
|
||||||
|
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, CallExpression,
|
||||||
|
CallExpressionKw, Expr, IfExpression, KclNone, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject,
|
||||||
|
Node, ObjectExpression, TagDeclarator, UnaryExpression, UnaryOperator,
|
||||||
|
},
|
||||||
source_range::SourceRange,
|
source_range::SourceRange,
|
||||||
std::{args::Arg, FunctionKind},
|
std::{args::Arg, FunctionKind},
|
||||||
};
|
};
|
5
src/wasm-lib/kcl/src/parsing/ast/mod.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pub(crate) mod digest;
|
||||||
|
pub(crate) mod execute;
|
||||||
|
pub mod modify;
|
||||||
|
pub(crate) mod source_range;
|
||||||
|
pub mod types;
|
@ -7,13 +7,13 @@ use kcmc::{
|
|||||||
use kittycad_modeling_cmds as kcmc;
|
use kittycad_modeling_cmds as kcmc;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{
|
|
||||||
ArrayExpression, CallExpression, ConstraintLevel, FormatOptions, Literal, Node, PipeExpression,
|
|
||||||
PipeSubstitution, VariableDeclarator,
|
|
||||||
},
|
|
||||||
engine::EngineManager,
|
engine::EngineManager,
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::Point2d,
|
executor::Point2d,
|
||||||
|
parsing::ast::types::{
|
||||||
|
ArrayExpression, CallExpression, ConstraintLevel, FormatOptions, Literal, Node, PipeExpression,
|
||||||
|
PipeSubstitution, VariableDeclarator,
|
||||||
|
},
|
||||||
source_range::{ModuleId, SourceRange},
|
source_range::{ModuleId, SourceRange},
|
||||||
Program,
|
Program,
|
||||||
};
|
};
|
||||||
@ -184,7 +184,7 @@ pub async fn modify_ast_for_sketch(
|
|||||||
let recasted = program.ast.recast(&FormatOptions::default(), 0);
|
let recasted = program.ast.recast(&FormatOptions::default(), 0);
|
||||||
|
|
||||||
// Re-parse the ast so we get the correct source ranges.
|
// Re-parse the ast so we get the correct source ranges.
|
||||||
*program = crate::parser::parse_str(&recasted, module_id)
|
*program = crate::parsing::parse_str(&recasted, module_id)
|
||||||
.parse_errs_as_err()?
|
.parse_errs_as_err()?
|
||||||
.into();
|
.into();
|
||||||
|
|
@ -1,4 +1,4 @@
|
|||||||
use super::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject};
|
use crate::parsing::ast::types::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject};
|
||||||
use crate::source_range::ModuleId;
|
use crate::source_range::ModuleId;
|
||||||
|
|
||||||
impl BodyItem {
|
impl BodyItem {
|
@ -8,7 +8,7 @@ use crate::SourceRange;
|
|||||||
// TODO: This should be its own type, similar to Program,
|
// TODO: This should be its own type, similar to Program,
|
||||||
// but guaranteed to have an Expression as its final item.
|
// but guaranteed to have an Expression as its final item.
|
||||||
// https://github.com/KittyCAD/modeling-app/issues/4015
|
// https://github.com/KittyCAD/modeling-app/issues/4015
|
||||||
type IfBlock = crate::ast::types::Program;
|
type IfBlock = crate::parsing::ast::types::Program;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
#[databake(path = kcl_lib::ast::types)]
|
#[databake(path = kcl_lib::ast::types)]
|
@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize};
|
|||||||
use serde_json::Value as JValue;
|
use serde_json::Value as JValue;
|
||||||
|
|
||||||
use super::Node;
|
use super::Node;
|
||||||
use crate::ast::types::{Expr, Literal};
|
use crate::parsing::ast::types::{Expr, Literal};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
#[databake(path = kcl_lib::ast::types)]
|
#[databake(path = kcl_lib::ast::types)]
|
@ -18,8 +18,8 @@ use tower_lsp::lsp_types::{
|
|||||||
CompletionItem, CompletionItemKind, DocumentSymbol, FoldingRange, FoldingRangeKind, Range as LspRange, SymbolKind,
|
CompletionItem, CompletionItemKind, DocumentSymbol, FoldingRange, FoldingRangeKind, Range as LspRange, SymbolKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::execute::execute_pipe_body;
|
use super::{digest::Digest, execute::execute_pipe_body};
|
||||||
pub use crate::ast::types::{
|
pub use crate::parsing::ast::types::{
|
||||||
condition::{ElseIf, IfExpression},
|
condition::{ElseIf, IfExpression},
|
||||||
literal_value::LiteralValue,
|
literal_value::LiteralValue,
|
||||||
none::KclNone,
|
none::KclNone,
|
||||||
@ -28,19 +28,14 @@ use crate::{
|
|||||||
docs::StdLibFn,
|
docs::StdLibFn,
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
executor::{ExecState, ExecutorContext, KclValue, Metadata, TagIdentifier},
|
executor::{ExecState, ExecutorContext, KclValue, Metadata, TagIdentifier},
|
||||||
parser::PIPE_OPERATOR,
|
parsing::PIPE_OPERATOR,
|
||||||
source_range::{ModuleId, SourceRange},
|
source_range::{ModuleId, SourceRange},
|
||||||
std::kcl_stdlib::KclStdLibFn,
|
std::kcl_stdlib::KclStdLibFn,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod condition;
|
mod condition;
|
||||||
pub(crate) mod digest;
|
|
||||||
pub(crate) mod execute;
|
|
||||||
mod literal_value;
|
mod literal_value;
|
||||||
mod none;
|
mod none;
|
||||||
pub(crate) mod source_range;
|
|
||||||
|
|
||||||
use digest::Digest;
|
|
||||||
|
|
||||||
pub enum Definition<'a> {
|
pub enum Definition<'a> {
|
||||||
Variable(&'a VariableDeclarator),
|
Variable(&'a VariableDeclarator),
|
||||||
@ -1029,15 +1024,6 @@ pub enum CommentStyle {
|
|||||||
Block,
|
Block,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommentStyle {
|
|
||||||
fn digestable_id(&self) -> [u8; 2] {
|
|
||||||
match &self {
|
|
||||||
CommentStyle::Line => *b"//",
|
|
||||||
CommentStyle::Block => *b"/*",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
#[databake(path = kcl_lib::ast::types)]
|
#[databake(path = kcl_lib::ast::types)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@ -1502,13 +1488,6 @@ pub enum ItemVisibility {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl ItemVisibility {
|
impl ItemVisibility {
|
||||||
fn digestable_id(&self) -> [u8; 1] {
|
|
||||||
match self {
|
|
||||||
ItemVisibility::Default => [0],
|
|
||||||
ItemVisibility::Export => [1],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_default(&self) -> bool {
|
fn is_default(&self) -> bool {
|
||||||
matches!(self, Self::Default)
|
matches!(self, Self::Default)
|
||||||
}
|
}
|
||||||
@ -1732,13 +1711,6 @@ pub enum VariableKind {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl VariableKind {
|
impl VariableKind {
|
||||||
fn digestable_id(&self) -> [u8; 1] {
|
|
||||||
match self {
|
|
||||||
VariableKind::Const => [2],
|
|
||||||
VariableKind::Fn => [3],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_completion_items() -> Result<Vec<CompletionItem>> {
|
pub fn to_completion_items() -> Result<Vec<CompletionItem>> {
|
||||||
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
let mut settings = schemars::gen::SchemaSettings::openapi3();
|
||||||
settings.inline_subschemas = true;
|
settings.inline_subschemas = true;
|
||||||
@ -3158,7 +3130,7 @@ fn ghi = (x) => {
|
|||||||
|
|
||||||
ghi("things")
|
ghi("things")
|
||||||
"#;
|
"#;
|
||||||
let program = crate::parser::top_level_parse(code).unwrap();
|
let program = crate::parsing::top_level_parse(code).unwrap();
|
||||||
let folding_ranges = program.get_lsp_folding_ranges();
|
let folding_ranges = program.get_lsp_folding_ranges();
|
||||||
assert_eq!(folding_ranges.len(), 3);
|
assert_eq!(folding_ranges.len(), 3);
|
||||||
assert_eq!(folding_ranges[0].start_line, 29);
|
assert_eq!(folding_ranges[0].start_line, 29);
|
||||||
@ -3194,7 +3166,7 @@ fn ghi = (x) => {
|
|||||||
return x
|
return x
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
let program = crate::parser::top_level_parse(code).unwrap();
|
let program = crate::parsing::top_level_parse(code).unwrap();
|
||||||
let symbols = program.get_lsp_symbols(code).unwrap();
|
let symbols = program.get_lsp_symbols(code).unwrap();
|
||||||
assert_eq!(symbols.len(), 7);
|
assert_eq!(symbols.len(), 7);
|
||||||
}
|
}
|
||||||
@ -3214,7 +3186,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
}, %)
|
}, %)
|
||||||
|> extrude(h, %)
|
|> extrude(h, %)
|
||||||
"#;
|
"#;
|
||||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
|
|
||||||
let value = program.get_non_code_meta_for_position(50);
|
let value = program.get_non_code_meta_for_position(50);
|
||||||
|
|
||||||
@ -3237,7 +3209,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
}, %)
|
}, %)
|
||||||
|> extrude(h, %)
|
|> extrude(h, %)
|
||||||
"#;
|
"#;
|
||||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
|
|
||||||
let value = program.get_non_code_meta_for_position(124);
|
let value = program.get_non_code_meta_for_position(124);
|
||||||
|
|
||||||
@ -3250,7 +3222,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
|> startProfileAt([0,0], %)
|
|> startProfileAt([0,0], %)
|
||||||
|> xLine(5, %) // lin
|
|> xLine(5, %) // lin
|
||||||
"#;
|
"#;
|
||||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
|
|
||||||
let value = program.get_non_code_meta_for_position(86);
|
let value = program.get_non_code_meta_for_position(86);
|
||||||
|
|
||||||
@ -3262,7 +3234,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
let some_program_string = r#"fn thing = (arg0: number, arg1: string, tag?: string) => {
|
let some_program_string = r#"fn thing = (arg0: number, arg1: string, tag?: string) => {
|
||||||
return arg0
|
return arg0
|
||||||
}"#;
|
}"#;
|
||||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
|
|
||||||
// Check the program output for the types of the parameters.
|
// Check the program output for the types of the parameters.
|
||||||
let function = program.body.first().unwrap();
|
let function = program.body.first().unwrap();
|
||||||
@ -3284,7 +3256,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
let some_program_string = r#"fn thing = (arg0: number[], arg1: string[], tag?: string) => {
|
let some_program_string = r#"fn thing = (arg0: number[], arg1: string[], tag?: string) => {
|
||||||
return arg0
|
return arg0
|
||||||
}"#;
|
}"#;
|
||||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
|
|
||||||
// Check the program output for the types of the parameters.
|
// Check the program output for the types of the parameters.
|
||||||
let function = program.body.first().unwrap();
|
let function = program.body.first().unwrap();
|
||||||
@ -3307,7 +3279,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
return arg0
|
return arg0
|
||||||
}"#;
|
}"#;
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let program = crate::parser::parse_str(some_program_string, module_id).unwrap();
|
let program = crate::parsing::parse_str(some_program_string, module_id).unwrap();
|
||||||
|
|
||||||
// Check the program output for the types of the parameters.
|
// Check the program output for the types of the parameters.
|
||||||
let function = program.body.first().unwrap();
|
let function = program.body.first().unwrap();
|
||||||
@ -3378,7 +3350,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
return 1
|
return 1
|
||||||
}"#;
|
}"#;
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let program = crate::parser::parse_str(some_program_string, module_id).unwrap();
|
let program = crate::parsing::parse_str(some_program_string, module_id).unwrap();
|
||||||
|
|
||||||
// Check the program output for the types of the parameters.
|
// Check the program output for the types of the parameters.
|
||||||
let function = program.body.first().unwrap();
|
let function = program.body.first().unwrap();
|
||||||
@ -3566,7 +3538,7 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
#[tokio::test(flavor = "multi_thread")]
|
#[tokio::test(flavor = "multi_thread")]
|
||||||
async fn test_parse_object_bool() {
|
async fn test_parse_object_bool() {
|
||||||
let some_program_string = r#"some_func({thing: true, other_thing: false})"#;
|
let some_program_string = r#"some_func({thing: true, other_thing: false})"#;
|
||||||
let program = crate::parser::top_level_parse(some_program_string).unwrap();
|
let program = crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
|
|
||||||
// We want to get the bool and verify it is a bool.
|
// We want to get the bool and verify it is a bool.
|
||||||
|
|
||||||
@ -3607,29 +3579,4 @@ const cylinder = startSketchOn('-XZ')
|
|||||||
|
|
||||||
assert_eq!(l.raw, "false");
|
assert_eq!(l.raw, "false");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread")]
|
|
||||||
async fn test_parse_digest() {
|
|
||||||
let prog1_string = r#"startSketchOn('XY')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> line([5, 5], %)
|
|
||||||
"#;
|
|
||||||
let prog1_digest = crate::parser::top_level_parse(prog1_string).unwrap().compute_digest();
|
|
||||||
|
|
||||||
let prog2_string = r#"startSketchOn('XY')
|
|
||||||
|> startProfileAt([0, 2], %)
|
|
||||||
|> line([5, 5], %)
|
|
||||||
"#;
|
|
||||||
let prog2_digest = crate::parser::top_level_parse(prog2_string).unwrap().compute_digest();
|
|
||||||
|
|
||||||
assert!(prog1_digest != prog2_digest);
|
|
||||||
|
|
||||||
let prog3_string = r#"startSketchOn('XY')
|
|
||||||
|> startProfileAt([0, 0], %)
|
|
||||||
|> line([5, 5], %)
|
|
||||||
"#;
|
|
||||||
let prog3_digest = crate::parser::top_level_parse(prog3_string).unwrap().compute_digest();
|
|
||||||
|
|
||||||
assert_eq!(prog1_digest, prog3_digest);
|
|
||||||
}
|
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ use schemars::JsonSchema;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::Node;
|
use super::Node;
|
||||||
use crate::{ast::types::ConstraintLevel, executor::KclValue};
|
use crate::{executor::KclValue, parsing::ast::types::ConstraintLevel};
|
||||||
|
|
||||||
const KCL_NONE_ID: &str = "KCL_NONE_ID";
|
const KCL_NONE_ID: &str = "KCL_NONE_ID";
|
||||||
|
|
@ -2,7 +2,7 @@ use winnow::{error::StrContext, stream::Stream};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
token::Token,
|
parsing::token::Token,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
|
@ -1,6 +1,6 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{BinaryExpression, BinaryOperator, BinaryPart, Node},
|
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
|
parsing::ast::types::{BinaryExpression, BinaryOperator, BinaryPart, Node},
|
||||||
SourceRange,
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -126,7 +126,8 @@ impl From<BinaryOperator> for BinaryExpressionToken {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{ast::types::Literal, source_range::ModuleId};
|
use crate::parsing::ast::types::Literal;
|
||||||
|
use crate::source_range::ModuleId;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn parse_and_evaluate() {
|
fn parse_and_evaluate() {
|
@ -1,15 +1,19 @@
|
|||||||
use parser_impl::ParseContext;
|
use parser::ParseContext;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{Node, Program},
|
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
source_range::{ModuleId, SourceRange},
|
parsing::{
|
||||||
|
ast::types::{Node, Program},
|
||||||
token::{Token, TokenType},
|
token::{Token, TokenType},
|
||||||
|
},
|
||||||
|
source_range::{ModuleId, SourceRange},
|
||||||
};
|
};
|
||||||
|
|
||||||
mod bad_inputs;
|
pub(crate) mod ast;
|
||||||
|
mod error;
|
||||||
mod math;
|
mod math;
|
||||||
pub(crate) mod parser_impl;
|
pub(crate) mod parser;
|
||||||
|
pub(crate) mod token;
|
||||||
|
|
||||||
pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%";
|
pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%";
|
||||||
pub const PIPE_OPERATOR: &str = "|>";
|
pub const PIPE_OPERATOR: &str = "|>";
|
||||||
@ -33,7 +37,7 @@ pub fn top_level_parse(code: &str) -> ParseResult {
|
|||||||
|
|
||||||
/// Parse the given KCL code into an AST.
|
/// Parse the given KCL code into an AST.
|
||||||
pub fn parse_str(code: &str, module_id: ModuleId) -> ParseResult {
|
pub fn parse_str(code: &str, module_id: ModuleId) -> ParseResult {
|
||||||
let tokens = pr_try!(crate::token::lexer(code, module_id));
|
let tokens = pr_try!(crate::parsing::token::lexer(code, module_id));
|
||||||
parse_tokens(tokens)
|
parse_tokens(tokens)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +72,7 @@ pub fn parse_tokens(tokens: Vec<Token>) -> ParseResult {
|
|||||||
return Node::<Program>::default().into();
|
return Node::<Program>::default().into();
|
||||||
}
|
}
|
||||||
|
|
||||||
parser_impl::run_parser(&mut tokens.as_slice())
|
parser::run_parser(&mut tokens.as_slice())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Result of parsing.
|
/// Result of parsing.
|
||||||
@ -104,7 +108,7 @@ impl ParseResult {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn unwrap_errs(&self) -> &[parser_impl::error::ParseError] {
|
pub fn unwrap_errs(&self) -> &[error::ParseError] {
|
||||||
&self.0.as_ref().unwrap().1.errors
|
&self.0.as_ref().unwrap().1.errors
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,3 +149,18 @@ impl From<KclError> for ParseResult {
|
|||||||
ParseResult(Err(e))
|
ParseResult(Err(e))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
macro_rules! parse_and_lex {
|
||||||
|
($func_name:ident, $test_kcl_program:expr) => {
|
||||||
|
#[test]
|
||||||
|
fn $func_name() {
|
||||||
|
let _ = crate::parsing::top_level_parse($test_kcl_program);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
parse_and_lex!(crash_eof_1, "{\"ގގ\0\0\0\"\".");
|
||||||
|
parse_and_lex!(crash_eof_2, "(/=e\"\u{616}ݝ\"\"");
|
||||||
|
}
|
@ -8,27 +8,29 @@ use winnow::{
|
|||||||
token::{any, one_of, take_till},
|
token::{any, one_of, take_till},
|
||||||
};
|
};
|
||||||
|
|
||||||
use self::error::ParseError;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{
|
|
||||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, BoxNode,
|
|
||||||
CallExpression, CallExpressionKw, CommentStyle, ElseIf, Expr, ExpressionStatement, FnArgPrimitive, FnArgType,
|
|
||||||
FunctionExpression, Identifier, IfExpression, ImportItem, ImportStatement, ItemVisibility, LabeledArg, Literal,
|
|
||||||
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, NonCodeMeta, NonCodeNode, NonCodeValue,
|
|
||||||
ObjectExpression, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement,
|
|
||||||
Shebang, TagDeclarator, UnaryExpression, UnaryOperator, VariableDeclaration, VariableDeclarator, VariableKind,
|
|
||||||
},
|
|
||||||
docs::StdLibFn,
|
docs::StdLibFn,
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
parser::{
|
parsing::{
|
||||||
math::BinaryExpressionToken, parser_impl::error::ContextError, PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR,
|
ast::types::{
|
||||||
|
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, BoxNode,
|
||||||
|
CallExpression, CallExpressionKw, CommentStyle, ElseIf, Expr, ExpressionStatement, FnArgPrimitive,
|
||||||
|
FnArgType, FunctionExpression, Identifier, IfExpression, ImportItem, ImportStatement, ItemVisibility,
|
||||||
|
Literal, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, Node, NonCodeMeta, NonCodeNode,
|
||||||
|
NonCodeValue, ObjectExpression, ObjectProperty, Parameter, PipeExpression, PipeSubstitution, Program,
|
||||||
|
ReturnStatement, Shebang, TagDeclarator, UnaryExpression, UnaryOperator, VariableDeclaration,
|
||||||
|
VariableDeclarator, VariableKind,
|
||||||
},
|
},
|
||||||
|
error::{self, ContextError, ParseError},
|
||||||
|
math::BinaryExpressionToken,
|
||||||
token::{Token, TokenType},
|
token::{Token, TokenType},
|
||||||
|
PIPE_OPERATOR, PIPE_SUBSTITUTION_OPERATOR,
|
||||||
|
},
|
||||||
unparser::ExprContext,
|
unparser::ExprContext,
|
||||||
SourceRange,
|
SourceRange,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub(crate) mod error;
|
use super::ast::types::LabeledArg;
|
||||||
|
|
||||||
thread_local! {
|
thread_local! {
|
||||||
/// The current `ParseContext`. `None` if parsing is not currently happening on this thread.
|
/// The current `ParseContext`. `None` if parsing is not currently happening on this thread.
|
||||||
@ -2288,14 +2290,14 @@ mod tests {
|
|||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{BodyItem, Expr, VariableKind},
|
parsing::ast::types::{BodyItem, Expr, VariableKind},
|
||||||
ModuleId,
|
ModuleId,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn assert_reserved(word: &str) {
|
fn assert_reserved(word: &str) {
|
||||||
// Try to use it as a variable name.
|
// Try to use it as a variable name.
|
||||||
let code = format!(r#"{} = 0"#, word);
|
let code = format!(r#"{} = 0"#, word);
|
||||||
let result = crate::parser::top_level_parse(code.as_str());
|
let result = crate::parsing::top_level_parse(code.as_str());
|
||||||
let err = &result.unwrap_errs()[0];
|
let err = &result.unwrap_errs()[0];
|
||||||
// Which token causes the error may change. In "return = 0", for
|
// Which token causes the error may change. In "return = 0", for
|
||||||
// example, "return" is the problem.
|
// example, "return" is the problem.
|
||||||
@ -2313,7 +2315,7 @@ mod tests {
|
|||||||
fn reserved_words() {
|
fn reserved_words() {
|
||||||
// Since these are stored in a set, we sort to make the tests
|
// Since these are stored in a set, we sort to make the tests
|
||||||
// deterministic.
|
// deterministic.
|
||||||
for word in crate::token::RESERVED_WORDS.keys().sorted() {
|
for word in crate::parsing::token::RESERVED_WORDS.keys().sorted() {
|
||||||
assert_reserved(word);
|
assert_reserved(word);
|
||||||
}
|
}
|
||||||
assert_reserved("import");
|
assert_reserved("import");
|
||||||
@ -2322,7 +2324,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_args() {
|
fn parse_args() {
|
||||||
for (i, (test, expected_len)) in [("someVar", 1), ("5, 3", 2), (r#""a""#, 1)].into_iter().enumerate() {
|
for (i, (test, expected_len)) in [("someVar", 1), ("5, 3", 2), (r#""a""#, 1)].into_iter().enumerate() {
|
||||||
let tokens = crate::token::lexer(test, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test, ModuleId::default()).unwrap();
|
||||||
let actual = match arguments.parse(&tokens) {
|
let actual = match arguments.parse(&tokens) {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(e) => panic!("Failed test {i}, could not parse function arguments from \"{test}\": {e:?}"),
|
Err(e) => panic!("Failed test {i}, could not parse function arguments from \"{test}\": {e:?}"),
|
||||||
@ -2333,7 +2335,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn weird_program_unclosed_paren() {
|
fn weird_program_unclosed_paren() {
|
||||||
let tokens = crate::token::lexer("fn firstPrime(", ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer("fn firstPrime(", ModuleId::default()).unwrap();
|
||||||
let last = tokens.last().unwrap();
|
let last = tokens.last().unwrap();
|
||||||
let err: super::error::ErrorKind = program.parse(&tokens).unwrap_err().into();
|
let err: super::error::ErrorKind = program.parse(&tokens).unwrap_err().into();
|
||||||
let err = err.unwrap_parse_error();
|
let err = err.unwrap_parse_error();
|
||||||
@ -2345,7 +2347,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn weird_program_just_a_pipe() {
|
fn weird_program_just_a_pipe() {
|
||||||
let tokens = crate::token::lexer("|", ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer("|", ModuleId::default()).unwrap();
|
||||||
let err: super::error::ErrorKind = program.parse(&tokens).unwrap_err().into();
|
let err: super::error::ErrorKind = program.parse(&tokens).unwrap_err().into();
|
||||||
let err = err.unwrap_parse_error();
|
let err = err.unwrap_parse_error();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -2358,7 +2360,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn parse_binary_expressions() {
|
fn parse_binary_expressions() {
|
||||||
for (i, test_program) in ["1 + 2 + 3"].into_iter().enumerate() {
|
for (i, test_program) in ["1 + 2 + 3"].into_iter().enumerate() {
|
||||||
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
|
||||||
let mut slice = tokens.as_slice();
|
let mut slice = tokens.as_slice();
|
||||||
let _actual = match binary_expression.parse_next(&mut slice) {
|
let _actual = match binary_expression.parse_next(&mut slice) {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
@ -2369,7 +2371,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_vardec_no_keyword() {
|
fn test_vardec_no_keyword() {
|
||||||
let tokens = crate::token::lexer("x = 4", ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer("x = 4", ModuleId::default()).unwrap();
|
||||||
let vardec = declaration(&mut tokens.as_slice()).unwrap();
|
let vardec = declaration(&mut tokens.as_slice()).unwrap();
|
||||||
assert_eq!(vardec.inner.kind, VariableKind::Const);
|
assert_eq!(vardec.inner.kind, VariableKind::Const);
|
||||||
let vardec = vardec.declarations.first().unwrap();
|
let vardec = vardec.declarations.first().unwrap();
|
||||||
@ -2382,7 +2384,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_negative_operands() {
|
fn test_negative_operands() {
|
||||||
let tokens = crate::token::lexer("-leg2", ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer("-leg2", ModuleId::default()).unwrap();
|
||||||
let _s = operand.parse_next(&mut tokens.as_slice()).unwrap();
|
let _s = operand.parse_next(&mut tokens.as_slice()).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2396,7 +2398,7 @@ mod tests {
|
|||||||
// comment 2
|
// comment 2
|
||||||
return 1
|
return 1
|
||||||
}"#;
|
}"#;
|
||||||
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
|
||||||
let mut slice = tokens.as_slice();
|
let mut slice = tokens.as_slice();
|
||||||
let expr = function_decl.map(|t| t.0).parse_next(&mut slice).unwrap();
|
let expr = function_decl.map(|t| t.0).parse_next(&mut slice).unwrap();
|
||||||
assert_eq!(expr.params, vec![]);
|
assert_eq!(expr.params, vec![]);
|
||||||
@ -2414,7 +2416,7 @@ mod tests {
|
|||||||
const yo = { a = { b = { c = '123' } } } /* block
|
const yo = { a = { b = { c = '123' } } } /* block
|
||||||
comment */
|
comment */
|
||||||
}"#;
|
}"#;
|
||||||
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
|
||||||
let mut slice = tokens.as_slice();
|
let mut slice = tokens.as_slice();
|
||||||
let expr = function_decl.map(|t| t.0).parse_next(&mut slice).unwrap();
|
let expr = function_decl.map(|t| t.0).parse_next(&mut slice).unwrap();
|
||||||
let comment0 = &expr.body.non_code_meta.non_code_nodes.get(&0).unwrap()[0];
|
let comment0 = &expr.body.non_code_meta.non_code_nodes.get(&0).unwrap()[0];
|
||||||
@ -2427,7 +2429,7 @@ comment */
|
|||||||
/* comment at start */
|
/* comment at start */
|
||||||
|
|
||||||
const mySk1 = startSketchAt([0, 0])"#;
|
const mySk1 = startSketchAt([0, 0])"#;
|
||||||
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
|
||||||
let program = program.parse(&tokens).unwrap();
|
let program = program.parse(&tokens).unwrap();
|
||||||
let mut starting_comments = program.inner.non_code_meta.start_nodes;
|
let mut starting_comments = program.inner.non_code_meta.start_nodes;
|
||||||
assert_eq!(starting_comments.len(), 2);
|
assert_eq!(starting_comments.len(), 2);
|
||||||
@ -2445,7 +2447,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_comment_in_pipe() {
|
fn test_comment_in_pipe() {
|
||||||
let tokens = crate::token::lexer(r#"const x = y() |> /*hi*/ z(%)"#, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(r#"const x = y() |> /*hi*/ z(%)"#, ModuleId::default()).unwrap();
|
||||||
let mut body = program.parse(&tokens).unwrap().inner.body;
|
let mut body = program.parse(&tokens).unwrap().inner.body;
|
||||||
let BodyItem::VariableDeclaration(mut item) = body.remove(0) else {
|
let BodyItem::VariableDeclaration(mut item) = body.remove(0) else {
|
||||||
panic!("expected vardec");
|
panic!("expected vardec");
|
||||||
@ -2472,7 +2474,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
return sg
|
return sg
|
||||||
return sg
|
return sg
|
||||||
}"#;
|
}"#;
|
||||||
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
|
||||||
let mut slice = tokens.as_slice();
|
let mut slice = tokens.as_slice();
|
||||||
let _expr = function_decl.parse_next(&mut slice).unwrap();
|
let _expr = function_decl.parse_next(&mut slice).unwrap();
|
||||||
}
|
}
|
||||||
@ -2484,7 +2486,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
return 2
|
return 2
|
||||||
}";
|
}";
|
||||||
let module_id = ModuleId::from_usize(1);
|
let module_id = ModuleId::from_usize(1);
|
||||||
let tokens = crate::token::lexer(test_program, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, module_id).unwrap();
|
||||||
let mut slice = tokens.as_slice();
|
let mut slice = tokens.as_slice();
|
||||||
let expr = function_decl.map(|t| t.0).parse_next(&mut slice).unwrap();
|
let expr = function_decl.map(|t| t.0).parse_next(&mut slice).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -2549,7 +2551,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
|> c(%) // inline-comment
|
|> c(%) // inline-comment
|
||||||
|> d(%)"#;
|
|> d(%)"#;
|
||||||
|
|
||||||
let tokens = crate::token::lexer(test_input, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_input, ModuleId::default()).unwrap();
|
||||||
let mut slice = tokens.as_slice();
|
let mut slice = tokens.as_slice();
|
||||||
let Node {
|
let Node {
|
||||||
inner: PipeExpression {
|
inner: PipeExpression {
|
||||||
@ -2580,7 +2582,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let tokens = crate::token::lexer(test_program, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, module_id).unwrap();
|
||||||
let Program { non_code_meta, .. } = function_body.parse(&tokens).unwrap().inner;
|
let Program { non_code_meta, .. } = function_body.parse(&tokens).unwrap().inner;
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
vec![Node::new(
|
vec![Node::new(
|
||||||
@ -2648,7 +2650,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
comment */
|
comment */
|
||||||
return 1"#;
|
return 1"#;
|
||||||
|
|
||||||
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
|
||||||
let actual = program.parse(&tokens).unwrap();
|
let actual = program.parse(&tokens).unwrap();
|
||||||
assert_eq!(actual.non_code_meta.non_code_nodes.len(), 1);
|
assert_eq!(actual.non_code_meta.non_code_nodes.len(), 1);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
@ -2663,7 +2665,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_bracketed_binary_expression() {
|
fn test_bracketed_binary_expression() {
|
||||||
let input = "(2 - 3)";
|
let input = "(2 - 3)";
|
||||||
let tokens = crate::token::lexer(input, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(input, ModuleId::default()).unwrap();
|
||||||
let actual = match binary_expr_in_parens.parse(&tokens) {
|
let actual = match binary_expr_in_parens.parse(&tokens) {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(e) => panic!("{e:?}"),
|
Err(e) => panic!("{e:?}"),
|
||||||
@ -2678,7 +2680,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
"6 / ( sigmaAllow * width )",
|
"6 / ( sigmaAllow * width )",
|
||||||
"sqrt(distance * p * FOS * 6 / ( sigmaAllow * width ))",
|
"sqrt(distance * p * FOS * 6 / ( sigmaAllow * width ))",
|
||||||
] {
|
] {
|
||||||
let tokens = crate::token::lexer(input, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(input, ModuleId::default()).unwrap();
|
||||||
let _actual = match expression.parse(&tokens) {
|
let _actual = match expression.parse(&tokens) {
|
||||||
Ok(x) => x,
|
Ok(x) => x,
|
||||||
Err(e) => panic!("{e:?}"),
|
Err(e) => panic!("{e:?}"),
|
||||||
@ -2689,7 +2691,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_arithmetic() {
|
fn test_arithmetic() {
|
||||||
let input = "1 * (2 - 3)";
|
let input = "1 * (2 - 3)";
|
||||||
let tokens = crate::token::lexer(input, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(input, ModuleId::default()).unwrap();
|
||||||
// The RHS should be a binary expression.
|
// The RHS should be a binary expression.
|
||||||
let actual = binary_expression.parse(&tokens).unwrap();
|
let actual = binary_expression.parse(&tokens).unwrap();
|
||||||
assert_eq!(actual.operator, BinaryOperator::Mul);
|
assert_eq!(actual.operator, BinaryOperator::Mul);
|
||||||
@ -2717,7 +2719,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
let tokens = crate::token::lexer(test_input, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_input, ModuleId::default()).unwrap();
|
||||||
let mut actual = match declaration.parse(&tokens) {
|
let mut actual = match declaration.parse(&tokens) {
|
||||||
Err(e) => panic!("Could not parse test {i}: {e:#?}"),
|
Err(e) => panic!("Could not parse test {i}: {e:#?}"),
|
||||||
Ok(a) => a,
|
Ok(a) => a,
|
||||||
@ -2735,7 +2737,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_function_call() {
|
fn test_function_call() {
|
||||||
for (i, test_input) in ["const x = f(1)", "const x = f( 1 )"].into_iter().enumerate() {
|
for (i, test_input) in ["const x = f(1)", "const x = f( 1 )"].into_iter().enumerate() {
|
||||||
let tokens = crate::token::lexer(test_input, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_input, ModuleId::default()).unwrap();
|
||||||
let _actual = match declaration.parse(&tokens) {
|
let _actual = match declaration.parse(&tokens) {
|
||||||
Err(e) => panic!("Could not parse test {i}: {e:#?}"),
|
Err(e) => panic!("Could not parse test {i}: {e:#?}"),
|
||||||
Ok(a) => a,
|
Ok(a) => a,
|
||||||
@ -2746,7 +2748,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_nested_arithmetic() {
|
fn test_nested_arithmetic() {
|
||||||
let input = "1 * ((2 - 3) / 4)";
|
let input = "1 * ((2 - 3) / 4)";
|
||||||
let tokens = crate::token::lexer(input, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(input, ModuleId::default()).unwrap();
|
||||||
// The RHS should be a binary expression.
|
// The RHS should be a binary expression.
|
||||||
let outer = binary_expression.parse(&tokens).unwrap();
|
let outer = binary_expression.parse(&tokens).unwrap();
|
||||||
assert_eq!(outer.operator, BinaryOperator::Mul);
|
assert_eq!(outer.operator, BinaryOperator::Mul);
|
||||||
@ -2765,7 +2767,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
fn binary_expression_ignores_whitespace() {
|
fn binary_expression_ignores_whitespace() {
|
||||||
let tests = ["1 - 2", "1- 2", "1 -2", "1-2"];
|
let tests = ["1 - 2", "1- 2", "1 -2", "1-2"];
|
||||||
for test in tests {
|
for test in tests {
|
||||||
let tokens = crate::token::lexer(test, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test, ModuleId::default()).unwrap();
|
||||||
let actual = binary_expression.parse(&tokens).unwrap();
|
let actual = binary_expression.parse(&tokens).unwrap();
|
||||||
assert_eq!(actual.operator, BinaryOperator::Sub);
|
assert_eq!(actual.operator, BinaryOperator::Sub);
|
||||||
let BinaryPart::Literal(left) = actual.inner.left else {
|
let BinaryPart::Literal(left) = actual.inner.left else {
|
||||||
@ -2786,7 +2788,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
a comment
|
a comment
|
||||||
spanning a few lines */
|
spanning a few lines */
|
||||||
|> z(%)"#;
|
|> z(%)"#;
|
||||||
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
|
||||||
let actual = pipe_expression.parse(&tokens).unwrap();
|
let actual = pipe_expression.parse(&tokens).unwrap();
|
||||||
let n = actual.non_code_meta.non_code_nodes.len();
|
let n = actual.non_code_meta.non_code_nodes.len();
|
||||||
assert_eq!(n, 1, "expected one comment in pipe expression but found {n}");
|
assert_eq!(n, 1, "expected one comment in pipe expression but found {n}");
|
||||||
@ -2814,7 +2816,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
|
||||||
let actual = pipe_expression.parse(&tokens);
|
let actual = pipe_expression.parse(&tokens);
|
||||||
assert!(actual.is_ok(), "could not parse test {i}, '{test_program}'");
|
assert!(actual.is_ok(), "could not parse test {i}, '{test_program}'");
|
||||||
let actual = actual.unwrap();
|
let actual = actual.unwrap();
|
||||||
@ -2959,7 +2961,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
{
|
{
|
||||||
let tokens = crate::token::lexer(test_program, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, module_id).unwrap();
|
||||||
let actual = non_code_node.parse(&tokens);
|
let actual = non_code_node.parse(&tokens);
|
||||||
assert!(actual.is_ok(), "could not parse test {i}: {actual:#?}");
|
assert!(actual.is_ok(), "could not parse test {i}: {actual:#?}");
|
||||||
let actual = actual.unwrap();
|
let actual = actual.unwrap();
|
||||||
@ -2971,7 +2973,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
fn recognize_invalid_params() {
|
fn recognize_invalid_params() {
|
||||||
let test_fn = "(let) => { return 1 }";
|
let test_fn = "(let) => { return 1 }";
|
||||||
let module_id = ModuleId::from_usize(2);
|
let module_id = ModuleId::from_usize(2);
|
||||||
let tokens = crate::token::lexer(test_fn, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(test_fn, module_id).unwrap();
|
||||||
let err = function_decl.parse(&tokens).unwrap_err().into_inner();
|
let err = function_decl.parse(&tokens).unwrap_err().into_inner();
|
||||||
let cause = err.cause.unwrap();
|
let cause = err.cause.unwrap();
|
||||||
// This is the token `let`
|
// This is the token `let`
|
||||||
@ -2987,7 +2989,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
let string_literal = r#""
|
let string_literal = r#""
|
||||||
// a comment
|
// a comment
|
||||||
""#;
|
""#;
|
||||||
let tokens = crate::token::lexer(string_literal, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(string_literal, ModuleId::default()).unwrap();
|
||||||
let parsed_literal = literal.parse(&tokens).unwrap();
|
let parsed_literal = literal.parse(&tokens).unwrap();
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
parsed_literal.value,
|
parsed_literal.value,
|
||||||
@ -3004,7 +3006,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
|> lineTo([0, -0], %) // MoveRelative
|
|> lineTo([0, -0], %) // MoveRelative
|
||||||
|
|
||||||
"#;
|
"#;
|
||||||
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
|
||||||
let mut slice = &tokens[..];
|
let mut slice = &tokens[..];
|
||||||
let _actual = pipe_expression.parse_next(&mut slice).unwrap();
|
let _actual = pipe_expression.parse_next(&mut slice).unwrap();
|
||||||
assert_eq!(slice[0].token_type, TokenType::Whitespace);
|
assert_eq!(slice[0].token_type, TokenType::Whitespace);
|
||||||
@ -3013,14 +3015,14 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_pipes_on_pipes() {
|
fn test_pipes_on_pipes() {
|
||||||
let test_program = include_str!("../../../tests/executor/inputs/pipes_on_pipes.kcl");
|
let test_program = include_str!("../../../tests/executor/inputs/pipes_on_pipes.kcl");
|
||||||
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
|
||||||
let _ = run_parser(&mut &*tokens).unwrap();
|
let _ = run_parser(&mut &*tokens).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_cube() {
|
fn test_cube() {
|
||||||
let test_program = include_str!("../../../tests/executor/inputs/cube.kcl");
|
let test_program = include_str!("../../../tests/executor/inputs/cube.kcl");
|
||||||
let tokens = crate::token::lexer(test_program, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test_program, ModuleId::default()).unwrap();
|
||||||
match program.parse(&tokens) {
|
match program.parse(&tokens) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -3038,7 +3040,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
("a,b", vec!["a", "b"]),
|
("a,b", vec!["a", "b"]),
|
||||||
];
|
];
|
||||||
for (i, (input, expected)) in tests.into_iter().enumerate() {
|
for (i, (input, expected)) in tests.into_iter().enumerate() {
|
||||||
let tokens = crate::token::lexer(input, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(input, ModuleId::default()).unwrap();
|
||||||
let actual = parameters.parse(&tokens);
|
let actual = parameters.parse(&tokens);
|
||||||
assert!(actual.is_ok(), "could not parse test {i}");
|
assert!(actual.is_ok(), "could not parse test {i}");
|
||||||
let actual_ids: Vec<_> = actual.unwrap().into_iter().map(|p| p.identifier.inner.name).collect();
|
let actual_ids: Vec<_> = actual.unwrap().into_iter().map(|p| p.identifier.inner.name).collect();
|
||||||
@ -3052,7 +3054,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
return 2
|
return 2
|
||||||
}";
|
}";
|
||||||
|
|
||||||
let tokens = crate::token::lexer(input, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(input, ModuleId::default()).unwrap();
|
||||||
let actual = function_decl.parse(&tokens);
|
let actual = function_decl.parse(&tokens);
|
||||||
assert!(actual.is_ok(), "could not parse test function");
|
assert!(actual.is_ok(), "could not parse test function");
|
||||||
}
|
}
|
||||||
@ -3062,8 +3064,8 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
let tests = ["const myVar = 5", "const myVar=5", "const myVar =5", "const myVar= 5"];
|
let tests = ["const myVar = 5", "const myVar=5", "const myVar =5", "const myVar= 5"];
|
||||||
for test in tests {
|
for test in tests {
|
||||||
// Run the original parser
|
// Run the original parser
|
||||||
let tokens = crate::token::lexer(test, ModuleId::default()).unwrap();
|
let tokens = crate::parsing::token::lexer(test, ModuleId::default()).unwrap();
|
||||||
let mut expected_body = crate::parser::parse_tokens(tokens.clone()).unwrap().inner.body;
|
let mut expected_body = crate::parsing::parse_tokens(tokens.clone()).unwrap().inner.body;
|
||||||
assert_eq!(expected_body.len(), 1);
|
assert_eq!(expected_body.len(), 1);
|
||||||
let BodyItem::VariableDeclaration(expected) = expected_body.pop().unwrap() else {
|
let BodyItem::VariableDeclaration(expected) = expected_body.pop().unwrap() else {
|
||||||
panic!("Expected variable declaration");
|
panic!("Expected variable declaration");
|
||||||
@ -3090,7 +3092,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_math_parse() {
|
fn test_math_parse() {
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let actual = crate::parser::parse_str(r#"5 + "a""#, module_id).unwrap().inner.body;
|
let actual = crate::parsing::parse_str(r#"5 + "a""#, module_id).unwrap().inner.body;
|
||||||
let expr = Node::boxed(
|
let expr = Node::boxed(
|
||||||
BinaryExpression {
|
BinaryExpression {
|
||||||
operator: BinaryOperator::Add,
|
operator: BinaryOperator::Add,
|
||||||
@ -3226,7 +3228,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
fn test_abstract_syntax_tree() {
|
fn test_abstract_syntax_tree() {
|
||||||
let code = "5 +6";
|
let code = "5 +6";
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let result = crate::parser::parse_str(code, module_id).unwrap();
|
let result = crate::parsing::parse_str(code, module_id).unwrap();
|
||||||
let expected_result = Node::new(
|
let expected_result = Node::new(
|
||||||
Program {
|
Program {
|
||||||
body: vec![BodyItem::ExpressionStatement(Node::new(
|
body: vec![BodyItem::ExpressionStatement(Node::new(
|
||||||
@ -3281,13 +3283,13 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_empty_file() {
|
fn test_empty_file() {
|
||||||
let some_program_string = r#""#;
|
let some_program_string = r#""#;
|
||||||
let result = crate::parser::top_level_parse(some_program_string);
|
let result = crate::parsing::top_level_parse(some_program_string);
|
||||||
assert!(result.is_ok());
|
assert!(result.is_ok());
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn assert_no_err(p: &str) -> (Node<Program>, ParseContext) {
|
fn assert_no_err(p: &str) -> (Node<Program>, ParseContext) {
|
||||||
let result = crate::parser::top_level_parse(p);
|
let result = crate::parsing::top_level_parse(p);
|
||||||
let result = result.0.unwrap();
|
let result = result.0.unwrap();
|
||||||
assert!(result.1.errors.is_empty(), "found: {:#?}", result.1.errors);
|
assert!(result.1.errors.is_empty(), "found: {:#?}", result.1.errors);
|
||||||
(result.0.unwrap(), result.1)
|
(result.0.unwrap(), result.1)
|
||||||
@ -3295,7 +3297,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn assert_err(p: &str, msg: &str, src: [usize; 2]) {
|
fn assert_err(p: &str, msg: &str, src: [usize; 2]) {
|
||||||
let result = crate::parser::top_level_parse(p);
|
let result = crate::parsing::top_level_parse(p);
|
||||||
let err = &result.unwrap_errs()[0];
|
let err = &result.unwrap_errs()[0];
|
||||||
assert_eq!(err.message, msg);
|
assert_eq!(err.message, msg);
|
||||||
assert_eq!(err.source_range.start(), src[0]);
|
assert_eq!(err.source_range.start(), src[0]);
|
||||||
@ -3304,7 +3306,7 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn assert_err_contains(p: &str, expected: &str) {
|
fn assert_err_contains(p: &str, expected: &str) {
|
||||||
let result = crate::parser::top_level_parse(p);
|
let result = crate::parsing::top_level_parse(p);
|
||||||
let err = &result.unwrap_errs()[0].message;
|
let err = &result.unwrap_errs()[0].message;
|
||||||
assert!(err.contains(expected), "actual='{err}'");
|
assert!(err.contains(expected), "actual='{err}'");
|
||||||
}
|
}
|
||||||
@ -3322,14 +3324,14 @@ const mySk1 = startSketchAt([0, 0])"#;
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_parse_member_expression_double_nested_braces() {
|
fn test_parse_member_expression_double_nested_braces() {
|
||||||
let code = r#"const prop = yo["one"][two]"#;
|
let code = r#"const prop = yo["one"][two]"#;
|
||||||
crate::parser::top_level_parse(code).unwrap();
|
crate::parsing::top_level_parse(code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_member_expression_binary_expression_period_number_first() {
|
fn test_parse_member_expression_binary_expression_period_number_first() {
|
||||||
let code = r#"const obj = { a: 1, b: 2 }
|
let code = r#"const obj = { a: 1, b: 2 }
|
||||||
const height = 1 - obj.a"#;
|
const height = 1 - obj.a"#;
|
||||||
crate::parser::top_level_parse(code).unwrap();
|
crate::parsing::top_level_parse(code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -3337,47 +3339,47 @@ const height = 1 - obj.a"#;
|
|||||||
let code = r#"const obj = { thing: 1 }
|
let code = r#"const obj = { thing: 1 }
|
||||||
startSketchOn(obj.sketch)"#;
|
startSketchOn(obj.sketch)"#;
|
||||||
|
|
||||||
crate::parser::top_level_parse(code).unwrap();
|
crate::parsing::top_level_parse(code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_member_expression_binary_expression_brace_number_first() {
|
fn test_parse_member_expression_binary_expression_brace_number_first() {
|
||||||
let code = r#"const obj = { a: 1, b: 2 }
|
let code = r#"const obj = { a: 1, b: 2 }
|
||||||
const height = 1 - obj["a"]"#;
|
const height = 1 - obj["a"]"#;
|
||||||
crate::parser::top_level_parse(code).unwrap();
|
crate::parsing::top_level_parse(code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_member_expression_binary_expression_brace_number_second() {
|
fn test_parse_member_expression_binary_expression_brace_number_second() {
|
||||||
let code = r#"const obj = { a: 1, b: 2 }
|
let code = r#"const obj = { a: 1, b: 2 }
|
||||||
const height = obj["a"] - 1"#;
|
const height = obj["a"] - 1"#;
|
||||||
crate::parser::top_level_parse(code).unwrap();
|
crate::parsing::top_level_parse(code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_member_expression_binary_expression_in_array_number_first() {
|
fn test_parse_member_expression_binary_expression_in_array_number_first() {
|
||||||
let code = r#"const obj = { a: 1, b: 2 }
|
let code = r#"const obj = { a: 1, b: 2 }
|
||||||
const height = [1 - obj["a"], 0]"#;
|
const height = [1 - obj["a"], 0]"#;
|
||||||
crate::parser::top_level_parse(code).unwrap();
|
crate::parsing::top_level_parse(code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_member_expression_binary_expression_in_array_number_second() {
|
fn test_parse_member_expression_binary_expression_in_array_number_second() {
|
||||||
let code = r#"const obj = { a: 1, b: 2 }
|
let code = r#"const obj = { a: 1, b: 2 }
|
||||||
const height = [obj["a"] - 1, 0]"#;
|
const height = [obj["a"] - 1, 0]"#;
|
||||||
crate::parser::top_level_parse(code).unwrap();
|
crate::parsing::top_level_parse(code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_member_expression_binary_expression_in_array_number_second_missing_space() {
|
fn test_parse_member_expression_binary_expression_in_array_number_second_missing_space() {
|
||||||
let code = r#"const obj = { a: 1, b: 2 }
|
let code = r#"const obj = { a: 1, b: 2 }
|
||||||
const height = [obj["a"] -1, 0]"#;
|
const height = [obj["a"] -1, 0]"#;
|
||||||
crate::parser::top_level_parse(code).unwrap();
|
crate::parsing::top_level_parse(code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_anon_fn() {
|
fn test_anon_fn() {
|
||||||
crate::parser::top_level_parse("foo(42, fn(x) { return x + 1 })").unwrap();
|
crate::parsing::top_level_parse("foo(42, fn(x) { return x + 1 })").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -3415,7 +3417,7 @@ const secondExtrude = startSketchOn('XY')
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_parens_unicode() {
|
fn test_parse_parens_unicode() {
|
||||||
let result = crate::parser::top_level_parse("(ޜ");
|
let result = crate::parsing::top_level_parse("(ޜ");
|
||||||
let KclError::Lexical(details) = result.0.unwrap_err() else {
|
let KclError::Lexical(details) = result.0.unwrap_err() else {
|
||||||
panic!();
|
panic!();
|
||||||
};
|
};
|
||||||
@ -3433,12 +3435,12 @@ const thickness = 0.56
|
|||||||
|
|
||||||
const bracket = [-leg2 + thickness, 0]
|
const bracket = [-leg2 + thickness, 0]
|
||||||
"#;
|
"#;
|
||||||
crate::parser::top_level_parse(code).unwrap();
|
crate::parsing::top_level_parse(code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_nested_open_brackets() {
|
fn test_parse_nested_open_brackets() {
|
||||||
crate::parser::top_level_parse(
|
crate::parsing::top_level_parse(
|
||||||
r#"
|
r#"
|
||||||
z(-[["#,
|
z(-[["#,
|
||||||
)
|
)
|
||||||
@ -3625,14 +3627,14 @@ e
|
|||||||
}
|
}
|
||||||
firstPrimeNumber()
|
firstPrimeNumber()
|
||||||
"#;
|
"#;
|
||||||
let _ast = crate::parser::top_level_parse(code).unwrap();
|
let _ast = crate::parsing::top_level_parse(code).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn array() {
|
fn array() {
|
||||||
let program = r#"[1, 2, 3]"#;
|
let program = r#"[1, 2, 3]"#;
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let tokens = crate::token::lexer(program, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(program, module_id).unwrap();
|
||||||
let mut sl: &[Token] = &tokens;
|
let mut sl: &[Token] = &tokens;
|
||||||
let _arr = array_elem_by_elem(&mut sl).unwrap();
|
let _arr = array_elem_by_elem(&mut sl).unwrap();
|
||||||
}
|
}
|
||||||
@ -3645,7 +3647,7 @@ e
|
|||||||
3,
|
3,
|
||||||
]"#;
|
]"#;
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let tokens = crate::token::lexer(program, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(program, module_id).unwrap();
|
||||||
let mut sl: &[Token] = &tokens;
|
let mut sl: &[Token] = &tokens;
|
||||||
let _arr = array_elem_by_elem(&mut sl).unwrap();
|
let _arr = array_elem_by_elem(&mut sl).unwrap();
|
||||||
}
|
}
|
||||||
@ -3659,7 +3661,7 @@ e
|
|||||||
3
|
3
|
||||||
]"#;
|
]"#;
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let tokens = crate::token::lexer(program, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(program, module_id).unwrap();
|
||||||
let mut sl: &[Token] = &tokens;
|
let mut sl: &[Token] = &tokens;
|
||||||
let _arr = array_elem_by_elem(&mut sl).unwrap();
|
let _arr = array_elem_by_elem(&mut sl).unwrap();
|
||||||
}
|
}
|
||||||
@ -3672,7 +3674,7 @@ e
|
|||||||
4
|
4
|
||||||
}";
|
}";
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let tokens = crate::token::lexer(some_program_string, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(some_program_string, module_id).unwrap();
|
||||||
let mut sl: &[Token] = &tokens;
|
let mut sl: &[Token] = &tokens;
|
||||||
let _res = if_expr(&mut sl).unwrap();
|
let _res = if_expr(&mut sl).unwrap();
|
||||||
}
|
}
|
||||||
@ -3683,7 +3685,7 @@ e
|
|||||||
4
|
4
|
||||||
}";
|
}";
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let tokens = crate::token::lexer(some_program_string, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(some_program_string, module_id).unwrap();
|
||||||
let mut sl: &[Token] = &tokens;
|
let mut sl: &[Token] = &tokens;
|
||||||
let _res = else_if(&mut sl).unwrap();
|
let _res = else_if(&mut sl).unwrap();
|
||||||
}
|
}
|
||||||
@ -3698,7 +3700,7 @@ e
|
|||||||
5
|
5
|
||||||
}";
|
}";
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let tokens = crate::token::lexer(some_program_string, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(some_program_string, module_id).unwrap();
|
||||||
let mut sl: &[Token] = &tokens;
|
let mut sl: &[Token] = &tokens;
|
||||||
let _res = if_expr(&mut sl).unwrap();
|
let _res = if_expr(&mut sl).unwrap();
|
||||||
}
|
}
|
||||||
@ -3711,7 +3713,7 @@ e
|
|||||||
|
|
||||||
thing(false)
|
thing(false)
|
||||||
"#;
|
"#;
|
||||||
crate::parser::top_level_parse(some_program_string).unwrap();
|
crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -3752,7 +3754,7 @@ thing(false)
|
|||||||
|> line([-5.09, 12.33], %)
|
|> line([-5.09, 12.33], %)
|
||||||
asdasd
|
asdasd
|
||||||
"#;
|
"#;
|
||||||
crate::parser::top_level_parse(test_program).unwrap_errs();
|
crate::parsing::top_level_parse(test_program).unwrap_errs();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -3773,14 +3775,14 @@ const b2 = cube([3,3], 4)
|
|||||||
const pt1 = b1[0]
|
const pt1 = b1[0]
|
||||||
const pt2 = b2[0]
|
const pt2 = b2[0]
|
||||||
"#;
|
"#;
|
||||||
crate::parser::top_level_parse(some_program_string).unwrap();
|
crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_math_with_stdlib() {
|
fn test_math_with_stdlib() {
|
||||||
let some_program_string = r#"const d2r = pi() / 2
|
let some_program_string = r#"const d2r = pi() / 2
|
||||||
let other_thing = 2 * cos(3)"#;
|
let other_thing = 2 * cos(3)"#;
|
||||||
crate::parser::top_level_parse(some_program_string).unwrap();
|
crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -3798,7 +3800,7 @@ let other_thing = 2 * cos(3)"#;
|
|||||||
}
|
}
|
||||||
let myBox = box([0,0], -3, -16, -10)
|
let myBox = box([0,0], -3, -16, -10)
|
||||||
"#;
|
"#;
|
||||||
crate::parser::top_level_parse(some_program_string).unwrap();
|
crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -3818,7 +3820,7 @@ let myBox = box([0,0], -3, -16, -10)
|
|||||||
fn arg_labels() {
|
fn arg_labels() {
|
||||||
let input = r#"length: 3"#;
|
let input = r#"length: 3"#;
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let tokens = crate::token::lexer(input, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(input, module_id).unwrap();
|
||||||
let mut sl: &[Token] = &tokens;
|
let mut sl: &[Token] = &tokens;
|
||||||
super::labeled_arguments(&mut sl).unwrap();
|
super::labeled_arguments(&mut sl).unwrap();
|
||||||
}
|
}
|
||||||
@ -3827,7 +3829,7 @@ let myBox = box([0,0], -3, -16, -10)
|
|||||||
fn kw_fn() {
|
fn kw_fn() {
|
||||||
for input in ["val = foo(x, y: z)", "val = foo(y: z)"] {
|
for input in ["val = foo(x, y: z)", "val = foo(y: z)"] {
|
||||||
let module_id = ModuleId::default();
|
let module_id = ModuleId::default();
|
||||||
let tokens = crate::token::lexer(input, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer(input, module_id).unwrap();
|
||||||
let sl = &tokens;
|
let sl = &tokens;
|
||||||
super::program.parse(sl).unwrap();
|
super::program.parse(sl).unwrap();
|
||||||
}
|
}
|
||||||
@ -3900,7 +3902,6 @@ int(42.3)"#;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod snapshot_math_tests {
|
mod snapshot_math_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ModuleId;
|
|
||||||
|
|
||||||
// This macro generates a test function with the given function name.
|
// This macro generates a test function with the given function name.
|
||||||
// The macro takes a KCL program, ensures it tokenizes and parses, then compares
|
// The macro takes a KCL program, ensures it tokenizes and parses, then compares
|
||||||
@ -3909,8 +3910,8 @@ mod snapshot_math_tests {
|
|||||||
($func_name:ident, $test_kcl_program:expr) => {
|
($func_name:ident, $test_kcl_program:expr) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn $func_name() {
|
fn $func_name() {
|
||||||
let module_id = ModuleId::default();
|
let module_id = crate::ModuleId::default();
|
||||||
let tokens = crate::token::lexer($test_kcl_program, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer($test_kcl_program, module_id).unwrap();
|
||||||
ParseContext::init();
|
ParseContext::init();
|
||||||
|
|
||||||
let actual = match binary_expression.parse(&tokens) {
|
let actual = match binary_expression.parse(&tokens) {
|
||||||
@ -3939,7 +3940,6 @@ mod snapshot_math_tests {
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod snapshot_tests {
|
mod snapshot_tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::ModuleId;
|
|
||||||
|
|
||||||
// This macro generates a test function with the given function name.
|
// This macro generates a test function with the given function name.
|
||||||
// The macro takes a KCL program, ensures it tokenizes and parses, then compares
|
// The macro takes a KCL program, ensures it tokenizes and parses, then compares
|
||||||
@ -3948,8 +3948,8 @@ mod snapshot_tests {
|
|||||||
($func_name:ident, $test_kcl_program:expr) => {
|
($func_name:ident, $test_kcl_program:expr) => {
|
||||||
#[test]
|
#[test]
|
||||||
fn $func_name() {
|
fn $func_name() {
|
||||||
let module_id = ModuleId::default();
|
let module_id = crate::ModuleId::default();
|
||||||
let tokens = crate::token::lexer($test_kcl_program, module_id).unwrap();
|
let tokens = crate::parsing::token::lexer($test_kcl_program, module_id).unwrap();
|
||||||
print_tokens(&tokens);
|
print_tokens(&tokens);
|
||||||
ParseContext::init();
|
ParseContext::init();
|
||||||
let actual = match program.parse(&tokens) {
|
let actual = match program.parse(&tokens) {
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3609
|
assertion_line: 3851
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3610
|
assertion_line: 3852
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3611
|
assertion_line: 3853
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3612
|
assertion_line: 3854
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3613
|
assertion_line: 3855
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3614
|
assertion_line: 3856
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3615
|
assertion_line: 3857
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3616
|
assertion_line: 3858
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3617
|
assertion_line: 3859
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3618
|
assertion_line: 3860
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3619
|
assertion_line: 3861
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3893
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3963
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3964
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3721
|
assertion_line: 3965
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3966
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3974
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3981
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3989
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3993
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3994
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3995
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3996
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3997
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3998
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 3999
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 4000
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 4001
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 4002
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,6 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
assertion_line: 3759
|
assertion_line: 4003
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|
@ -1,5 +1,6 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parsing/parser.rs
|
||||||
|
assertion_line: 4004
|
||||||
expression: actual
|
expression: actual
|
||||||
snapshot_kind: text
|
snapshot_kind: text
|
||||||
---
|
---
|