Merge remote-tracking branch 'origin/main' into paultag/refgraph
@ -101,7 +101,6 @@ layout: manual
|
||||
* [`sin`](kcl/sin)
|
||||
* [`sqrt`](kcl/sqrt)
|
||||
* [`startProfileAt`](kcl/startProfileAt)
|
||||
* [`startSketchAt`](kcl/startSketchAt)
|
||||
* [`startSketchOn`](kcl/startSketchOn)
|
||||
* [`sweep`](kcl/sweep)
|
||||
* [`tan`](kcl/tan)
|
||||
|
||||
@ -9,7 +9,7 @@ Create a 3D surface or solid by interpolating between two or more sketches.
|
||||
The sketches need to closed and on the same plane.
|
||||
|
||||
```js
|
||||
loft(sketches: [Sketch], v_degree: NonZeroU32, bez_approximate_rational: bool, base_curve_index?: u32, tolerance?: number) -> Solid
|
||||
loft(sketches: [Sketch], v_degree: NonZeroU32, bez_approximate_rational: bool, base_curve_index?: integer, tolerance?: number) -> Solid
|
||||
```
|
||||
|
||||
|
||||
@ -20,7 +20,7 @@ loft(sketches: [Sketch], v_degree: NonZeroU32, bez_approximate_rational: bool, b
|
||||
| `sketches` | [`[Sketch]`](/docs/kcl/types/Sketch) | Which sketches to loft. Must include at least 2 sketches. | Yes |
|
||||
| `v_degree` | `NonZeroU32` | Degree of the interpolation. Must be greater than zero. For example, use 2 for quadratic, or 3 for cubic interpolation in the V direction. This defaults to 2, if not specified. | Yes |
|
||||
| `bez_approximate_rational` | `bool` | Attempt to approximate rational curves (such as arcs) using a bezier. This will remove banding around interpolations between arcs and non-arcs. It may produce errors in other scenarios Over time, this field won't be necessary. | Yes |
|
||||
| `base_curve_index` | `u32` | This can be set to override the automatically determined topological base curve, which is usually the first section encountered. | No |
|
||||
| `base_curve_index` | `integer` | This can be set to override the automatically determined topological base curve, which is usually the first section encountered. | No |
|
||||
| `tolerance` | `number` | Tolerance for the loft operation. | No |
|
||||
|
||||
### Returns
|
||||
|
||||
@ -35,7 +35,7 @@ The transform function returns a transform object. All properties of the object
|
||||
- `rotation.origin` (either "local" i.e. rotate around its own center, "global" i.e. rotate around the scene's center, or a 3D point, defaults to "local")
|
||||
|
||||
```js
|
||||
patternTransform(total_instances: u32, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid]
|
||||
patternTransform(total_instances: integer, transform_function: FunctionParam, solid_set: SolidSet) -> [Solid]
|
||||
```
|
||||
|
||||
|
||||
@ -43,7 +43,7 @@ patternTransform(total_instances: u32, transform_function: FunctionParam, solid_
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `total_instances` | `u32` | | Yes |
|
||||
| `total_instances` | `integer` | | Yes |
|
||||
| `transform_function` | `FunctionParam` | | Yes |
|
||||
| `solid_set` | [`SolidSet`](/docs/kcl/types/SolidSet) | A solid or a group of solids. | Yes |
|
||||
|
||||
@ -95,7 +95,8 @@ fn cube(length, center) {
|
||||
p2 = [l + x, l + y]
|
||||
p3 = [l + x, -l + y]
|
||||
|
||||
return startSketchAt(p0)
|
||||
return startSketchOn('XY')
|
||||
|> startProfileAt(p0, %)
|
||||
|> lineTo(p1, %)
|
||||
|> lineTo(p2, %)
|
||||
|> lineTo(p3, %)
|
||||
@ -132,7 +133,8 @@ fn cube(length, center) {
|
||||
p2 = [l + x, l + y]
|
||||
p3 = [l + x, -l + y]
|
||||
|
||||
return startSketchAt(p0)
|
||||
return startSketchOn('XY')
|
||||
|> startProfileAt(p0, %)
|
||||
|> lineTo(p1, %)
|
||||
|> lineTo(p2, %)
|
||||
|> lineTo(p3, %)
|
||||
@ -195,7 +197,8 @@ fn transform(i) {
|
||||
{ rotation = { angle = 45 * i } }
|
||||
]
|
||||
}
|
||||
startSketchAt([0, 0])
|
||||
startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> polygon({
|
||||
radius = 10,
|
||||
numSides = 4,
|
||||
|
||||
@ -9,7 +9,7 @@ Just like patternTransform, but works on 2D sketches not 3D solids.
|
||||
|
||||
|
||||
```js
|
||||
patternTransform2d(total_instances: u32, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
|
||||
patternTransform2d(total_instances: integer, transform_function: FunctionParam, solid_set: SketchSet) -> [Sketch]
|
||||
```
|
||||
|
||||
|
||||
@ -17,7 +17,7 @@ patternTransform2d(total_instances: u32, transform_function: FunctionParam, soli
|
||||
|
||||
| Name | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `total_instances` | `u32` | | Yes |
|
||||
| `total_instances` | `integer` | | Yes |
|
||||
| `transform_function` | `FunctionParam` | | Yes |
|
||||
| `solid_set` | [`SketchSet`](/docs/kcl/types/SketchSet) | A sketch or a group of sketches. | Yes |
|
||||
|
||||
|
||||
@ -79,10 +79,11 @@ fn decagon(radius) {
|
||||
stepAngle = 1 / 10 * tau()
|
||||
|
||||
// Start the decagon sketch at this point.
|
||||
startOfDecagonSketch = startSketchAt([cos(0) * radius, sin(0) * radius])
|
||||
startOfDecagonSketch = startSketchOn('XY')
|
||||
|> startProfileAt([cos(0) * radius, sin(0) * radius], %)
|
||||
|
||||
// Use a `reduce` to draw the remaining decagon sides.
|
||||
// For each number in the array 1..10, run the given function,
|
||||
// Use a `reduce` to draw the remaining decagon sides.
|
||||
// For each number in the array 1..10, run the given function,
|
||||
// which takes a partially-sketched decagon and adds one more edge to it.
|
||||
fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) {
|
||||
// Draw one edge of the decagon.
|
||||
@ -97,7 +98,8 @@ fn decagon(radius) {
|
||||
/* The `decagon` above is basically like this pseudo-code:
|
||||
fn decagon(radius):
|
||||
stepAngle = (1/10) * tau()
|
||||
startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])
|
||||
plane = startSketchOn('XY')
|
||||
startOfDecagonSketch = startProfileAt([(cos(0)*radius), (sin(0) * radius)], plane)
|
||||
|
||||
// Here's the reduce part.
|
||||
partialDecagon = startOfDecagonSketch
|
||||
|
||||
@ -28,7 +28,8 @@ segEnd(tag: TagIdentifier) -> [number]
|
||||
|
||||
```js
|
||||
w = 15
|
||||
cube = startSketchAt([0, 0])
|
||||
cube = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([w, 0], %, $line1)
|
||||
|> line([0, w], %, $line2)
|
||||
|> line([-w, 0], %, $line3)
|
||||
@ -37,7 +38,8 @@ cube = startSketchAt([0, 0])
|
||||
|> extrude(5, %)
|
||||
|
||||
fn cylinder(radius, tag) {
|
||||
return startSketchAt([0, 0])
|
||||
return startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> circle({
|
||||
radius = radius,
|
||||
center = segEnd(tag)
|
||||
|
||||
@ -28,7 +28,8 @@ segStart(tag: TagIdentifier) -> [number]
|
||||
|
||||
```js
|
||||
w = 15
|
||||
cube = startSketchAt([0, 0])
|
||||
cube = startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> line([w, 0], %, $line1)
|
||||
|> line([0, w], %, $line2)
|
||||
|> line([-w, 0], %, $line3)
|
||||
@ -37,7 +38,8 @@ cube = startSketchAt([0, 0])
|
||||
|> extrude(5, %)
|
||||
|
||||
fn cylinder(radius, tag) {
|
||||
return startSketchAt([0, 0])
|
||||
return startSketchOn('XY')
|
||||
|> startProfileAt([0, 0], %)
|
||||
|> circle({
|
||||
radius = radius,
|
||||
center = segStart(tag)
|
||||
|
||||
@ -4,6 +4,8 @@ excerpt: "Start a new 2-dimensional sketch at a given point on the 'XY' plane."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
**WARNING:** This function is deprecated.
|
||||
|
||||
Start a new 2-dimensional sketch at a given point on the 'XY' plane.
|
||||
|
||||
|
||||
|
||||
@ -97135,7 +97135,7 @@
|
||||
},
|
||||
{
|
||||
"name": "base_curve_index",
|
||||
"type": "u32",
|
||||
"type": "integer",
|
||||
"schema": {
|
||||
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
|
||||
"title": "Nullable_uint32",
|
||||
@ -101932,6 +101932,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -103313,6 +103338,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModuleId": {
|
||||
"description": "Identifier of a source file. Uses a u32 to keep the size small.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"KclNone": {
|
||||
"description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).",
|
||||
"type": "object",
|
||||
@ -103931,6 +103962,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -105312,6 +105368,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModuleId": {
|
||||
"description": "Identifier of a source file. Uses a u32 to keep the size small.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"KclNone": {
|
||||
"description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).",
|
||||
"type": "object",
|
||||
@ -105934,6 +105996,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -107315,6 +107402,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModuleId": {
|
||||
"description": "Identifier of a source file. Uses a u32 to keep the size small.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"KclNone": {
|
||||
"description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).",
|
||||
"type": "object",
|
||||
@ -122705,7 +122798,7 @@
|
||||
"args": [
|
||||
{
|
||||
"name": "total_instances",
|
||||
"type": "u32",
|
||||
"type": "integer",
|
||||
"schema": {
|
||||
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
|
||||
"title": "uint32",
|
||||
@ -125467,10 +125560,10 @@
|
||||
"examples": [
|
||||
"// Each instance will be shifted along the X axis.\nfn transform(id) {\n return { translate = [4 * id, 0, 0] }\n}\n\n// Sketch 4 cylinders.\nsketch001 = startSketchOn('XZ')\n |> circle({ center = [0, 0], radius = 2 }, %)\n |> extrude(5, %)\n |> patternTransform(4, transform, %)",
|
||||
"// Each instance will be shifted along the X axis,\n// with a gap between the original (at x = 0) and the first replica\n// (at x = 8). This is because `id` starts at 1.\nfn transform(id) {\n return { translate = [4 * (1 + id), 0, 0] }\n}\n\nsketch001 = startSketchOn('XZ')\n |> circle({ center = [0, 0], radius = 2 }, %)\n |> extrude(5, %)\n |> patternTransform(4, transform, %)",
|
||||
"fn cube(length, center) {\n l = length / 2\n x = center[0]\n y = center[1]\n p0 = [-l + x, -l + y]\n p1 = [-l + x, l + y]\n p2 = [l + x, l + y]\n p3 = [l + x, -l + y]\n\n return startSketchAt(p0)\n |> lineTo(p1, %)\n |> lineTo(p2, %)\n |> lineTo(p3, %)\n |> lineTo(p0, %)\n |> close(%)\n |> extrude(length, %)\n}\n\nwidth = 20\nfn transform(i) {\n return {\n // Move down each time.\n translate = [0, 0, -i * width],\n // Make the cube longer, wider and flatter each time.\n scale = [pow(1.1, i), pow(1.1, i), pow(0.9, i)],\n // Turn by 15 degrees each time.\n rotation = { angle = 15 * i, origin = \"local\" }\n }\n}\n\nmyCubes = cube(width, [100, 0])\n |> patternTransform(25, transform, %)",
|
||||
"fn cube(length, center) {\n l = length / 2\n x = center[0]\n y = center[1]\n p0 = [-l + x, -l + y]\n p1 = [-l + x, l + y]\n p2 = [l + x, l + y]\n p3 = [l + x, -l + y]\n\n return startSketchAt(p0)\n |> lineTo(p1, %)\n |> lineTo(p2, %)\n |> lineTo(p3, %)\n |> lineTo(p0, %)\n |> close(%)\n |> extrude(length, %)\n}\n\nwidth = 20\nfn transform(i) {\n return {\n translate = [0, 0, -i * width],\n rotation = {\n angle = 90 * i,\n // Rotate around the overall scene's origin.\n origin = \"global\"\n }\n }\n}\nmyCubes = cube(width, [100, 100])\n |> patternTransform(4, transform, %)",
|
||||
"fn cube(length, center) {\n l = length / 2\n x = center[0]\n y = center[1]\n p0 = [-l + x, -l + y]\n p1 = [-l + x, l + y]\n p2 = [l + x, l + y]\n p3 = [l + x, -l + y]\n\n return startSketchOn('XY')\n |> startProfileAt(p0, %)\n |> lineTo(p1, %)\n |> lineTo(p2, %)\n |> lineTo(p3, %)\n |> lineTo(p0, %)\n |> close(%)\n |> extrude(length, %)\n}\n\nwidth = 20\nfn transform(i) {\n return {\n // Move down each time.\n translate = [0, 0, -i * width],\n // Make the cube longer, wider and flatter each time.\n scale = [pow(1.1, i), pow(1.1, i), pow(0.9, i)],\n // Turn by 15 degrees each time.\n rotation = { angle = 15 * i, origin = \"local\" }\n }\n}\n\nmyCubes = cube(width, [100, 0])\n |> patternTransform(25, transform, %)",
|
||||
"fn cube(length, center) {\n l = length / 2\n x = center[0]\n y = center[1]\n p0 = [-l + x, -l + y]\n p1 = [-l + x, l + y]\n p2 = [l + x, l + y]\n p3 = [l + x, -l + y]\n\n return startSketchOn('XY')\n |> startProfileAt(p0, %)\n |> lineTo(p1, %)\n |> lineTo(p2, %)\n |> lineTo(p3, %)\n |> lineTo(p0, %)\n |> close(%)\n |> extrude(length, %)\n}\n\nwidth = 20\nfn transform(i) {\n return {\n translate = [0, 0, -i * width],\n rotation = {\n angle = 90 * i,\n // Rotate around the overall scene's origin.\n origin = \"global\"\n }\n }\n}\nmyCubes = cube(width, [100, 100])\n |> patternTransform(4, transform, %)",
|
||||
"// Parameters\nr = 50 // base radius\nh = 10 // layer height\nt = 0.005 // taper factor [0-1)\n// Defines how to modify each layer of the vase.\n// Each replica is shifted up the Z axis, and has a smoothly-varying radius\nfn transform(replicaId) {\n scale = r * abs(1 - (t * replicaId)) * (5 + cos(replicaId / 8))\n return {\n translate = [0, 0, replicaId * 10],\n scale = [scale, scale, 0]\n }\n}\n// Each layer is just a pretty thin cylinder.\nfn layer() {\n return startSketchOn(\"XY\")\n // or some other plane idk\n |> circle({ center = [0, 0], radius = 1 }, %, $tag1)\n |> extrude(h, %)\n}\n// The vase is 100 layers tall.\n// The 100 layers are replica of each other, with a slight transformation applied to each.\nvase = layer()\n |> patternTransform(100, transform, %)",
|
||||
"fn transform(i) {\n // Transform functions can return multiple transforms. They'll be applied in order.\n return [\n { translate = [30 * i, 0, 0] },\n { rotation = { angle = 45 * i } }\n ]\n}\nstartSketchAt([0, 0])\n |> polygon({\n radius = 10,\n numSides = 4,\n center = [0, 0],\n inscribed = false\n }, %)\n |> extrude(4, %)\n |> patternTransform(3, transform, %)"
|
||||
"fn transform(i) {\n // Transform functions can return multiple transforms. They'll be applied in order.\n return [\n { translate = [30 * i, 0, 0] },\n { rotation = { angle = 45 * i } }\n ]\n}\nstartSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> polygon({\n radius = 10,\n numSides = 4,\n center = [0, 0],\n inscribed = false\n }, %)\n |> extrude(4, %)\n |> patternTransform(3, transform, %)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -125482,7 +125575,7 @@
|
||||
"args": [
|
||||
{
|
||||
"name": "total_instances",
|
||||
"type": "u32",
|
||||
"type": "integer",
|
||||
"schema": {
|
||||
"$schema": "https://spec.openapis.org/oas/3.0/schema/2019-04-02#/definitions/Schema",
|
||||
"title": "uint32",
|
||||
@ -137258,6 +137351,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -138639,6 +138757,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModuleId": {
|
||||
"description": "Identifier of a source file. Uses a u32 to keep the size small.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"KclNone": {
|
||||
"description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).",
|
||||
"type": "object",
|
||||
@ -139254,6 +139378,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -139869,6 +140018,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -141250,6 +141424,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModuleId": {
|
||||
"description": "Identifier of a source file. Uses a u32 to keep the size small.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"KclNone": {
|
||||
"description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).",
|
||||
"type": "object",
|
||||
@ -141866,6 +142046,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -142508,6 +142713,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -143862,6 +144092,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModuleId": {
|
||||
"description": "Identifier of a source file. Uses a u32 to keep the size small.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"KclNone": {
|
||||
"description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).",
|
||||
"type": "object",
|
||||
@ -144496,6 +144732,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -145877,6 +146138,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModuleId": {
|
||||
"description": "Identifier of a source file. Uses a u32 to keep the size small.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"KclNone": {
|
||||
"description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).",
|
||||
"type": "object",
|
||||
@ -146492,6 +146759,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -147107,6 +147399,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -148488,6 +148805,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModuleId": {
|
||||
"description": "Identifier of a source file. Uses a u32 to keep the size small.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"KclNone": {
|
||||
"description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).",
|
||||
"type": "object",
|
||||
@ -149106,6 +149429,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -150487,6 +150835,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModuleId": {
|
||||
"description": "Identifier of a source file. Uses a u32 to keep the size small.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"KclNone": {
|
||||
"description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).",
|
||||
"type": "object",
|
||||
@ -151103,6 +151457,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -151745,6 +152124,31 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
"__meta",
|
||||
"type",
|
||||
"value"
|
||||
],
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"Module"
|
||||
]
|
||||
},
|
||||
"value": {
|
||||
"$ref": "#/components/schemas/ModuleId"
|
||||
},
|
||||
"__meta": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/Metadata"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "object",
|
||||
"required": [
|
||||
@ -153099,6 +153503,12 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"ModuleId": {
|
||||
"description": "Identifier of a source file. Uses a u32 to keep the size small.",
|
||||
"type": "integer",
|
||||
"format": "uint32",
|
||||
"minimum": 0.0
|
||||
},
|
||||
"KclNone": {
|
||||
"description": "KCL value for an optional parameter which was not given an argument. (remember, parameters are in the function declaration, arguments are in the function call/application).",
|
||||
"type": "object",
|
||||
@ -153126,7 +153536,7 @@
|
||||
"examples": [
|
||||
"// This function adds two numbers.\nfn add(a, b) {\n return a + b\n}\n\n// This function adds an array of numbers.\n// It uses the `reduce` function, to call the `add` function on every\n// element of the `arr` parameter. The starting value is 0.\nfn sum(arr) {\n return reduce(arr, 0, add)\n}\n\n/* The above is basically like this pseudo-code:\nfn sum(arr):\n sumSoFar = 0\n for i in arr:\n sumSoFar = add(sumSoFar, i)\n return sumSoFar */\n\n\n// We use `assertEqual` to check that our `sum` function gives the\n// expected result. It's good to check your work!\nassertEqual(sum([1, 2, 3]), 6, 0.00001, \"1 + 2 + 3 summed is 6\")",
|
||||
"// This example works just like the previous example above, but it uses\n// an anonymous `add` function as its parameter, instead of declaring a\n// named function outside.\narr = [1, 2, 3]\nsum = reduce(arr, 0, fn(i, result_so_far) {\n return i + result_so_far\n})\n\n// We use `assertEqual` to check that our `sum` function gives the\n// expected result. It's good to check your work!\nassertEqual(sum, 6, 0.00001, \"1 + 2 + 3 summed is 6\")",
|
||||
"// Declare a function that sketches a decagon.\nfn decagon(radius) {\n // Each side of the decagon is turned this many degrees from the previous angle.\n stepAngle = 1 / 10 * tau()\n\n // Start the decagon sketch at this point.\n startOfDecagonSketch = startSketchAt([cos(0) * radius, sin(0) * radius])\n\n // Use a `reduce` to draw the remaining decagon sides.\n // For each number in the array 1..10, run the given function,\n // which takes a partially-sketched decagon and adds one more edge to it.\n fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) {\n // Draw one edge of the decagon.\n x = cos(stepAngle * i) * radius\n y = sin(stepAngle * i) * radius\n return lineTo([x, y], partialDecagon)\n })\n\n return fullDecagon\n}\n\n/* The `decagon` above is basically like this pseudo-code:\nfn decagon(radius):\n stepAngle = (1/10) * tau()\n startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])\n\n // Here's the reduce part.\n partialDecagon = startOfDecagonSketch\n for i in [1..10]:\n x = cos(stepAngle * i) * radius\n y = sin(stepAngle * i) * radius\n partialDecagon = lineTo([x, y], partialDecagon)\n fullDecagon = partialDecagon // it's now full\n return fullDecagon */\n\n\n// Use the `decagon` function declared above, to sketch a decagon with radius 5.\ndecagon(5.0)\n |> close(%)"
|
||||
"// Declare a function that sketches a decagon.\nfn decagon(radius) {\n // Each side of the decagon is turned this many degrees from the previous angle.\n stepAngle = 1 / 10 * tau()\n\n // Start the decagon sketch at this point.\n startOfDecagonSketch = startSketchOn('XY')\n |> startProfileAt([cos(0) * radius, sin(0) * radius], %)\n\n // Use a `reduce` to draw the remaining decagon sides.\n // For each number in the array 1..10, run the given function,\n // which takes a partially-sketched decagon and adds one more edge to it.\n fullDecagon = reduce([1..10], startOfDecagonSketch, fn(i, partialDecagon) {\n // Draw one edge of the decagon.\n x = cos(stepAngle * i) * radius\n y = sin(stepAngle * i) * radius\n return lineTo([x, y], partialDecagon)\n })\n\n return fullDecagon\n}\n\n/* The `decagon` above is basically like this pseudo-code:\nfn decagon(radius):\n stepAngle = (1/10) * tau()\n plane = startSketchOn('XY')\n startOfDecagonSketch = startProfileAt([(cos(0)*radius), (sin(0) * radius)], plane)\n\n // Here's the reduce part.\n partialDecagon = startOfDecagonSketch\n for i in [1..10]:\n x = cos(stepAngle * i) * radius\n y = sin(stepAngle * i) * radius\n partialDecagon = lineTo([x, y], partialDecagon)\n fullDecagon = partialDecagon // it's now full\n return fullDecagon */\n\n\n// Use the `decagon` function declared above, to sketch a decagon with radius 5.\ndecagon(5.0)\n |> close(%)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -158942,7 +159352,7 @@
|
||||
"unpublished": false,
|
||||
"deprecated": false,
|
||||
"examples": [
|
||||
"w = 15\ncube = startSketchAt([0, 0])\n |> line([w, 0], %, $line1)\n |> line([0, w], %, $line2)\n |> line([-w, 0], %, $line3)\n |> line([0, -w], %, $line4)\n |> close(%)\n |> extrude(5, %)\n\nfn cylinder(radius, tag) {\n return startSketchAt([0, 0])\n |> circle({\n radius = radius,\n center = segEnd(tag)\n }, %)\n |> extrude(radius, %)\n}\n\ncylinder(1, line1)\ncylinder(2, line2)\ncylinder(3, line3)\ncylinder(4, line4)"
|
||||
"w = 15\ncube = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([w, 0], %, $line1)\n |> line([0, w], %, $line2)\n |> line([-w, 0], %, $line3)\n |> line([0, -w], %, $line4)\n |> close(%)\n |> extrude(5, %)\n\nfn cylinder(radius, tag) {\n return startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> circle({\n radius = radius,\n center = segEnd(tag)\n }, %)\n |> extrude(radius, %)\n}\n\ncylinder(1, line1)\ncylinder(2, line2)\ncylinder(3, line3)\ncylinder(4, line4)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -162571,7 +162981,7 @@
|
||||
"unpublished": false,
|
||||
"deprecated": false,
|
||||
"examples": [
|
||||
"w = 15\ncube = startSketchAt([0, 0])\n |> line([w, 0], %, $line1)\n |> line([0, w], %, $line2)\n |> line([-w, 0], %, $line3)\n |> line([0, -w], %, $line4)\n |> close(%)\n |> extrude(5, %)\n\nfn cylinder(radius, tag) {\n return startSketchAt([0, 0])\n |> circle({\n radius = radius,\n center = segStart(tag)\n }, %)\n |> extrude(radius, %)\n}\n\ncylinder(1, line1)\ncylinder(2, line2)\ncylinder(3, line3)\ncylinder(4, line4)"
|
||||
"w = 15\ncube = startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> line([w, 0], %, $line1)\n |> line([0, w], %, $line2)\n |> line([-w, 0], %, $line3)\n |> line([0, -w], %, $line4)\n |> close(%)\n |> extrude(5, %)\n\nfn cylinder(radius, tag) {\n return startSketchOn('XY')\n |> startProfileAt([0, 0], %)\n |> circle({\n radius = radius,\n center = segStart(tag)\n }, %)\n |> extrude(radius, %)\n}\n\ncylinder(1, line1)\ncylinder(2, line2)\ncylinder(3, line3)\ncylinder(4, line4)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@ -173863,7 +174273,7 @@
|
||||
"labelRequired": true
|
||||
},
|
||||
"unpublished": false,
|
||||
"deprecated": false,
|
||||
"deprecated": true,
|
||||
"examples": [
|
||||
"exampleSketch = startSketchAt([0, 0])\n |> line([10, 0], %)\n |> line([0, 10], %)\n |> line([-10, 0], %)\n |> close(%)\n\nexample = extrude(5, exampleSketch)",
|
||||
"exampleSketch = startSketchAt([10, 10])\n |> line([10, 0], %)\n |> line([0, 10], %)\n |> line([-10, 0], %)\n |> close(%)\n\nexample = extrude(5, exampleSketch)",
|
||||
|
||||
@ -13,13 +13,18 @@ Data to draw an angled line.
|
||||
|
||||
An angle and length with explicitly named parameters
|
||||
|
||||
[`PolarCoordsData`](/docs/kcl/types/PolarCoordsData)
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `angle` |`number`| The angle of the line (in degrees). | No |
|
||||
| `length` |`number`| The length of the line. | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
@ -329,6 +329,23 @@ Data for an imported geometry.
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|----------|------|-------------|----------|
|
||||
| `type` |enum: `Module`| | No |
|
||||
| `value` |[`ModuleId`](/docs/kcl/types/ModuleId)| Any KCL value. | No |
|
||||
| `__meta` |`[` [`Metadata`](/docs/kcl/types/Metadata) `]`| | No |
|
||||
|
||||
|
||||
----
|
||||
|
||||
**Type:** `object`
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description | Required |
|
||||
|
||||
16
docs/kcl/types/ModuleId.md
Normal file
@ -0,0 +1,16 @@
|
||||
---
|
||||
title: "ModuleId"
|
||||
excerpt: "Identifier of a source file. Uses a u32 to keep the size small."
|
||||
layout: manual
|
||||
---
|
||||
|
||||
Identifier of a source file. Uses a u32 to keep the size small.
|
||||
|
||||
**Type:** `integer` (`uint32`)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 49 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 54 KiB After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 144 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 124 KiB After Width: | Height: | Size: 124 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 40 KiB |
@ -1,6 +1,17 @@
|
||||
import { test, expect } from '@playwright/test'
|
||||
|
||||
import { getUtils, setup, tearDown, TEST_COLORS } from './test-utils'
|
||||
import {
|
||||
test as testFixture,
|
||||
expect as expectFixture,
|
||||
} from './fixtures/fixtureSetup'
|
||||
import { join } from 'path'
|
||||
import {
|
||||
getUtils,
|
||||
setup,
|
||||
tearDown,
|
||||
TEST_COLORS,
|
||||
executorInputPath,
|
||||
} from './test-utils'
|
||||
import * as fsp from 'fs/promises'
|
||||
import { XOR } from 'lib/utils'
|
||||
|
||||
test.beforeEach(async ({ context, page }, testInfo) => {
|
||||
@ -1028,3 +1039,58 @@ part002 = startSketchOn('XZ')
|
||||
await expect(page.getByTestId('segment-overlay')).toHaveCount(2)
|
||||
})
|
||||
})
|
||||
testFixture.describe('Electron constraint tests', () => {
|
||||
testFixture(
|
||||
'Able to double click label to set constraint',
|
||||
{ tag: '@electron' },
|
||||
async ({ tronApp, homePage, scene, editor, toolbar }) => {
|
||||
await tronApp.initialise({
|
||||
fixtures: { homePage, scene, editor, toolbar },
|
||||
folderSetupFn: async (dir) => {
|
||||
const bracketDir = join(dir, 'test-sample')
|
||||
await fsp.mkdir(bracketDir, { recursive: true })
|
||||
await fsp.copyFile(
|
||||
executorInputPath('angled_line.kcl'),
|
||||
join(bracketDir, 'main.kcl')
|
||||
)
|
||||
},
|
||||
})
|
||||
const [clickHandler] = scene.makeMouseHelpers(600, 300)
|
||||
|
||||
await test.step('setup test', async () => {
|
||||
await homePage.expectState({
|
||||
projectCards: [
|
||||
{
|
||||
title: 'test-sample',
|
||||
fileCount: 1,
|
||||
},
|
||||
],
|
||||
sortBy: 'last-modified-desc',
|
||||
})
|
||||
await homePage.openProject('test-sample')
|
||||
await scene.waitForExecutionDone()
|
||||
})
|
||||
|
||||
await test.step('Double click to constrain', async () => {
|
||||
await clickHandler()
|
||||
await tronApp.page.getByRole('button', { name: 'Edit Sketch' }).click()
|
||||
const child = tronApp.page
|
||||
.locator('.segment-length-label-text')
|
||||
.first()
|
||||
.locator('xpath=..')
|
||||
await child.dblclick()
|
||||
const cmdBarSubmitButton = tronApp.page.getByRole('button', {
|
||||
name: 'arrow right Continue',
|
||||
})
|
||||
await cmdBarSubmitButton.click()
|
||||
await expectFixture(tronApp.page.locator('.cm-content')).toContainText(
|
||||
'length001 = 15.3'
|
||||
)
|
||||
await expectFixture(tronApp.page.locator('.cm-content')).toContainText(
|
||||
'|> angledLine([9, length001], %)'
|
||||
)
|
||||
await tronApp.page.getByRole('button', { name: 'Exit Sketch' }).click()
|
||||
})
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
@ -174,8 +174,13 @@ export const ClientSideScene = ({
|
||||
const Overlays = () => {
|
||||
const { context } = useModelingContext()
|
||||
if (context.mouseState.type === 'isDragging') return null
|
||||
// Set a large zIndex, the overlay for hover dropdown menu on line segments needs to render
|
||||
// over the length labels on the line segments
|
||||
return (
|
||||
<div className="absolute inset-0 pointer-events-none">
|
||||
<div
|
||||
className="absolute inset-0 pointer-events-none"
|
||||
style={{ zIndex: '99999999' }}
|
||||
>
|
||||
{Object.entries(context.segmentOverlays)
|
||||
.filter((a) => a[1].visible)
|
||||
.map(([pathToNodeString, overlay], index) => {
|
||||
|
||||
@ -51,6 +51,7 @@ import {
|
||||
defaultSourceRange,
|
||||
sourceRangeFromRust,
|
||||
resultIsOk,
|
||||
SourceRange,
|
||||
} from 'lang/wasm'
|
||||
import {
|
||||
engineCommandManager,
|
||||
@ -102,6 +103,7 @@ import { Point3d } from 'wasm-lib/kcl/bindings/Point3d'
|
||||
import { SegmentInputs } from 'lang/std/stdTypes'
|
||||
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
||||
import { radToDeg } from 'three/src/math/MathUtils'
|
||||
import { getArtifactFromRange, codeRefFromRange } from 'lang/std/artifactGraph'
|
||||
|
||||
type DraftSegment = 'line' | 'tangentialArcTo'
|
||||
|
||||
@ -584,11 +586,12 @@ export class SceneEntities {
|
||||
)
|
||||
|
||||
let seg: Group
|
||||
const _node1 = getNodeFromPath<CallExpression>(
|
||||
const _node1 = getNodeFromPath<Node<CallExpression>>(
|
||||
maybeModdedAst,
|
||||
segPathToNode,
|
||||
'CallExpression'
|
||||
)
|
||||
|
||||
if (err(_node1)) return
|
||||
const callExpName = _node1.node?.callee?.name
|
||||
|
||||
@ -611,6 +614,15 @@ export class SceneEntities {
|
||||
from: segment.from,
|
||||
to: segment.to,
|
||||
}
|
||||
|
||||
const startRange = _node1.node.start
|
||||
const endRange = _node1.node.end
|
||||
const sourceRange: SourceRange = [startRange, endRange, true]
|
||||
const selection: Selections = computeSelectionFromSourceRangeAndAST(
|
||||
sourceRange,
|
||||
maybeModdedAst
|
||||
)
|
||||
|
||||
const result = initSegment({
|
||||
prevSegment: sketch.paths[index - 1],
|
||||
callExpName,
|
||||
@ -623,6 +635,7 @@ export class SceneEntities {
|
||||
theme: sceneInfra._theme,
|
||||
isSelected,
|
||||
sceneInfra,
|
||||
selection,
|
||||
})
|
||||
if (err(result)) return
|
||||
const { group: _group, updateOverlaysCallback } = result
|
||||
@ -2351,3 +2364,27 @@ export function getQuaternionFromZAxis(zAxis: Vector3): Quaternion {
|
||||
function massageFormats(a: Vec3Array | Point3d): Vector3 {
|
||||
return isArray(a) ? new Vector3(a[0], a[1], a[2]) : new Vector3(a.x, a.y, a.z)
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a SourceRange [x,y,boolean] create a Selections object which contains
|
||||
* graphSelections with the artifact and codeRef.
|
||||
* This can be passed to 'Set selection' to internally set the selection of the
|
||||
* modelingMachine from code.
|
||||
*/
|
||||
function computeSelectionFromSourceRangeAndAST(
|
||||
sourceRange: SourceRange,
|
||||
ast: Node<Program>
|
||||
): Selections {
|
||||
const artifactGraph = engineCommandManager.artifactGraph
|
||||
const artifact = getArtifactFromRange(sourceRange, artifactGraph) || undefined
|
||||
const selection: Selections = {
|
||||
graphSelections: [
|
||||
{
|
||||
artifact,
|
||||
codeRef: codeRefFromRange(sourceRange, ast),
|
||||
},
|
||||
],
|
||||
otherSelections: [],
|
||||
}
|
||||
return selection
|
||||
}
|
||||
|
||||
@ -229,7 +229,6 @@ export class SceneInfra {
|
||||
const vector = new Vector3(0, 0, 0)
|
||||
|
||||
// Get the position of the object3D in world space
|
||||
// console.log('arrowGroup', arrowGroup)
|
||||
arrowGroup.getWorldPosition(vector)
|
||||
|
||||
// Project that position to screen space
|
||||
@ -347,7 +346,6 @@ export class SceneInfra {
|
||||
requestAnimationFrame(this.animate)
|
||||
TWEEN.update() // This will update all tweens during the animation loop
|
||||
if (!this.isFovAnimationInProgress) {
|
||||
// console.log('animation frame', this.cameraControls.camera)
|
||||
this.camControls.update()
|
||||
this.renderer.render(this.scene, this.camControls.camera)
|
||||
this.labelRenderer.render(this.scene, this.camControls.camera)
|
||||
@ -434,7 +432,6 @@ export class SceneInfra {
|
||||
if (!this.selected.hasBeenDragged && hasBeenDragged) {
|
||||
this.selected.hasBeenDragged = true
|
||||
// this is where we could fire a onDragStart event
|
||||
// console.log('onDragStart', this.selected)
|
||||
}
|
||||
if (
|
||||
hasBeenDragged &&
|
||||
|
||||
@ -56,6 +56,8 @@ import { normaliseAngle, roundOff } from 'lib/utils'
|
||||
import { SegmentOverlayPayload } from 'machines/modelingMachine'
|
||||
import { SegmentInputs } from 'lang/std/stdTypes'
|
||||
import { err } from 'lib/trap'
|
||||
import { editorManager, sceneInfra } from 'lib/singletons'
|
||||
import { Selections } from 'lib/selections'
|
||||
|
||||
interface CreateSegmentArgs {
|
||||
input: SegmentInputs
|
||||
@ -69,6 +71,7 @@ interface CreateSegmentArgs {
|
||||
theme: Themes
|
||||
isSelected?: boolean
|
||||
sceneInfra: SceneInfra
|
||||
selection?: Selections
|
||||
}
|
||||
|
||||
interface UpdateSegmentArgs {
|
||||
@ -118,6 +121,7 @@ class StraightSegment implements SegmentUtils {
|
||||
isSelected = false,
|
||||
sceneInfra,
|
||||
prevSegment,
|
||||
selection,
|
||||
}) => {
|
||||
if (input.type !== 'straight-segment')
|
||||
return new Error('Invalid segment type')
|
||||
@ -156,6 +160,7 @@ class StraightSegment implements SegmentUtils {
|
||||
isSelected,
|
||||
callExpName,
|
||||
baseColor,
|
||||
selection,
|
||||
}
|
||||
|
||||
// All segment types get an extra segment handle,
|
||||
@ -823,8 +828,37 @@ function createLengthIndicator({
|
||||
lengthIndicatorText.innerText = roundOff(length).toString()
|
||||
const lengthIndicatorWrapper = document.createElement('div')
|
||||
|
||||
// Double click workflow
|
||||
lengthIndicatorWrapper.ondblclick = () => {
|
||||
const selection = lengthIndicatorGroup.parent?.userData.selection
|
||||
if (!selection) {
|
||||
console.error('Unable to dimension segment when clicking the label.')
|
||||
return
|
||||
}
|
||||
sceneInfra.modelingSend({
|
||||
type: 'Set selection',
|
||||
data: {
|
||||
selectionType: 'singleCodeCursor',
|
||||
selection: selection.graphSelections[0],
|
||||
},
|
||||
})
|
||||
|
||||
// Command Bar
|
||||
editorManager.commandBarSend({
|
||||
type: 'Find and select command',
|
||||
data: {
|
||||
name: 'Constrain length',
|
||||
groupId: 'modeling',
|
||||
argDefaultValues: {
|
||||
selection,
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// Style the elements
|
||||
lengthIndicatorWrapper.style.position = 'absolute'
|
||||
lengthIndicatorWrapper.style.pointerEvents = 'auto'
|
||||
lengthIndicatorWrapper.appendChild(lengthIndicatorText)
|
||||
const cssObject = new CSS2DObject(lengthIndicatorWrapper)
|
||||
cssObject.name = SEGMENT_LENGTH_LABEL_TEXT
|
||||
|
||||
@ -109,6 +109,7 @@ function DisplayObj({
|
||||
setHasCursor(false)
|
||||
}
|
||||
}, [node.start, node.end, node.type])
|
||||
|
||||
return (
|
||||
<pre
|
||||
ref={ref}
|
||||
|
||||
@ -625,7 +625,6 @@ export const ModelingMachineProvider = ({
|
||||
}
|
||||
|
||||
const canShell = canShellSelection(selectionRanges)
|
||||
console.log('canShellSelection', canShellSelection(selectionRanges))
|
||||
if (err(canShell)) return false
|
||||
return canShell
|
||||
},
|
||||
|
||||
@ -12,6 +12,7 @@ export const kclHighlight = styleTags({
|
||||
'AddOp MultOp ExpOp': t.arithmeticOperator,
|
||||
BangOp: t.logicOperator,
|
||||
CompOp: t.compareOperator,
|
||||
LogicOp: t.logicOperator,
|
||||
'Equals Arrow': t.definitionOperator,
|
||||
PipeOperator: t.controlOperator,
|
||||
String: t.string,
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
mult @left
|
||||
add @left
|
||||
comp @left
|
||||
logic @left
|
||||
pipe @left
|
||||
range
|
||||
}
|
||||
@ -40,7 +41,8 @@ expression[@isGroup=Expression] {
|
||||
expression !add AddOp expression |
|
||||
expression !mult MultOp expression |
|
||||
expression !exp ExpOp expression |
|
||||
expression !comp CompOp expression
|
||||
expression !comp CompOp expression |
|
||||
expression !logic LogicOp expression
|
||||
} |
|
||||
UnaryExpression { UnaryOp expression } |
|
||||
ParenthesizedExpression { "(" expression ")" } |
|
||||
@ -89,6 +91,7 @@ commaSep1NoTrailingComma<term> { term ("," term)* }
|
||||
AddOp { "+" | "-" }
|
||||
MultOp { "/" | "*" | "\\" }
|
||||
ExpOp { "^" }
|
||||
LogicOp { "|" | "&" }
|
||||
BangOp { "!" }
|
||||
CompOp { "==" | "!=" | "<=" | ">=" | "<" | ">" }
|
||||
Equals { "=" }
|
||||
|
||||
@ -397,6 +397,7 @@ function moreNodePathFromSourceRange(
|
||||
}
|
||||
return path
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
console.error('not implemented: ' + node.type)
|
||||
|
||||
@ -871,3 +871,21 @@ export function codeRefFromRange(range: SourceRange, ast: Program): CodeRef {
|
||||
pathToNode: getNodePathFromSourceRange(ast, range),
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an artifact from a code source range
|
||||
*/
|
||||
export function getArtifactFromRange(
|
||||
range: SourceRange,
|
||||
artifactGraph: ArtifactGraph
|
||||
): Artifact | null {
|
||||
for (const artifact of artifactGraph.values()) {
|
||||
if ('codeRef' in artifact) {
|
||||
const match =
|
||||
artifact.codeRef.range[0] === range[0] &&
|
||||
artifact.codeRef.range[1] === range[1]
|
||||
if (match) return artifact
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@ -259,7 +259,7 @@ export function emptyExecState(): ExecState {
|
||||
|
||||
function execStateFromRaw(raw: RawExecState): ExecState {
|
||||
return {
|
||||
memory: ProgramMemory.fromRaw(raw.memory),
|
||||
memory: ProgramMemory.fromRaw(raw.modLocal.memory),
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -9,7 +9,7 @@ import { Selections } from 'lib/selections'
|
||||
import { kclManager } from 'lib/singletons'
|
||||
import { err } from 'lib/trap'
|
||||
import { modelingMachine, SketchTool } from 'machines/modelingMachine'
|
||||
import { revolveAxisValidator } from './validators'
|
||||
import { loftValidator, revolveAxisValidator } from './validators'
|
||||
|
||||
type OutputFormat = Models['OutputFormat_type']
|
||||
type OutputTypeKey = OutputFormat['type']
|
||||
@ -297,6 +297,7 @@ export const modelingMachineCommandConfig: StateMachineCommandSetConfig<
|
||||
multiple: true,
|
||||
required: true,
|
||||
skip: false,
|
||||
validation: loftValidator,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@ -104,3 +104,52 @@ export const revolveAxisValidator = async ({
|
||||
return 'Unable to revolve with selected axis'
|
||||
}
|
||||
}
|
||||
|
||||
export const loftValidator = async ({
|
||||
data,
|
||||
}: {
|
||||
data: { [key: string]: Selections }
|
||||
context: CommandBarContext
|
||||
}): Promise<boolean | string> => {
|
||||
if (!isSelections(data.selection)) {
|
||||
return 'Unable to loft, selections are missing'
|
||||
}
|
||||
const { selection } = data
|
||||
|
||||
if (selection.graphSelections.some((s) => s.artifact?.type !== 'solid2D')) {
|
||||
return 'Unable to loft, some selection are not solid2Ds'
|
||||
}
|
||||
|
||||
const sectionIds = data.selection.graphSelections.flatMap((s) =>
|
||||
s.artifact?.type === 'solid2D' ? s.artifact.pathId : []
|
||||
)
|
||||
|
||||
if (sectionIds.length < 2) {
|
||||
return 'Unable to loft, selection contains less than two solid2Ds'
|
||||
}
|
||||
|
||||
const loftCommand = async () => {
|
||||
// TODO: check what to do with these
|
||||
const DEFAULT_V_DEGREE = 2
|
||||
const DEFAULT_TOLERANCE = 2
|
||||
const DEFAULT_BEZ_APPROXIMATE_RATIONAL = false
|
||||
return await engineCommandManager.sendSceneCommand({
|
||||
type: 'modeling_cmd_req',
|
||||
cmd_id: uuidv4(),
|
||||
cmd: {
|
||||
section_ids: sectionIds,
|
||||
type: 'loft',
|
||||
bez_approximate_rational: DEFAULT_BEZ_APPROXIMATE_RATIONAL,
|
||||
tolerance: DEFAULT_TOLERANCE,
|
||||
v_degree: DEFAULT_V_DEGREE,
|
||||
},
|
||||
})
|
||||
}
|
||||
const attempt = await dryRunWrapper(loftCommand)
|
||||
if (attempt?.success) {
|
||||
return true
|
||||
} else {
|
||||
// return error message for the toast
|
||||
return 'Unable to loft with selected sketches'
|
||||
}
|
||||
}
|
||||
|
||||
@ -779,6 +779,8 @@ fn rust_type_to_openapi_type(t: &str) -> String {
|
||||
|
||||
if t == "f64" {
|
||||
return "number".to_string();
|
||||
} else if t == "u32" {
|
||||
return "integer".to_string();
|
||||
} else if t == "str" {
|
||||
return "string".to_string();
|
||||
} else {
|
||||
@ -803,7 +805,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
||||
|
||||
quote! {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn #test_name_mock() {
|
||||
async fn #test_name_mock() -> miette::Result<()> {
|
||||
let program = crate::Program::parse_no_errs(#code_block).unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await.unwrap())),
|
||||
@ -813,15 +815,33 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
|
||||
ctx.run(program.into(), &mut crate::ExecState::default()).await.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", #fn_name, #index),
|
||||
kcl_source: #code_block.to_string(),
|
||||
}));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn #test_name() {
|
||||
async fn #test_name() -> miette::Result<()> {
|
||||
let code = #code_block;
|
||||
// Note, `crate` must be kcl_lib
|
||||
let result = crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm, None).await.unwrap();
|
||||
let result = match crate::test_server::execute_and_snapshot(code, crate::settings::types::UnitLength::Mm, None).await {
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", #fn_name, #index),
|
||||
kcl_source: #code_block.to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err)=> panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(&format!("tests/outputs/{}.png", #output_test_name_str), &result, 0.99);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_someFn {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_someFn0() {
|
||||
async fn test_mock_example_someFn0() -> miette::Result<()> {
|
||||
let program = crate::Program::parse_no_errs("someFn()").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -14,26 +14,43 @@ mod test_examples_someFn {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "someFn", 0usize),
|
||||
kcl_source: "someFn()".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_someFn0() {
|
||||
async fn kcl_test_example_someFn0() -> miette::Result<()> {
|
||||
let code = "someFn()";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "someFn", 0usize),
|
||||
kcl_source: "someFn()".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_someFn0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_someFn {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_someFn0() {
|
||||
async fn test_mock_example_someFn0() -> miette::Result<()> {
|
||||
let program = crate::Program::parse_no_errs("someFn()").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -14,26 +14,43 @@ mod test_examples_someFn {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "someFn", 0usize),
|
||||
kcl_source: "someFn()".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_someFn0() {
|
||||
async fn kcl_test_example_someFn0() -> miette::Result<()> {
|
||||
let code = "someFn()";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "someFn", 0usize),
|
||||
kcl_source: "someFn()".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_someFn0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_show {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_show0() {
|
||||
async fn test_mock_example_show0() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is another code block.\nyes sirrr.\nshow").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -15,30 +15,47 @@ mod test_examples_show {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "show", 0usize),
|
||||
kcl_source: "This is another code block.\nyes sirrr.\nshow".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_show0() {
|
||||
async fn kcl_test_example_show0() -> miette::Result<()> {
|
||||
let code = "This is another code block.\nyes sirrr.\nshow";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "show", 0usize),
|
||||
kcl_source: "This is another code block.\nyes sirrr.\nshow".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_show1() {
|
||||
async fn test_mock_example_show1() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -52,26 +69,43 @@ mod test_examples_show {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "show", 1usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_show1() {
|
||||
async fn kcl_test_example_show1() -> miette::Result<()> {
|
||||
let code = "This is code.\nIt does other shit.\nshow";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "show", 1usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show1"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_show {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_show0() {
|
||||
async fn test_mock_example_show0() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -15,26 +15,43 @@ mod test_examples_show {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "show", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_show0() {
|
||||
async fn kcl_test_example_show0() -> miette::Result<()> {
|
||||
let code = "This is code.\nIt does other shit.\nshow";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "show", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_my_func {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_my_func0() {
|
||||
async fn test_mock_example_my_func0() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is another code block.\nyes sirrr.\nmyFunc")
|
||||
.unwrap();
|
||||
@ -16,30 +16,47 @@ mod test_examples_my_func {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "my_func", 0usize),
|
||||
kcl_source: "This is another code block.\nyes sirrr.\nmyFunc".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_my_func0() {
|
||||
async fn kcl_test_example_my_func0() -> miette::Result<()> {
|
||||
let code = "This is another code block.\nyes sirrr.\nmyFunc";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "my_func", 0usize),
|
||||
kcl_source: "This is another code block.\nyes sirrr.\nmyFunc".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_my_func0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_my_func1() {
|
||||
async fn test_mock_example_my_func1() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is code.\nIt does other shit.\nmyFunc").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -53,26 +70,43 @@ mod test_examples_my_func {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "my_func", 1usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nmyFunc".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_my_func1() {
|
||||
async fn kcl_test_example_my_func1() -> miette::Result<()> {
|
||||
let code = "This is code.\nIt does other shit.\nmyFunc";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "my_func", 1usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nmyFunc".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_my_func1"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_line_to {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_line_to0() {
|
||||
async fn test_mock_example_line_to0() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is another code block.\nyes sirrr.\nlineTo")
|
||||
.unwrap();
|
||||
@ -16,30 +16,47 @@ mod test_examples_line_to {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "line_to", 0usize),
|
||||
kcl_source: "This is another code block.\nyes sirrr.\nlineTo".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_line_to0() {
|
||||
async fn kcl_test_example_line_to0() -> miette::Result<()> {
|
||||
let code = "This is another code block.\nyes sirrr.\nlineTo";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "line_to", 0usize),
|
||||
kcl_source: "This is another code block.\nyes sirrr.\nlineTo".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_line_to0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_line_to1() {
|
||||
async fn test_mock_example_line_to1() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is code.\nIt does other shit.\nlineTo").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -53,26 +70,43 @@ mod test_examples_line_to {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "line_to", 1usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nlineTo".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_line_to1() {
|
||||
async fn kcl_test_example_line_to1() -> miette::Result<()> {
|
||||
let code = "This is code.\nIt does other shit.\nlineTo";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "line_to", 1usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nlineTo".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_line_to1"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_min {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_min0() {
|
||||
async fn test_mock_example_min0() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is another code block.\nyes sirrr.\nmin").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -15,30 +15,47 @@ mod test_examples_min {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "min", 0usize),
|
||||
kcl_source: "This is another code block.\nyes sirrr.\nmin".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_min0() {
|
||||
async fn kcl_test_example_min0() -> miette::Result<()> {
|
||||
let code = "This is another code block.\nyes sirrr.\nmin";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "min", 0usize),
|
||||
kcl_source: "This is another code block.\nyes sirrr.\nmin".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_min0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_min1() {
|
||||
async fn test_mock_example_min1() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is code.\nIt does other shit.\nmin").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -52,26 +69,43 @@ mod test_examples_min {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "min", 1usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nmin".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_min1() {
|
||||
async fn kcl_test_example_min1() -> miette::Result<()> {
|
||||
let code = "This is code.\nIt does other shit.\nmin";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "min", 1usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nmin".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_min1"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_show {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_show0() {
|
||||
async fn test_mock_example_show0() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -15,26 +15,43 @@ mod test_examples_show {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "show", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_show0() {
|
||||
async fn kcl_test_example_show0() -> miette::Result<()> {
|
||||
let code = "This is code.\nIt does other shit.\nshow";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "show", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_import {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_import0() {
|
||||
async fn test_mock_example_import0() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -15,26 +15,43 @@ mod test_examples_import {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "import", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_import0() {
|
||||
async fn kcl_test_example_import0() -> miette::Result<()> {
|
||||
let code = "This is code.\nIt does other shit.\nimport";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "import", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_import {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_import0() {
|
||||
async fn test_mock_example_import0() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -15,26 +15,43 @@ mod test_examples_import {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "import", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_import0() {
|
||||
async fn kcl_test_example_import0() -> miette::Result<()> {
|
||||
let code = "This is code.\nIt does other shit.\nimport";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "import", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_import {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_import0() {
|
||||
async fn test_mock_example_import0() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is code.\nIt does other shit.\nimport").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -15,26 +15,43 @@ mod test_examples_import {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "import", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_import0() {
|
||||
async fn kcl_test_example_import0() -> miette::Result<()> {
|
||||
let code = "This is code.\nIt does other shit.\nimport";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "import", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nimport".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_import0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_show {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_show0() {
|
||||
async fn test_mock_example_show0() -> miette::Result<()> {
|
||||
let program =
|
||||
crate::Program::parse_no_errs("This is code.\nIt does other shit.\nshow").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
@ -15,26 +15,43 @@ mod test_examples_show {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "show", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_show0() {
|
||||
async fn kcl_test_example_show0() -> miette::Result<()> {
|
||||
let code = "This is code.\nIt does other shit.\nshow";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "show", 0usize),
|
||||
kcl_source: "This is code.\nIt does other shit.\nshow".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_show0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
#[cfg(test)]
|
||||
mod test_examples_some_function {
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_mock_example_some_function0() {
|
||||
async fn test_mock_example_some_function0() -> miette::Result<()> {
|
||||
let program = crate::Program::parse_no_errs("someFunction()").unwrap();
|
||||
let ctx = crate::ExecutorContext {
|
||||
engine: std::sync::Arc::new(Box::new(
|
||||
@ -14,26 +14,43 @@ mod test_examples_some_function {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
};
|
||||
ctx.run(program.into(), &mut crate::ExecState::default())
|
||||
.await
|
||||
.unwrap();
|
||||
if let Err(e) = ctx.run(program.into(), &mut crate::ExecState::new()).await {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "some_function", 0usize),
|
||||
kcl_source: "someFunction()".to_string(),
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||
async fn kcl_test_example_some_function0() {
|
||||
async fn kcl_test_example_some_function0() -> miette::Result<()> {
|
||||
let code = "someFunction()";
|
||||
let result = crate::test_server::execute_and_snapshot(
|
||||
let result = match crate::test_server::execute_and_snapshot(
|
||||
code,
|
||||
crate::settings::types::UnitLength::Mm,
|
||||
None,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
{
|
||||
Err(crate::errors::ExecError::Kcl(e)) => {
|
||||
return Err(miette::Report::new(crate::errors::Report {
|
||||
error: e,
|
||||
filename: format!("{}{}", "some_function", 0usize),
|
||||
kcl_source: "someFunction()".to_string(),
|
||||
}));
|
||||
}
|
||||
Err(other_err) => panic!("{}", other_err),
|
||||
Ok(img) => img,
|
||||
};
|
||||
twenty_twenty::assert_image(
|
||||
&format!("tests/outputs/{}.png", "serial_test_example_some_function0"),
|
||||
&result,
|
||||
0.99,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -164,7 +164,7 @@ async fn snapshot_endpoint(body: Bytes, state: ExecutorContext) -> Response<Body
|
||||
};
|
||||
|
||||
eprintln!("Executing {test_name}");
|
||||
let mut exec_state = ExecState::default();
|
||||
let mut exec_state = ExecState::new();
|
||||
// This is a shitty source range, I don't know what else to use for it though.
|
||||
// There's no actual KCL associated with this reset_scene call.
|
||||
if let Err(e) = state
|
||||
|
||||
@ -16,7 +16,7 @@ pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
|
||||
let ctx = ExecutorContext::new_forwarded_mock(Arc::new(Box::new(
|
||||
crate::conn_mock_core::EngineConnection::new(ref_result).await?,
|
||||
)));
|
||||
ctx.run(program.into(), &mut ExecState::default()).await?;
|
||||
ctx.run(program.into(), &mut ExecState::new()).await?;
|
||||
|
||||
let result = result.lock().expect("mutex lock").clone();
|
||||
Ok(result)
|
||||
|
||||
@ -604,24 +604,6 @@ fn clean_function_name(name: &str) -> String {
|
||||
fn_name
|
||||
}
|
||||
|
||||
/// Check if a schema is the same as another schema, but don't check the description.
|
||||
fn is_same_schema(sa: &schemars::schema::Schema, sb: &schemars::schema::Schema) -> bool {
|
||||
let schemars::schema::Schema::Object(a) = sa else {
|
||||
return sa == sb;
|
||||
};
|
||||
|
||||
let schemars::schema::Schema::Object(b) = sb else {
|
||||
return sa == sb;
|
||||
};
|
||||
|
||||
let mut a = a.clone();
|
||||
a.metadata = None;
|
||||
let mut b = b.clone();
|
||||
b.metadata = None;
|
||||
|
||||
a == b
|
||||
}
|
||||
|
||||
/// Recursively create references for types we already know about.
|
||||
fn recurse_and_create_references(
|
||||
name: &str,
|
||||
@ -655,24 +637,6 @@ fn recurse_and_create_references(
|
||||
return Ok(schemars::schema::Schema::Object(obj));
|
||||
}
|
||||
|
||||
// Check if this is the type we already know about.
|
||||
for (n, s) in types {
|
||||
if is_same_schema(schema, s) && name != n && !n.starts_with("[") {
|
||||
// Return a reference to the type.
|
||||
let sref = schemars::schema::Schema::new_ref(n.to_string());
|
||||
// Add the existing metadata to the reference.
|
||||
let schemars::schema::Schema::Object(ro) = sref else {
|
||||
return Err(anyhow::anyhow!(
|
||||
"Failed to get object schema, should have not been a primitive"
|
||||
));
|
||||
};
|
||||
let mut ro = ro.clone();
|
||||
ro.metadata = o.metadata.clone();
|
||||
|
||||
return Ok(schemars::schema::Schema::Object(ro));
|
||||
}
|
||||
}
|
||||
|
||||
let mut obj = o.clone();
|
||||
|
||||
// If we have an object iterate over the properties and recursively create references.
|
||||
|
||||
@ -353,7 +353,6 @@ pub struct CompilationError {
|
||||
}
|
||||
|
||||
impl CompilationError {
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn err(source_range: SourceRange, message: impl ToString) -> CompilationError {
|
||||
CompilationError {
|
||||
source_range,
|
||||
|
||||
73
src/wasm-lib/kcl/src/execution/annotations.rs
Normal file
@ -0,0 +1,73 @@
|
||||
//! Data on available annotations.
|
||||
|
||||
use super::kcl_value::{UnitAngle, UnitLen};
|
||||
use crate::{
|
||||
errors::KclErrorDetails,
|
||||
parsing::ast::types::{Expr, Node, NonCodeValue, ObjectProperty},
|
||||
KclError, SourceRange,
|
||||
};
|
||||
|
||||
pub(super) const SETTINGS: &str = "settings";
|
||||
pub(super) const SETTINGS_UNIT_LENGTH: &str = "defaultLengthUnit";
|
||||
pub(super) const SETTINGS_UNIT_ANGLE: &str = "defaultAngleUnit";
|
||||
|
||||
pub(super) fn expect_properties<'a>(
|
||||
for_key: &'static str,
|
||||
annotation: &'a NonCodeValue,
|
||||
source_range: SourceRange,
|
||||
) -> Result<&'a [Node<ObjectProperty>], KclError> {
|
||||
match annotation {
|
||||
NonCodeValue::Annotation { name, properties } => {
|
||||
assert_eq!(name.name, for_key);
|
||||
Ok(&**properties.as_ref().ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Empty `{for_key}` annotation"),
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn expect_ident(expr: &Expr) -> Result<&str, KclError> {
|
||||
match expr {
|
||||
Expr::Identifier(id) => Ok(&id.name),
|
||||
e => Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Unexpected settings value, expected a simple name, e.g., `mm`".to_owned(),
|
||||
source_ranges: vec![e.into()],
|
||||
})),
|
||||
}
|
||||
}
|
||||
|
||||
impl UnitLen {
|
||||
pub(super) fn from_str(s: &str, source_range: SourceRange) -> Result<Self, KclError> {
|
||||
match s {
|
||||
"mm" => Ok(UnitLen::Mm),
|
||||
"cm" => Ok(UnitLen::Cm),
|
||||
"m" => Ok(UnitLen::M),
|
||||
"inch" | "in" => Ok(UnitLen::Inches),
|
||||
"ft" => Ok(UnitLen::Feet),
|
||||
"yd" => Ok(UnitLen::Yards),
|
||||
value => Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Unexpected settings value: `{value}`; expected one of `mm`, `cm`, `m`, `inch`, `ft`, `yd`"
|
||||
),
|
||||
source_ranges: vec![source_range],
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UnitAngle {
|
||||
pub(super) fn from_str(s: &str, source_range: SourceRange) -> Result<Self, KclError> {
|
||||
match s {
|
||||
"deg" => Ok(UnitAngle::Degrees),
|
||||
"rad" => Ok(UnitAngle::Radians),
|
||||
value => Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!("Unexpected settings value: `{value}`; expected one of `deg`, `rad`"),
|
||||
source_ranges: vec![source_range],
|
||||
})),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -29,7 +29,7 @@ impl BinaryPart {
|
||||
match self {
|
||||
BinaryPart::Literal(literal) => Ok(literal.into()),
|
||||
BinaryPart::Identifier(identifier) => {
|
||||
let value = exec_state.memory.get(&identifier.name, identifier.into())?;
|
||||
let value = exec_state.memory().get(&identifier.name, identifier.into())?;
|
||||
Ok(value.clone())
|
||||
}
|
||||
BinaryPart::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, ctx).await,
|
||||
@ -47,7 +47,7 @@ impl Node<MemberExpression> {
|
||||
let array = match &self.object {
|
||||
MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
|
||||
MemberObject::Identifier(identifier) => {
|
||||
let value = exec_state.memory.get(&identifier.name, identifier.into())?;
|
||||
let value = exec_state.memory().get(&identifier.name, identifier.into())?;
|
||||
value.clone()
|
||||
}
|
||||
};
|
||||
@ -75,7 +75,7 @@ impl Node<MemberExpression> {
|
||||
// TODO: Don't use recursion here, use a loop.
|
||||
MemberObject::MemberExpression(member_expr) => member_expr.get_result(exec_state)?,
|
||||
MemberObject::Identifier(identifier) => {
|
||||
let value = exec_state.memory.get(&identifier.name, identifier.into())?;
|
||||
let value = exec_state.memory().get(&identifier.name, identifier.into())?;
|
||||
value.clone()
|
||||
}
|
||||
};
|
||||
@ -170,6 +170,42 @@ impl Node<BinaryExpression> {
|
||||
}
|
||||
}
|
||||
|
||||
// Check if we are doing logical operations on booleans.
|
||||
if self.operator == BinaryOperator::Or || self.operator == BinaryOperator::And {
|
||||
let KclValue::Bool {
|
||||
value: left_value,
|
||||
meta: _,
|
||||
} = left_value
|
||||
else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Cannot apply logical operator to non-boolean value: {}",
|
||||
left_value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![self.left.clone().into()],
|
||||
}));
|
||||
};
|
||||
let KclValue::Bool {
|
||||
value: right_value,
|
||||
meta: _,
|
||||
} = right_value
|
||||
else {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Cannot apply logical operator to non-boolean value: {}",
|
||||
right_value.human_friendly_type()
|
||||
),
|
||||
source_ranges: vec![self.right.clone().into()],
|
||||
}));
|
||||
};
|
||||
let raw_value = match self.operator {
|
||||
BinaryOperator::Or => left_value || right_value,
|
||||
BinaryOperator::And => left_value && right_value,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
return Ok(KclValue::Bool { value: raw_value, meta });
|
||||
}
|
||||
|
||||
let left = parse_number_as_f64(&left_value, self.left.clone().into())?;
|
||||
let right = parse_number_as_f64(&right_value, self.right.clone().into())?;
|
||||
|
||||
@ -222,6 +258,7 @@ impl Node<BinaryExpression> {
|
||||
value: left == right,
|
||||
meta,
|
||||
},
|
||||
BinaryOperator::And | BinaryOperator::Or => unreachable!(),
|
||||
};
|
||||
|
||||
Ok(value)
|
||||
@ -310,11 +347,11 @@ pub(crate) async fn execute_pipe_body(
|
||||
// Now that we've evaluated the first child expression in the pipeline, following child expressions
|
||||
// should use the previous child expression for %.
|
||||
// This means there's no more need for the previous pipe_value from the parent AST node above this one.
|
||||
let previous_pipe_value = std::mem::replace(&mut exec_state.pipe_value, Some(output));
|
||||
let previous_pipe_value = std::mem::replace(&mut exec_state.mod_local.pipe_value, Some(output));
|
||||
// Evaluate remaining elements.
|
||||
let result = inner_execute_pipe_body(exec_state, body, ctx).await;
|
||||
// Restore the previous pipe value.
|
||||
exec_state.pipe_value = previous_pipe_value;
|
||||
exec_state.mod_local.pipe_value = previous_pipe_value;
|
||||
|
||||
result
|
||||
}
|
||||
@ -340,10 +377,10 @@ async fn inner_execute_pipe_body(
|
||||
let output = ctx
|
||||
.execute_expr(expression, exec_state, &metadata, StatementKind::Expression)
|
||||
.await?;
|
||||
exec_state.pipe_value = Some(output);
|
||||
exec_state.mod_local.pipe_value = Some(output);
|
||||
}
|
||||
// Safe to unwrap here, because pipe_value always has something pushed in when the `match first` executes.
|
||||
let final_output = exec_state.pipe_value.take().unwrap();
|
||||
let final_output = exec_state.mod_local.pipe_value.take().unwrap();
|
||||
Ok(final_output)
|
||||
}
|
||||
|
||||
@ -384,6 +421,7 @@ impl Node<CallExpressionKw> {
|
||||
},
|
||||
self.into(),
|
||||
ctx.clone(),
|
||||
exec_state.mod_local.pipe_value.clone().map(Arg::synthetic),
|
||||
);
|
||||
match ctx.stdlib.get_either(fn_name) {
|
||||
FunctionKind::Core(func) => {
|
||||
@ -417,7 +455,7 @@ impl Node<CallExpressionKw> {
|
||||
// before running, and we will likely want to use the
|
||||
// return value. The call takes ownership of the args,
|
||||
// so we need to build the op before the call.
|
||||
exec_state.operations.push(op);
|
||||
exec_state.mod_local.operations.push(op);
|
||||
}
|
||||
result
|
||||
};
|
||||
@ -431,8 +469,8 @@ impl Node<CallExpressionKw> {
|
||||
let source_range = SourceRange::from(self);
|
||||
// Clone the function so that we can use a mutable reference to
|
||||
// exec_state.
|
||||
let func = exec_state.memory.get(fn_name, source_range)?.clone();
|
||||
let fn_dynamic_state = exec_state.dynamic_state.merge(&exec_state.memory);
|
||||
let func = exec_state.memory().get(fn_name, source_range)?.clone();
|
||||
let fn_dynamic_state = exec_state.mod_local.dynamic_state.merge(exec_state.memory());
|
||||
|
||||
// Track call operation.
|
||||
let op_labeled_args = args
|
||||
@ -441,16 +479,20 @@ impl Node<CallExpressionKw> {
|
||||
.iter()
|
||||
.map(|(k, v)| (k.clone(), OpArg::new(v.source_range)))
|
||||
.collect();
|
||||
exec_state.operations.push(Operation::UserDefinedFunctionCall {
|
||||
name: Some(fn_name.clone()),
|
||||
function_source_range: func.function_def_source_range().unwrap_or_default(),
|
||||
unlabeled_arg: args.kw_args.unlabeled.as_ref().map(|arg| OpArg::new(arg.source_range)),
|
||||
labeled_args: op_labeled_args,
|
||||
source_range: callsite,
|
||||
});
|
||||
exec_state
|
||||
.mod_local
|
||||
.operations
|
||||
.push(Operation::UserDefinedFunctionCall {
|
||||
name: Some(fn_name.clone()),
|
||||
function_source_range: func.function_def_source_range().unwrap_or_default(),
|
||||
unlabeled_arg: args.kw_args.unlabeled.as_ref().map(|arg| OpArg::new(arg.source_range)),
|
||||
labeled_args: op_labeled_args,
|
||||
source_range: callsite,
|
||||
});
|
||||
|
||||
let return_value = {
|
||||
let previous_dynamic_state = std::mem::replace(&mut exec_state.dynamic_state, fn_dynamic_state);
|
||||
let previous_dynamic_state =
|
||||
std::mem::replace(&mut exec_state.mod_local.dynamic_state, fn_dynamic_state);
|
||||
let result = func
|
||||
.call_fn_kw(args, exec_state, ctx.clone(), callsite)
|
||||
.await
|
||||
@ -459,7 +501,7 @@ impl Node<CallExpressionKw> {
|
||||
// TODO currently ignored by the frontend
|
||||
e.add_source_ranges(vec![source_range])
|
||||
});
|
||||
exec_state.dynamic_state = previous_dynamic_state;
|
||||
exec_state.mod_local.dynamic_state = previous_dynamic_state;
|
||||
result?
|
||||
};
|
||||
|
||||
@ -476,7 +518,10 @@ impl Node<CallExpressionKw> {
|
||||
})?;
|
||||
|
||||
// Track return operation.
|
||||
exec_state.operations.push(Operation::UserDefinedFunctionReturn);
|
||||
exec_state
|
||||
.mod_local
|
||||
.operations
|
||||
.push(Operation::UserDefinedFunctionReturn);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
@ -525,7 +570,12 @@ impl Node<CallExpression> {
|
||||
};
|
||||
|
||||
// Attempt to call the function.
|
||||
let args = crate::std::Args::new(fn_args, self.into(), ctx.clone());
|
||||
let args = crate::std::Args::new(
|
||||
fn_args,
|
||||
self.into(),
|
||||
ctx.clone(),
|
||||
exec_state.mod_local.pipe_value.clone().map(Arg::synthetic),
|
||||
);
|
||||
let result = {
|
||||
// Don't early-return in this block.
|
||||
let result = func.std_lib_fn()(exec_state, args).await;
|
||||
@ -537,7 +587,7 @@ impl Node<CallExpression> {
|
||||
// before running, and we will likely want to use the
|
||||
// return value. The call takes ownership of the args,
|
||||
// so we need to build the op before the call.
|
||||
exec_state.operations.push(op);
|
||||
exec_state.mod_local.operations.push(op);
|
||||
}
|
||||
result
|
||||
};
|
||||
@ -551,27 +601,31 @@ impl Node<CallExpression> {
|
||||
let source_range = SourceRange::from(self);
|
||||
// Clone the function so that we can use a mutable reference to
|
||||
// exec_state.
|
||||
let func = exec_state.memory.get(fn_name, source_range)?.clone();
|
||||
let fn_dynamic_state = exec_state.dynamic_state.merge(&exec_state.memory);
|
||||
let func = exec_state.memory().get(fn_name, source_range)?.clone();
|
||||
let fn_dynamic_state = exec_state.mod_local.dynamic_state.merge(exec_state.memory());
|
||||
|
||||
// Track call operation.
|
||||
exec_state.operations.push(Operation::UserDefinedFunctionCall {
|
||||
name: Some(fn_name.clone()),
|
||||
function_source_range: func.function_def_source_range().unwrap_or_default(),
|
||||
unlabeled_arg: None,
|
||||
// TODO: Add the arguments for legacy positional parameters.
|
||||
labeled_args: Default::default(),
|
||||
source_range: callsite,
|
||||
});
|
||||
exec_state
|
||||
.mod_local
|
||||
.operations
|
||||
.push(Operation::UserDefinedFunctionCall {
|
||||
name: Some(fn_name.clone()),
|
||||
function_source_range: func.function_def_source_range().unwrap_or_default(),
|
||||
unlabeled_arg: None,
|
||||
// TODO: Add the arguments for legacy positional parameters.
|
||||
labeled_args: Default::default(),
|
||||
source_range: callsite,
|
||||
});
|
||||
|
||||
let return_value = {
|
||||
let previous_dynamic_state = std::mem::replace(&mut exec_state.dynamic_state, fn_dynamic_state);
|
||||
let previous_dynamic_state =
|
||||
std::mem::replace(&mut exec_state.mod_local.dynamic_state, fn_dynamic_state);
|
||||
let result = func.call_fn(fn_args, exec_state, ctx.clone()).await.map_err(|e| {
|
||||
// Add the call expression to the source ranges.
|
||||
// TODO currently ignored by the frontend
|
||||
e.add_source_ranges(vec![source_range])
|
||||
});
|
||||
exec_state.dynamic_state = previous_dynamic_state;
|
||||
exec_state.mod_local.dynamic_state = previous_dynamic_state;
|
||||
result?
|
||||
};
|
||||
|
||||
@ -588,7 +642,10 @@ impl Node<CallExpression> {
|
||||
})?;
|
||||
|
||||
// Track return operation.
|
||||
exec_state.operations.push(Operation::UserDefinedFunctionReturn);
|
||||
exec_state
|
||||
.mod_local
|
||||
.operations
|
||||
.push(Operation::UserDefinedFunctionReturn);
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
@ -604,7 +661,7 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
|
||||
match result {
|
||||
KclValue::Sketch { value: ref mut sketch } => {
|
||||
for (_, tag) in sketch.tags.iter() {
|
||||
exec_state.memory.update_tag(&tag.value, tag.clone())?;
|
||||
exec_state.mut_memory().update_tag(&tag.value, tag.clone())?;
|
||||
}
|
||||
}
|
||||
KclValue::Solid(ref mut solid) => {
|
||||
@ -642,7 +699,7 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
|
||||
info.sketch = solid.id;
|
||||
t.info = Some(info);
|
||||
|
||||
exec_state.memory.update_tag(&tag.name, t.clone())?;
|
||||
exec_state.mut_memory().update_tag(&tag.name, t.clone())?;
|
||||
|
||||
// update the sketch tags.
|
||||
solid.sketch.tags.insert(tag.name.clone(), t);
|
||||
@ -650,11 +707,8 @@ fn update_memory_for_tags_of_geometry(result: &mut KclValue, exec_state: &mut Ex
|
||||
}
|
||||
|
||||
// Find the stale sketch in memory and update it.
|
||||
if let Some(current_env) = exec_state
|
||||
.memory
|
||||
.environments
|
||||
.get_mut(exec_state.memory.current_env.index())
|
||||
{
|
||||
let cur_env_index = exec_state.memory().current_env.index();
|
||||
if let Some(current_env) = exec_state.mut_memory().environments.get_mut(cur_env_index) {
|
||||
current_env.update_sketch_tags(&solid.sketch);
|
||||
}
|
||||
}
|
||||
@ -673,7 +727,9 @@ impl Node<TagDeclarator> {
|
||||
}],
|
||||
}));
|
||||
|
||||
exec_state.memory.add(&self.name, memory_item.clone(), self.into())?;
|
||||
exec_state
|
||||
.mut_memory()
|
||||
.add(&self.name, memory_item.clone(), self.into())?;
|
||||
|
||||
Ok(self.into())
|
||||
}
|
||||
@ -868,7 +924,7 @@ impl Property {
|
||||
Ok(Property::String(name.to_string()))
|
||||
} else {
|
||||
// Actually evaluate memory to compute the property.
|
||||
let prop = exec_state.memory.get(name, property_src)?;
|
||||
let prop = exec_state.memory().get(name, property_src)?;
|
||||
jvalue_to_prop(prop, property_sr, name)
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,9 +8,12 @@ use crate::{
|
||||
errors::KclErrorDetails,
|
||||
exec::{ProgramMemory, Sketch},
|
||||
execution::{Face, ImportedGeometry, MemoryFunction, Metadata, Plane, SketchSet, Solid, SolidSet, TagIdentifier},
|
||||
parsing::ast::types::{FunctionExpression, KclNone, LiteralValue, TagDeclarator, TagNode},
|
||||
parsing::{
|
||||
ast::types::{FunctionExpression, KclNone, LiteralValue, TagDeclarator, TagNode},
|
||||
token::NumericSuffix,
|
||||
},
|
||||
std::{args::Arg, FnAsArg},
|
||||
ExecState, ExecutorContext, KclError, SourceRange,
|
||||
ExecState, ExecutorContext, KclError, ModuleId, SourceRange,
|
||||
};
|
||||
|
||||
pub type KclObjectFields = HashMap<String, KclValue>;
|
||||
@ -84,6 +87,11 @@ pub enum KclValue {
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
Module {
|
||||
value: ModuleId,
|
||||
#[serde(rename = "__meta")]
|
||||
meta: Vec<Metadata>,
|
||||
},
|
||||
KclNone {
|
||||
value: KclNone,
|
||||
#[serde(rename = "__meta")]
|
||||
@ -143,6 +151,7 @@ impl From<KclValue> for Vec<SourceRange> {
|
||||
KclValue::String { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Array { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Object { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Module { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::Uuid { meta, .. } => to_vec_sr(&meta),
|
||||
KclValue::KclNone { meta, .. } => to_vec_sr(&meta),
|
||||
}
|
||||
@ -173,6 +182,7 @@ impl From<&KclValue> for Vec<SourceRange> {
|
||||
KclValue::Uuid { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::Array { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::Object { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::Module { meta, .. } => to_vec_sr(meta),
|
||||
KclValue::KclNone { meta, .. } => to_vec_sr(meta),
|
||||
}
|
||||
}
|
||||
@ -198,6 +208,7 @@ impl KclValue {
|
||||
KclValue::Solids { value } => value.iter().flat_map(|sketch| &sketch.meta).copied().collect(),
|
||||
KclValue::ImportedGeometry(x) => x.meta.clone(),
|
||||
KclValue::Function { meta, .. } => meta.clone(),
|
||||
KclValue::Module { meta, .. } => meta.clone(),
|
||||
KclValue::KclNone { meta, .. } => meta.clone(),
|
||||
}
|
||||
}
|
||||
@ -263,6 +274,7 @@ impl KclValue {
|
||||
KclValue::String { .. } => "string (text)",
|
||||
KclValue::Array { .. } => "array (list)",
|
||||
KclValue::Object { .. } => "object",
|
||||
KclValue::Module { .. } => "module",
|
||||
KclValue::KclNone { .. } => "None",
|
||||
}
|
||||
}
|
||||
@ -552,3 +564,52 @@ impl KclValue {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO called UnitLen so as not to clash with UnitLength in settings)
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum UnitLen {
|
||||
Mm,
|
||||
Cm,
|
||||
M,
|
||||
Inches,
|
||||
Feet,
|
||||
Yards,
|
||||
}
|
||||
|
||||
impl TryFrom<NumericSuffix> for UnitLen {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
|
||||
match suffix {
|
||||
NumericSuffix::Mm => Ok(Self::Mm),
|
||||
NumericSuffix::Cm => Ok(Self::Cm),
|
||||
NumericSuffix::M => Ok(Self::M),
|
||||
NumericSuffix::Inch => Ok(Self::Inches),
|
||||
NumericSuffix::Ft => Ok(Self::Feet),
|
||||
NumericSuffix::Yd => Ok(Self::Yards),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Eq)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type")]
|
||||
pub enum UnitAngle {
|
||||
Degrees,
|
||||
Radians,
|
||||
}
|
||||
|
||||
impl TryFrom<NumericSuffix> for UnitAngle {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(suffix: NumericSuffix) -> std::result::Result<Self, Self::Error> {
|
||||
match suffix {
|
||||
NumericSuffix::Deg => Ok(Self::Degrees),
|
||||
NumericSuffix::Rad => Ok(Self::Radians),
|
||||
_ => Err(()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -22,7 +22,9 @@ type Point3D = kcmc::shared::Point3d<f64>;
|
||||
|
||||
pub use function_param::FunctionParam;
|
||||
pub use kcl_value::{KclObjectFields, KclValue};
|
||||
use uuid::Uuid;
|
||||
|
||||
mod annotations;
|
||||
pub(crate) mod cache;
|
||||
mod cad_op;
|
||||
mod exec_ast;
|
||||
@ -35,7 +37,8 @@ use crate::{
|
||||
execution::cache::{CacheInformation, CacheResult},
|
||||
fs::{FileManager, FileSystem},
|
||||
parsing::ast::types::{
|
||||
BodyItem, Expr, FunctionExpression, ImportSelector, ItemVisibility, Node, NodeRef, TagDeclarator, TagNode,
|
||||
BodyItem, Expr, FunctionExpression, ImportSelector, ItemVisibility, Node, NodeRef, NonCodeValue,
|
||||
Program as AstProgram, TagDeclarator, TagNode,
|
||||
},
|
||||
settings::types::UnitLength,
|
||||
source_range::{ModuleId, SourceRange},
|
||||
@ -47,14 +50,32 @@ use crate::{
|
||||
pub use cad_op::Operation;
|
||||
|
||||
/// State for executing a program.
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ExecState {
|
||||
/// Program variable bindings.
|
||||
pub memory: ProgramMemory,
|
||||
pub global: GlobalState,
|
||||
pub mod_local: ModuleState,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct GlobalState {
|
||||
/// The stable artifact ID generator.
|
||||
pub id_generator: IdGenerator,
|
||||
/// Map from source file absolute path to module ID.
|
||||
pub path_to_source_id: IndexMap<std::path::PathBuf, ModuleId>,
|
||||
/// Map from module ID to module info.
|
||||
pub module_infos: IndexMap<ModuleId, ModuleInfo>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct ModuleState {
|
||||
/// Program variable bindings.
|
||||
pub memory: ProgramMemory,
|
||||
/// Dynamic state that follows dynamic flow of the program.
|
||||
pub dynamic_state: DynamicState,
|
||||
/// The current value of the pipe operator returned from the previous
|
||||
@ -65,29 +86,156 @@ pub struct ExecState {
|
||||
/// The stack of import statements for detecting circular module imports.
|
||||
/// If this is empty, we're not currently executing an import statement.
|
||||
pub import_stack: Vec<std::path::PathBuf>,
|
||||
/// Map from source file absolute path to module ID.
|
||||
pub path_to_source_id: IndexMap<std::path::PathBuf, ModuleId>,
|
||||
/// Map from module ID to module info.
|
||||
pub module_infos: IndexMap<ModuleId, ModuleInfo>,
|
||||
/// Operations that have been performed in execution order, for display in
|
||||
/// the Feature Tree.
|
||||
pub operations: Vec<Operation>,
|
||||
/// Settings specified from annotations.
|
||||
pub settings: MetaSettings,
|
||||
}
|
||||
|
||||
impl Default for ExecState {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
impl ExecState {
|
||||
fn add_module(&mut self, path: std::path::PathBuf) -> ModuleId {
|
||||
pub fn new() -> Self {
|
||||
ExecState {
|
||||
global: GlobalState::new(),
|
||||
mod_local: ModuleState::default(),
|
||||
}
|
||||
}
|
||||
|
||||
fn reset(&mut self) {
|
||||
let mut id_generator = self.global.id_generator.clone();
|
||||
// We do not pop the ids, since we want to keep the same id generator.
|
||||
// This is for the front end to keep track of the ids.
|
||||
id_generator.next_id = 0;
|
||||
|
||||
let mut global = GlobalState::new();
|
||||
global.id_generator = id_generator;
|
||||
|
||||
*self = ExecState {
|
||||
global,
|
||||
mod_local: ModuleState::default(),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn memory(&self) -> &ProgramMemory {
|
||||
&self.mod_local.memory
|
||||
}
|
||||
|
||||
pub fn mut_memory(&mut self) -> &mut ProgramMemory {
|
||||
&mut self.mod_local.memory
|
||||
}
|
||||
|
||||
pub fn next_uuid(&mut self) -> Uuid {
|
||||
self.global.id_generator.next_uuid()
|
||||
}
|
||||
|
||||
async fn add_module(
|
||||
&mut self,
|
||||
path: std::path::PathBuf,
|
||||
ctxt: &ExecutorContext,
|
||||
source_range: SourceRange,
|
||||
) -> Result<ModuleId, KclError> {
|
||||
// Need to avoid borrowing self in the closure.
|
||||
let new_module_id = ModuleId::from_usize(self.path_to_source_id.len());
|
||||
let new_module_id = ModuleId::from_usize(self.global.path_to_source_id.len());
|
||||
let mut is_new = false;
|
||||
let id = *self.path_to_source_id.entry(path.clone()).or_insert_with(|| {
|
||||
let id = *self.global.path_to_source_id.entry(path.clone()).or_insert_with(|| {
|
||||
is_new = true;
|
||||
new_module_id
|
||||
});
|
||||
|
||||
if is_new {
|
||||
let module_info = ModuleInfo { id, path };
|
||||
self.module_infos.insert(id, module_info);
|
||||
let source = ctxt.fs.read_to_string(&path, source_range).await?;
|
||||
// TODO handle parsing errors properly
|
||||
let parsed = crate::parsing::parse_str(&source, id).parse_errs_as_err()?;
|
||||
|
||||
let module_info = ModuleInfo {
|
||||
id,
|
||||
path,
|
||||
parsed: Some(parsed),
|
||||
};
|
||||
self.global.module_infos.insert(id, module_info);
|
||||
}
|
||||
id
|
||||
|
||||
Ok(id)
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalState {
|
||||
fn new() -> Self {
|
||||
let mut global = GlobalState {
|
||||
id_generator: Default::default(),
|
||||
path_to_source_id: Default::default(),
|
||||
module_infos: Default::default(),
|
||||
};
|
||||
|
||||
// TODO(#4434): Use the top-level file's path.
|
||||
let root_path = PathBuf::new();
|
||||
let root_id = ModuleId::default();
|
||||
global.module_infos.insert(
|
||||
root_id,
|
||||
ModuleInfo {
|
||||
id: root_id,
|
||||
path: root_path.clone(),
|
||||
parsed: None,
|
||||
},
|
||||
);
|
||||
global.path_to_source_id.insert(root_path, root_id);
|
||||
global
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct MetaSettings {
|
||||
pub default_length_units: kcl_value::UnitLen,
|
||||
pub default_angle_units: kcl_value::UnitAngle,
|
||||
}
|
||||
|
||||
impl Default for MetaSettings {
|
||||
fn default() -> Self {
|
||||
MetaSettings {
|
||||
default_length_units: kcl_value::UnitLen::Mm,
|
||||
default_angle_units: kcl_value::UnitAngle::Degrees,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MetaSettings {
|
||||
fn update_from_annotation(&mut self, annotation: &NonCodeValue, source_range: SourceRange) -> Result<(), KclError> {
|
||||
let properties = annotations::expect_properties(annotations::SETTINGS, annotation, source_range)?;
|
||||
|
||||
for p in properties {
|
||||
match &*p.inner.key.name {
|
||||
annotations::SETTINGS_UNIT_LENGTH => {
|
||||
let value = annotations::expect_ident(&p.inner.value)?;
|
||||
let value = kcl_value::UnitLen::from_str(value, source_range)?;
|
||||
self.default_length_units = value;
|
||||
}
|
||||
annotations::SETTINGS_UNIT_ANGLE => {
|
||||
let value = annotations::expect_ident(&p.inner.value)?;
|
||||
let value = kcl_value::UnitAngle::from_str(value, source_range)?;
|
||||
self.default_angle_units = value;
|
||||
}
|
||||
name => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Unexpected settings key: `{name}`; expected one of `{}`, `{}`",
|
||||
annotations::SETTINGS_UNIT_LENGTH,
|
||||
annotations::SETTINGS_UNIT_ANGLE
|
||||
),
|
||||
source_ranges: vec![source_range],
|
||||
}))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@ -159,6 +307,13 @@ impl ProgramMemory {
|
||||
}))
|
||||
}
|
||||
|
||||
/// Returns all bindings in the current scope.
|
||||
#[allow(dead_code)]
|
||||
fn get_all_cur_scope(&self) -> IndexMap<String, KclValue> {
|
||||
let env = &self.environments[self.current_env.index()];
|
||||
env.bindings.clone()
|
||||
}
|
||||
|
||||
/// Find all solids in the memory that are on a specific sketch id.
|
||||
/// This does not look inside closures. But as long as we do not allow
|
||||
/// mutation of variables in KCL, closure memory should be a subset of this.
|
||||
@ -274,18 +429,14 @@ pub struct DynamicState {
|
||||
}
|
||||
|
||||
impl DynamicState {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn merge(&self, memory: &ProgramMemory) -> Self {
|
||||
fn merge(&self, memory: &ProgramMemory) -> Self {
|
||||
let mut merged = self.clone();
|
||||
merged.append(memory);
|
||||
merged
|
||||
}
|
||||
|
||||
pub fn append(&mut self, memory: &ProgramMemory) {
|
||||
fn append(&mut self, memory: &ProgramMemory) {
|
||||
for env in &memory.environments {
|
||||
for item in env.bindings.values() {
|
||||
if let KclValue::Solid(eg) = item {
|
||||
@ -295,7 +446,7 @@ impl DynamicState {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edge_cut_ids_on_sketch(&self, sketch_id: uuid::Uuid) -> Vec<uuid::Uuid> {
|
||||
pub(crate) fn edge_cut_ids_on_sketch(&self, sketch_id: uuid::Uuid) -> Vec<uuid::Uuid> {
|
||||
self.solid_ids
|
||||
.iter()
|
||||
.flat_map(|eg| {
|
||||
@ -553,7 +704,7 @@ pub struct Plane {
|
||||
|
||||
impl Plane {
|
||||
pub(crate) fn from_plane_data(value: crate::std::sketch::PlaneData, exec_state: &mut ExecState) -> Self {
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.global.id_generator.next_uuid();
|
||||
match value {
|
||||
crate::std::sketch::PlaneData::XY => Plane {
|
||||
id,
|
||||
@ -1001,13 +1152,14 @@ pub enum BodyType {
|
||||
|
||||
/// Info about a module. Right now, this is pretty minimal. We hope to cache
|
||||
/// modules here in the future.
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
|
||||
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
pub struct ModuleInfo {
|
||||
/// The ID of the module.
|
||||
id: ModuleId,
|
||||
/// Absolute path of the module's source file.
|
||||
path: std::path::PathBuf,
|
||||
parsed: Option<Node<AstProgram>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize, Serialize, PartialEq, Clone, Copy, ts_rs::TS, JsonSchema)]
|
||||
@ -1796,7 +1948,7 @@ impl ExecutorContext {
|
||||
source_range: crate::execution::SourceRange,
|
||||
) -> Result<(), KclError> {
|
||||
self.engine
|
||||
.clear_scene(&mut exec_state.id_generator, source_range)
|
||||
.clear_scene(&mut exec_state.global.id_generator, source_range)
|
||||
.await?;
|
||||
|
||||
// We do not create the planes here as the post hook in wasm will do that
|
||||
@ -1897,23 +2049,13 @@ impl ExecutorContext {
|
||||
|
||||
if cache_result.clear_scene && !self.is_mock() {
|
||||
// Pop the execution state, since we are starting fresh.
|
||||
let mut id_generator = exec_state.id_generator.clone();
|
||||
// We do not pop the ids, since we want to keep the same id generator.
|
||||
// This is for the front end to keep track of the ids.
|
||||
id_generator.next_id = 0;
|
||||
*exec_state = ExecState {
|
||||
id_generator,
|
||||
..Default::default()
|
||||
};
|
||||
exec_state.reset();
|
||||
|
||||
// We don't do this in mock mode since there is no engine connection
|
||||
// anyways and from the TS side we override memory and don't want to clear it.
|
||||
self.reset_scene(exec_state, Default::default()).await?;
|
||||
}
|
||||
|
||||
// TODO: Use the top-level file's path.
|
||||
exec_state.add_module(std::path::PathBuf::from(""));
|
||||
|
||||
// Re-apply the settings, in case the cache was busted.
|
||||
self.engine.reapply_settings(&self.settings, Default::default()).await?;
|
||||
|
||||
@ -1931,17 +2073,35 @@ impl ExecutorContext {
|
||||
exec_state: &mut ExecState,
|
||||
body_type: BodyType,
|
||||
) -> Result<Option<KclValue>, KclError> {
|
||||
if let Some((annotation, source_range)) = program
|
||||
.non_code_meta
|
||||
.start_nodes
|
||||
.iter()
|
||||
.filter_map(|n| {
|
||||
n.annotation(annotations::SETTINGS)
|
||||
.map(|result| (result, n.as_source_range()))
|
||||
})
|
||||
.next()
|
||||
{
|
||||
exec_state
|
||||
.mod_local
|
||||
.settings
|
||||
.update_from_annotation(annotation, source_range)?;
|
||||
}
|
||||
|
||||
let mut last_expr = None;
|
||||
// Iterate over the body of the program.
|
||||
for statement in &program.body {
|
||||
match statement {
|
||||
BodyItem::ImportStatement(import_stmt) => {
|
||||
let source_range = SourceRange::from(import_stmt);
|
||||
let (module_memory, module_exports) =
|
||||
self.open_module(&import_stmt.path, exec_state, source_range).await?;
|
||||
let module_id = self.open_module(&import_stmt.path, exec_state, source_range).await?;
|
||||
|
||||
match &import_stmt.selector {
|
||||
ImportSelector::List { items } => {
|
||||
let (_, module_memory, module_exports) = self
|
||||
.exec_module(module_id, exec_state, ExecutionKind::Isolated, source_range)
|
||||
.await?;
|
||||
for import_item in items {
|
||||
// Extract the item from the module.
|
||||
let item =
|
||||
@ -1965,18 +2125,24 @@ impl ExecutorContext {
|
||||
}
|
||||
|
||||
// Add the item to the current module.
|
||||
exec_state.memory.add(
|
||||
exec_state.mut_memory().add(
|
||||
import_item.identifier(),
|
||||
item.clone(),
|
||||
SourceRange::from(&import_item.name),
|
||||
)?;
|
||||
|
||||
if let ItemVisibility::Export = import_stmt.visibility {
|
||||
exec_state.module_exports.push(import_item.identifier().to_owned());
|
||||
exec_state
|
||||
.mod_local
|
||||
.module_exports
|
||||
.push(import_item.identifier().to_owned());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImportSelector::Glob(_) => {
|
||||
let (_, module_memory, module_exports) = self
|
||||
.exec_module(module_id, exec_state, ExecutionKind::Isolated, source_range)
|
||||
.await?;
|
||||
for name in module_exports.iter() {
|
||||
let item = module_memory.get(name, source_range).map_err(|_err| {
|
||||
KclError::Internal(KclErrorDetails {
|
||||
@ -1984,18 +2150,20 @@ impl ExecutorContext {
|
||||
source_ranges: vec![source_range],
|
||||
})
|
||||
})?;
|
||||
exec_state.memory.add(name, item.clone(), source_range)?;
|
||||
exec_state.mut_memory().add(name, item.clone(), source_range)?;
|
||||
|
||||
if let ItemVisibility::Export = import_stmt.visibility {
|
||||
exec_state.module_exports.push(name.clone());
|
||||
exec_state.mod_local.module_exports.push(name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
ImportSelector::None(_) => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
message: "Importing whole module is not yet implemented, sorry.".to_owned(),
|
||||
source_ranges: vec![source_range],
|
||||
}));
|
||||
ImportSelector::None { .. } => {
|
||||
let name = import_stmt.module_name().unwrap();
|
||||
let item = KclValue::Module {
|
||||
value: module_id,
|
||||
meta: vec![source_range.into()],
|
||||
};
|
||||
exec_state.mut_memory().add(&name, item, source_range)?;
|
||||
}
|
||||
}
|
||||
last_expr = None;
|
||||
@ -2025,11 +2193,11 @@ impl ExecutorContext {
|
||||
StatementKind::Declaration { name: &var_name },
|
||||
)
|
||||
.await?;
|
||||
exec_state.memory.add(&var_name, memory_item, source_range)?;
|
||||
exec_state.mut_memory().add(&var_name, memory_item, source_range)?;
|
||||
|
||||
// Track exports.
|
||||
if let ItemVisibility::Export = variable_declaration.visibility {
|
||||
exec_state.module_exports.push(var_name);
|
||||
exec_state.mod_local.module_exports.push(var_name);
|
||||
}
|
||||
last_expr = None;
|
||||
}
|
||||
@ -2043,7 +2211,7 @@ impl ExecutorContext {
|
||||
StatementKind::Expression,
|
||||
)
|
||||
.await?;
|
||||
exec_state.memory.return_ = Some(value);
|
||||
exec_state.mut_memory().return_ = Some(value);
|
||||
last_expr = None;
|
||||
}
|
||||
}
|
||||
@ -2069,18 +2237,19 @@ impl ExecutorContext {
|
||||
path: &str,
|
||||
exec_state: &mut ExecState,
|
||||
source_range: SourceRange,
|
||||
) -> Result<(ProgramMemory, Vec<String>), KclError> {
|
||||
) -> Result<ModuleId, KclError> {
|
||||
let resolved_path = if let Some(project_dir) = &self.settings.project_directory {
|
||||
project_dir.join(path)
|
||||
} else {
|
||||
std::path::PathBuf::from(&path)
|
||||
};
|
||||
|
||||
if exec_state.import_stack.contains(&resolved_path) {
|
||||
if exec_state.mod_local.import_stack.contains(&resolved_path) {
|
||||
return Err(KclError::ImportCycle(KclErrorDetails {
|
||||
message: format!(
|
||||
"circular import of modules is not allowed: {} -> {}",
|
||||
exec_state
|
||||
.mod_local
|
||||
.import_stack
|
||||
.iter()
|
||||
.map(|p| p.as_path().to_string_lossy())
|
||||
@ -2091,31 +2260,44 @@ impl ExecutorContext {
|
||||
source_ranges: vec![source_range],
|
||||
}));
|
||||
}
|
||||
let module_id = exec_state.add_module(resolved_path.clone());
|
||||
let source = self.fs.read_to_string(&resolved_path, source_range).await?;
|
||||
// TODO handle parsing errors properly
|
||||
let program = crate::parsing::parse_str(&source, module_id).parse_errs_as_err()?;
|
||||
exec_state.add_module(resolved_path.clone(), self, source_range).await
|
||||
}
|
||||
|
||||
exec_state.import_stack.push(resolved_path.clone());
|
||||
let original_execution = self.engine.replace_execution_kind(ExecutionKind::Isolated);
|
||||
let original_memory = std::mem::take(&mut exec_state.memory);
|
||||
let original_exports = std::mem::take(&mut exec_state.module_exports);
|
||||
async fn exec_module(
|
||||
&self,
|
||||
module_id: ModuleId,
|
||||
exec_state: &mut ExecState,
|
||||
exec_kind: ExecutionKind,
|
||||
source_range: SourceRange,
|
||||
) -> Result<(Option<KclValue>, ProgramMemory, Vec<String>), KclError> {
|
||||
// TODO It sucks that we have to clone the whole module AST here
|
||||
let info = exec_state.global.module_infos[&module_id].clone();
|
||||
|
||||
let mut local_state = ModuleState {
|
||||
import_stack: exec_state.mod_local.import_stack.clone(),
|
||||
..Default::default()
|
||||
};
|
||||
local_state.import_stack.push(info.path.clone());
|
||||
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
|
||||
let original_execution = self.engine.replace_execution_kind(exec_kind);
|
||||
|
||||
// The unwrap here is safe since we only elide the AST for the top module.
|
||||
let result = self
|
||||
.inner_execute(&program, exec_state, crate::execution::BodyType::Root)
|
||||
.inner_execute(&info.parsed.unwrap(), exec_state, crate::execution::BodyType::Root)
|
||||
.await;
|
||||
let module_exports = std::mem::replace(&mut exec_state.module_exports, original_exports);
|
||||
let module_memory = std::mem::replace(&mut exec_state.memory, original_memory);
|
||||
self.engine.replace_execution_kind(original_execution);
|
||||
exec_state.import_stack.pop();
|
||||
|
||||
result.map_err(|err| {
|
||||
std::mem::swap(&mut exec_state.mod_local, &mut local_state);
|
||||
self.engine.replace_execution_kind(original_execution);
|
||||
|
||||
let result = result.map_err(|err| {
|
||||
if let KclError::ImportCycle(_) = err {
|
||||
// It was an import cycle. Keep the original message.
|
||||
err.override_source_ranges(vec![source_range])
|
||||
} else {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Error loading imported file. Open it to view more details. {path}: {}",
|
||||
"Error loading imported file. Open it to view more details. {}: {}",
|
||||
info.path.display(),
|
||||
err.message()
|
||||
),
|
||||
source_ranges: vec![source_range],
|
||||
@ -2123,7 +2305,7 @@ impl ExecutorContext {
|
||||
}
|
||||
})?;
|
||||
|
||||
Ok((module_memory, module_exports))
|
||||
Ok((result, local_state.memory, local_state.module_exports))
|
||||
}
|
||||
|
||||
#[async_recursion]
|
||||
@ -2139,8 +2321,23 @@ impl ExecutorContext {
|
||||
Expr::Literal(literal) => KclValue::from(literal),
|
||||
Expr::TagDeclarator(tag) => tag.execute(exec_state).await?,
|
||||
Expr::Identifier(identifier) => {
|
||||
let value = exec_state.memory.get(&identifier.name, identifier.into())?;
|
||||
value.clone()
|
||||
let value = exec_state.memory().get(&identifier.name, identifier.into())?.clone();
|
||||
if let KclValue::Module { value: module_id, meta } = value {
|
||||
let (result, _, _) = self
|
||||
.exec_module(module_id, exec_state, ExecutionKind::Normal, metadata.source_range)
|
||||
.await?;
|
||||
result.ok_or_else(|| {
|
||||
KclError::Semantic(KclErrorDetails {
|
||||
message: format!(
|
||||
"Evaluating module `{}` as part of an assembly did not produce a result",
|
||||
identifier.name
|
||||
),
|
||||
source_ranges: vec![metadata.source_range, meta[0].source_range],
|
||||
})
|
||||
})?
|
||||
} else {
|
||||
value
|
||||
}
|
||||
}
|
||||
Expr::BinaryExpression(binary_expression) => binary_expression.get_result(exec_state, self).await?,
|
||||
Expr::FunctionExpression(function_expression) => {
|
||||
@ -2151,7 +2348,7 @@ impl ExecutorContext {
|
||||
expression: function_expression.clone(),
|
||||
meta: vec![metadata.to_owned()],
|
||||
func: None,
|
||||
memory: Box::new(exec_state.memory.clone()),
|
||||
memory: Box::new(exec_state.memory().clone()),
|
||||
}
|
||||
}
|
||||
Expr::CallExpression(call_expression) => call_expression.execute(exec_state, self).await?,
|
||||
@ -2168,7 +2365,7 @@ impl ExecutorContext {
|
||||
source_ranges: vec![pipe_substitution.into()],
|
||||
}));
|
||||
}
|
||||
StatementKind::Expression => match exec_state.pipe_value.clone() {
|
||||
StatementKind::Expression => match exec_state.mod_local.pipe_value.clone() {
|
||||
Some(x) => x,
|
||||
None => {
|
||||
return Err(KclError::Semantic(KclErrorDetails {
|
||||
@ -2188,7 +2385,9 @@ impl ExecutorContext {
|
||||
let result = self
|
||||
.execute_expr(&expr.expr, exec_state, metadata, statement_kind)
|
||||
.await?;
|
||||
exec_state.memory.add(&expr.label.name, result.clone(), init.into())?;
|
||||
exec_state
|
||||
.mut_memory()
|
||||
.add(&expr.label.name, result.clone(), init.into())?;
|
||||
// TODO this lets us use the label as a variable name, but not as a tag in most cases
|
||||
result
|
||||
}
|
||||
@ -2373,12 +2572,12 @@ pub(crate) async fn call_user_defined_function(
|
||||
|
||||
// Execute the function body using the memory we just created.
|
||||
let (result, fn_memory) = {
|
||||
let previous_memory = std::mem::replace(&mut exec_state.memory, fn_memory);
|
||||
let previous_memory = std::mem::replace(&mut exec_state.mod_local.memory, fn_memory);
|
||||
let result = ctx
|
||||
.inner_execute(&function_expression.body, exec_state, BodyType::Block)
|
||||
.await;
|
||||
// Restore the previous memory.
|
||||
let fn_memory = std::mem::replace(&mut exec_state.memory, previous_memory);
|
||||
let fn_memory = std::mem::replace(&mut exec_state.mod_local.memory, previous_memory);
|
||||
|
||||
(result, fn_memory)
|
||||
};
|
||||
@ -2403,12 +2602,12 @@ pub(crate) async fn call_user_defined_function_kw(
|
||||
|
||||
// Execute the function body using the memory we just created.
|
||||
let (result, fn_memory) = {
|
||||
let previous_memory = std::mem::replace(&mut exec_state.memory, fn_memory);
|
||||
let previous_memory = std::mem::replace(&mut exec_state.mod_local.memory, fn_memory);
|
||||
let result = ctx
|
||||
.inner_execute(&function_expression.body, exec_state, BodyType::Block)
|
||||
.await;
|
||||
// Restore the previous memory.
|
||||
let fn_memory = std::mem::replace(&mut exec_state.memory, previous_memory);
|
||||
let fn_memory = std::mem::replace(&mut exec_state.mod_local.memory, previous_memory);
|
||||
|
||||
(result, fn_memory)
|
||||
};
|
||||
@ -2433,7 +2632,7 @@ mod tests {
|
||||
OldAstState,
|
||||
};
|
||||
|
||||
pub async fn parse_execute(code: &str) -> Result<(Program, ExecutorContext, ExecState)> {
|
||||
async fn parse_execute(code: &str) -> Result<(Program, ExecutorContext, ExecState)> {
|
||||
let program = Program::parse_no_errs(code)?;
|
||||
|
||||
let ctx = ExecutorContext {
|
||||
@ -2450,6 +2649,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// Convenience function to get a JSON value from memory and unwrap.
|
||||
#[track_caller]
|
||||
fn mem_get_json(memory: &ProgramMemory, name: &str) -> KclValue {
|
||||
memory.get(name, SourceRange::default()).unwrap().to_owned()
|
||||
}
|
||||
@ -2880,21 +3080,21 @@ let shape = layer() |> patternTransform(10, transform, %)
|
||||
async fn test_math_execute_with_functions() {
|
||||
let ast = r#"const myVar = 2 + min(100, -1 + legLen(5, 3))"#;
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(5.0, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap());
|
||||
assert_eq!(5.0, mem_get_json(exec_state.memory(), "myVar").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_execute() {
|
||||
let ast = r#"const myVar = 1 + 2 * (3 - 4) / -5 + 6"#;
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(7.4, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap());
|
||||
assert_eq!(7.4, mem_get_json(exec_state.memory(), "myVar").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_math_execute_start_negative() {
|
||||
let ast = r#"const myVar = -5 + 6"#;
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(1.0, mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap());
|
||||
assert_eq!(1.0, mem_get_json(exec_state.memory(), "myVar").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2903,7 +3103,7 @@ let shape = layer() |> patternTransform(10, transform, %)
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(
|
||||
std::f64::consts::TAU,
|
||||
mem_get_json(&exec_state.memory, "myVar").as_f64().unwrap()
|
||||
mem_get_json(exec_state.memory(), "myVar").as_f64().unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
@ -2911,7 +3111,7 @@ let shape = layer() |> patternTransform(10, transform, %)
|
||||
async fn test_math_define_decimal_without_leading_zero() {
|
||||
let ast = r#"let thing = .4 + 7"#;
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(7.4, mem_get_json(&exec_state.memory, "thing").as_f64().unwrap());
|
||||
assert_eq!(7.4, mem_get_json(exec_state.memory(), "thing").as_f64().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -2951,10 +3151,10 @@ fn check = (x) => {
|
||||
check(false)
|
||||
"#;
|
||||
let (_, _, exec_state) = parse_execute(ast).await.unwrap();
|
||||
assert_eq!(false, mem_get_json(&exec_state.memory, "notTrue").as_bool().unwrap());
|
||||
assert_eq!(true, mem_get_json(&exec_state.memory, "notFalse").as_bool().unwrap());
|
||||
assert_eq!(true, mem_get_json(&exec_state.memory, "c").as_bool().unwrap());
|
||||
assert_eq!(false, mem_get_json(&exec_state.memory, "d").as_bool().unwrap());
|
||||
assert_eq!(false, mem_get_json(exec_state.memory(), "notTrue").as_bool().unwrap());
|
||||
assert_eq!(true, mem_get_json(exec_state.memory(), "notFalse").as_bool().unwrap());
|
||||
assert_eq!(true, mem_get_json(exec_state.memory(), "c").as_bool().unwrap());
|
||||
assert_eq!(false, mem_get_json(exec_state.memory(), "d").as_bool().unwrap());
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
@ -3635,4 +3835,65 @@ shell({ faces = ['end'], thickness = 0.25 }, firstSketch)"#;
|
||||
|
||||
assert_eq!(result, None);
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_ids_stable_between_executions() {
|
||||
let code = r#"sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([61.74, 206.13], %)
|
||||
|> xLine(305.11, %, $seg01)
|
||||
|> yLine(-291.85, %)
|
||||
|> xLine(-segLen(seg01), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
|> extrude(40.14, %)
|
||||
|> shell({
|
||||
faces: [seg01],
|
||||
thickness: 3.14,
|
||||
}, %)
|
||||
"#;
|
||||
|
||||
let ctx = crate::test_server::new_context(UnitLength::Mm, true, None)
|
||||
.await
|
||||
.unwrap();
|
||||
let old_program = crate::Program::parse_no_errs(code).unwrap();
|
||||
// Execute the program.
|
||||
let mut exec_state = Default::default();
|
||||
let cache_info = crate::CacheInformation {
|
||||
old: None,
|
||||
new_ast: old_program.ast.clone(),
|
||||
};
|
||||
ctx.run(cache_info, &mut exec_state).await.unwrap();
|
||||
|
||||
// Get the id_generator from the first execution.
|
||||
let id_generator = exec_state.global.id_generator.clone();
|
||||
|
||||
let code = r#"sketch001 = startSketchOn('XZ')
|
||||
|> startProfileAt([62.74, 206.13], %)
|
||||
|> xLine(305.11, %, $seg01)
|
||||
|> yLine(-291.85, %)
|
||||
|> xLine(-segLen(seg01), %)
|
||||
|> lineTo([profileStartX(%), profileStartY(%)], %)
|
||||
|> close(%)
|
||||
|> extrude(40.14, %)
|
||||
|> shell({
|
||||
faces: [seg01],
|
||||
thickness: 3.14,
|
||||
}, %)
|
||||
"#;
|
||||
|
||||
// Execute a slightly different program again.
|
||||
let program: Program = crate::Program::parse_no_errs(code).unwrap();
|
||||
let cache_info = crate::CacheInformation {
|
||||
old: Some(crate::OldAstState {
|
||||
ast: old_program.ast.clone(),
|
||||
exec_state: exec_state.clone(),
|
||||
settings: ctx.settings.clone(),
|
||||
}),
|
||||
new_ast: program.ast.clone(),
|
||||
};
|
||||
// Execute the program.
|
||||
ctx.run(cache_info, &mut exec_state).await.unwrap();
|
||||
|
||||
assert_eq!(id_generator, exec_state.global.id_generator);
|
||||
}
|
||||
}
|
||||
|
||||
@ -726,11 +726,11 @@ impl Backend {
|
||||
drop(last_successful_ast_state);
|
||||
|
||||
self.memory_map
|
||||
.insert(params.uri.to_string(), exec_state.memory.clone());
|
||||
.insert(params.uri.to_string(), exec_state.memory().clone());
|
||||
|
||||
// Send the notification to the client that the memory was updated.
|
||||
self.client
|
||||
.send_notification::<custom_notifications::MemoryUpdated>(exec_state.memory)
|
||||
.send_notification::<custom_notifications::MemoryUpdated>(exec_state.mod_local.memory)
|
||||
.await;
|
||||
|
||||
Ok(())
|
||||
@ -1216,7 +1216,7 @@ impl LanguageServer for Backend {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
// Get the completion items forem the ast.
|
||||
// Get the completion items for the ast.
|
||||
let Ok(variables) = ast.completion_items() else {
|
||||
return Ok(Some(CompletionResponse::Array(completions)));
|
||||
};
|
||||
|
||||
@ -822,6 +822,59 @@ async fn test_kcl_lsp_completions_const_raw() {
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_kcl_lsp_completions_import() {
|
||||
let server = kcl_lsp_server(false).await.unwrap();
|
||||
|
||||
// Send open file.
|
||||
server
|
||||
.did_open(tower_lsp::lsp_types::DidOpenTextDocumentParams {
|
||||
text_document: tower_lsp::lsp_types::TextDocumentItem {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
language_id: "kcl".to_string(),
|
||||
version: 1,
|
||||
text: r#"import boo, baz as bux from 'bar.kcl'
|
||||
//import 'bar.kcl'
|
||||
x = b"#
|
||||
.to_string(),
|
||||
},
|
||||
})
|
||||
.await;
|
||||
|
||||
// Send completion request.
|
||||
let completions = server
|
||||
.completion(tower_lsp::lsp_types::CompletionParams {
|
||||
text_document_position: tower_lsp::lsp_types::TextDocumentPositionParams {
|
||||
text_document: tower_lsp::lsp_types::TextDocumentIdentifier {
|
||||
uri: "file:///test.kcl".try_into().unwrap(),
|
||||
},
|
||||
position: tower_lsp::lsp_types::Position { line: 2, character: 5 },
|
||||
},
|
||||
context: None,
|
||||
partial_result_params: Default::default(),
|
||||
work_done_progress_params: Default::default(),
|
||||
})
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
// Check the completions.
|
||||
if let tower_lsp::lsp_types::CompletionResponse::Array(completions) = completions {
|
||||
assert!(completions.len() > 10);
|
||||
// Find the one with label "foo".
|
||||
completions.iter().find(|completion| completion.label == "boo").unwrap();
|
||||
// completions
|
||||
// .iter()
|
||||
// .find(|completion| completion.label == "bar")
|
||||
// .unwrap();
|
||||
completions.iter().find(|completion| completion.label == "bux").unwrap();
|
||||
assert!(!completions.iter().any(|completion| completion.label == "baz"));
|
||||
// Find the one with label "bar".
|
||||
} else {
|
||||
panic!("Expected array of completions");
|
||||
}
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn test_kcl_lsp_on_hover() {
|
||||
let server = kcl_lsp_server(false).await.unwrap();
|
||||
@ -2344,6 +2397,7 @@ async fn kcl_test_kcl_lsp_diagnostics_on_execution_error() {
|
||||
.await;
|
||||
|
||||
// Get the diagnostics.
|
||||
// TODO warnings being stomped by execution errors?
|
||||
assert_diagnostic_count(server.diagnostics_map.get("file:///test.kcl").as_deref(), 1);
|
||||
|
||||
// Update the text.
|
||||
|
||||
@ -59,8 +59,8 @@ impl ImportStatement {
|
||||
}
|
||||
}
|
||||
ImportSelector::Glob(_) => hasher.update(b"ImportSelector::Glob"),
|
||||
ImportSelector::None(None) => hasher.update(b"ImportSelector::None"),
|
||||
ImportSelector::None(Some(alias)) => {
|
||||
ImportSelector::None { alias: None } => hasher.update(b"ImportSelector::None"),
|
||||
ImportSelector::None { alias: Some(alias) } => {
|
||||
hasher.update(b"ImportSelector::None");
|
||||
hasher.update(alias.compute_digest());
|
||||
}
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
//! Data types for the AST.
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
ops::{Deref, DerefMut, RangeInclusive},
|
||||
rc::Rc,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
@ -183,21 +185,24 @@ pub struct Program {
|
||||
impl Node<Program> {
|
||||
/// Walk the ast and get all the variables and tags as completion items.
|
||||
pub fn completion_items<'a>(&'a self) -> Result<Vec<CompletionItem>> {
|
||||
let completions = Arc::new(Mutex::new(vec![]));
|
||||
let completions = Rc::new(RefCell::new(vec![]));
|
||||
crate::walk::walk(self, |node: crate::walk::Node<'a>| {
|
||||
let mut findings = completions.lock().map_err(|_| anyhow::anyhow!("mutex"))?;
|
||||
let mut findings = completions.borrow_mut();
|
||||
match node {
|
||||
crate::walk::Node::TagDeclarator(tag) => {
|
||||
findings.push(tag.into());
|
||||
}
|
||||
crate::walk::Node::VariableDeclaration(variable) => {
|
||||
findings.extend::<Vec<CompletionItem>>(variable.into());
|
||||
findings.extend::<Vec<CompletionItem>>((&variable.inner).into());
|
||||
}
|
||||
crate::walk::Node::ImportStatement(i) => {
|
||||
findings.extend::<Vec<CompletionItem>>((&i.inner).into());
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
Ok::<bool, anyhow::Error>(true)
|
||||
})?;
|
||||
let x = completions.lock().unwrap();
|
||||
let x = completions.take();
|
||||
Ok(x.clone())
|
||||
}
|
||||
|
||||
@ -995,52 +1000,22 @@ pub struct NonCodeNode {
|
||||
pub digest: Option<Digest>,
|
||||
}
|
||||
|
||||
impl Node<NonCodeNode> {
|
||||
pub fn format(&self, indentation: &str) -> String {
|
||||
match &self.value {
|
||||
NonCodeValue::InlineComment {
|
||||
value,
|
||||
style: CommentStyle::Line,
|
||||
} => format!(" // {}\n", value),
|
||||
NonCodeValue::InlineComment {
|
||||
value,
|
||||
style: CommentStyle::Block,
|
||||
} => format!(" /* {} */", value),
|
||||
NonCodeValue::BlockComment { value, style } => match style {
|
||||
CommentStyle::Block => format!("{}/* {} */", indentation, value),
|
||||
CommentStyle::Line => {
|
||||
if value.trim().is_empty() {
|
||||
format!("{}//\n", indentation)
|
||||
} else {
|
||||
format!("{}// {}\n", indentation, value.trim())
|
||||
}
|
||||
}
|
||||
},
|
||||
NonCodeValue::NewLineBlockComment { value, style } => {
|
||||
let add_start_new_line = if self.start == 0 { "" } else { "\n\n" };
|
||||
match style {
|
||||
CommentStyle::Block => format!("{}{}/* {} */\n", add_start_new_line, indentation, value),
|
||||
CommentStyle::Line => {
|
||||
if value.trim().is_empty() {
|
||||
format!("{}{}//\n", add_start_new_line, indentation)
|
||||
} else {
|
||||
format!("{}{}// {}\n", add_start_new_line, indentation, value.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NonCodeValue::NewLine => "\n\n".to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl NonCodeNode {
|
||||
#[cfg(test)]
|
||||
pub fn value(&self) -> String {
|
||||
match &self.value {
|
||||
NonCodeValue::InlineComment { value, style: _ } => value.clone(),
|
||||
NonCodeValue::BlockComment { value, style: _ } => value.clone(),
|
||||
NonCodeValue::NewLineBlockComment { value, style: _ } => value.clone(),
|
||||
NonCodeValue::NewLine => "\n\n".to_string(),
|
||||
NonCodeValue::Annotation { name, .. } => name.name.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn annotation(&self, expected_name: &str) -> Option<&NonCodeValue> {
|
||||
match &self.value {
|
||||
a @ NonCodeValue::Annotation { name, .. } if name.name == expected_name => Some(a),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1058,6 +1033,7 @@ pub enum CommentStyle {
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
#[ts(export)]
|
||||
#[serde(tag = "type", rename_all = "camelCase")]
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
pub enum NonCodeValue {
|
||||
/// An inline comment.
|
||||
/// Here are examples:
|
||||
@ -1090,6 +1066,10 @@ pub enum NonCodeValue {
|
||||
// A new line like `\n\n` NOT a new line like `\n`.
|
||||
// This is also not a comment.
|
||||
NewLine,
|
||||
Annotation {
|
||||
name: Node<Identifier>,
|
||||
properties: Option<Vec<Node<ObjectProperty>>>,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Clone, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
@ -1225,7 +1205,7 @@ pub enum ImportSelector {
|
||||
Glob(Node<()>),
|
||||
/// Import the module itself (the param is an optional alias).
|
||||
/// E.g., `import "foo.kcl" as bar`
|
||||
None(Option<Node<Identifier>>),
|
||||
None { alias: Option<Node<Identifier>> },
|
||||
}
|
||||
|
||||
impl ImportSelector {
|
||||
@ -1244,8 +1224,8 @@ impl ImportSelector {
|
||||
None
|
||||
}
|
||||
ImportSelector::Glob(_) => None,
|
||||
ImportSelector::None(None) => None,
|
||||
ImportSelector::None(Some(alias)) => {
|
||||
ImportSelector::None { alias: None } => None,
|
||||
ImportSelector::None { alias: Some(alias) } => {
|
||||
let alias_source_range = SourceRange::from(&*alias);
|
||||
if !alias_source_range.contains(pos) {
|
||||
return None;
|
||||
@ -1264,8 +1244,8 @@ impl ImportSelector {
|
||||
}
|
||||
}
|
||||
ImportSelector::Glob(_) => {}
|
||||
ImportSelector::None(None) => {}
|
||||
ImportSelector::None(Some(alias)) => alias.rename(old_name, new_name),
|
||||
ImportSelector::None { alias: None } => {}
|
||||
ImportSelector::None { alias: Some(alias) } => alias.rename(old_name, new_name),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1296,30 +1276,10 @@ impl Node<ImportStatement> {
|
||||
false
|
||||
}
|
||||
ImportSelector::Glob(_) => false,
|
||||
ImportSelector::None(_) => name == self.module_name().unwrap(),
|
||||
ImportSelector::None { .. } => name == self.module_name().unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the name of the module object for this import.
|
||||
/// Validated during parsing and guaranteed to return `Some` if the statement imports
|
||||
/// the module itself (i.e., self.selector is ImportSelector::None).
|
||||
pub fn module_name(&self) -> Option<String> {
|
||||
if let ImportSelector::None(Some(alias)) = &self.selector {
|
||||
return Some(alias.name.clone());
|
||||
}
|
||||
|
||||
let mut parts = self.path.split('.');
|
||||
let name = parts.next()?;
|
||||
let ext = parts.next()?;
|
||||
let rest = parts.next();
|
||||
|
||||
if rest.is_some() || ext != "kcl" {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(name.to_owned())
|
||||
}
|
||||
|
||||
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
||||
ConstraintLevel::Full {
|
||||
source_ranges: vec![self.into()],
|
||||
@ -1335,6 +1295,59 @@ impl ImportStatement {
|
||||
pub fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
|
||||
self.selector.rename_identifiers(old_name, new_name);
|
||||
}
|
||||
|
||||
/// Get the name of the module object for this import.
|
||||
/// Validated during parsing and guaranteed to return `Some` if the statement imports
|
||||
/// the module itself (i.e., self.selector is ImportSelector::None).
|
||||
pub fn module_name(&self) -> Option<String> {
|
||||
if let ImportSelector::None { alias: Some(alias) } = &self.selector {
|
||||
return Some(alias.name.clone());
|
||||
}
|
||||
|
||||
let mut parts = self.path.split('.');
|
||||
let name = parts.next()?;
|
||||
let ext = parts.next()?;
|
||||
let rest = parts.next();
|
||||
|
||||
if rest.is_some() || ext != "kcl" {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(name.to_owned())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<&ImportStatement> for Vec<CompletionItem> {
|
||||
fn from(import: &ImportStatement) -> Self {
|
||||
match &import.selector {
|
||||
ImportSelector::List { items } => {
|
||||
items
|
||||
.iter()
|
||||
.map(|i| {
|
||||
let as_str = match &i.alias {
|
||||
Some(s) => format!(" as {}", s.name),
|
||||
None => String::new(),
|
||||
};
|
||||
CompletionItem {
|
||||
label: i.identifier().to_owned(),
|
||||
// TODO we can only find this after opening the module
|
||||
kind: None,
|
||||
detail: Some(format!("{}{as_str} from '{}'", i.name.name, import.path)),
|
||||
..CompletionItem::default()
|
||||
}
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
// TODO can't do completion for glob imports without static name resolution
|
||||
ImportSelector::Glob(_) => vec![],
|
||||
ImportSelector::None { .. } => vec![CompletionItem {
|
||||
label: import.module_name().unwrap(),
|
||||
kind: Some(CompletionItemKind::MODULE),
|
||||
detail: Some(format!("from '{}'", import.path)),
|
||||
..CompletionItem::default()
|
||||
}],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||
@ -1605,30 +1618,16 @@ pub struct VariableDeclaration {
|
||||
pub digest: Option<Digest>,
|
||||
}
|
||||
|
||||
impl From<&Node<VariableDeclaration>> for Vec<CompletionItem> {
|
||||
fn from(declaration: &Node<VariableDeclaration>) -> Self {
|
||||
impl From<&VariableDeclaration> for Vec<CompletionItem> {
|
||||
fn from(declaration: &VariableDeclaration) -> Self {
|
||||
vec![CompletionItem {
|
||||
label: declaration.declaration.id.name.to_string(),
|
||||
label_details: None,
|
||||
kind: Some(match declaration.inner.kind {
|
||||
kind: Some(match declaration.kind {
|
||||
VariableKind::Const => CompletionItemKind::CONSTANT,
|
||||
VariableKind::Fn => CompletionItemKind::FUNCTION,
|
||||
}),
|
||||
detail: Some(declaration.inner.kind.to_string()),
|
||||
documentation: None,
|
||||
deprecated: None,
|
||||
preselect: None,
|
||||
sort_text: None,
|
||||
filter_text: None,
|
||||
insert_text: None,
|
||||
insert_text_format: None,
|
||||
insert_text_mode: None,
|
||||
text_edit: None,
|
||||
additional_text_edits: None,
|
||||
command: None,
|
||||
commit_characters: None,
|
||||
data: None,
|
||||
tags: None,
|
||||
detail: Some(declaration.kind.to_string()),
|
||||
..CompletionItem::default()
|
||||
}]
|
||||
}
|
||||
}
|
||||
@ -1928,6 +1927,10 @@ impl Identifier {
|
||||
})
|
||||
}
|
||||
|
||||
pub fn is_nameable(&self) -> bool {
|
||||
!self.name.starts_with('_')
|
||||
}
|
||||
|
||||
/// Rename all identifiers that have the old name to the new given name.
|
||||
fn rename(&mut self, old_name: &str, new_name: &str) {
|
||||
if self.name == old_name {
|
||||
@ -2568,6 +2571,14 @@ pub enum BinaryOperator {
|
||||
#[serde(rename = "<=")]
|
||||
#[display("<=")]
|
||||
Lte,
|
||||
/// Are both left and right true?
|
||||
#[serde(rename = "&")]
|
||||
#[display("&")]
|
||||
And,
|
||||
/// Is either left or right true?
|
||||
#[serde(rename = "|")]
|
||||
#[display("|")]
|
||||
Or,
|
||||
}
|
||||
|
||||
/// Mathematical associativity.
|
||||
@ -2602,6 +2613,8 @@ impl BinaryOperator {
|
||||
BinaryOperator::Gte => *b"gte",
|
||||
BinaryOperator::Lt => *b"ltr",
|
||||
BinaryOperator::Lte => *b"lte",
|
||||
BinaryOperator::And => *b"and",
|
||||
BinaryOperator::Or => *b"lor",
|
||||
}
|
||||
}
|
||||
|
||||
@ -2614,6 +2627,8 @@ impl BinaryOperator {
|
||||
BinaryOperator::Pow => 13,
|
||||
Self::Gt | Self::Gte | Self::Lt | Self::Lte => 9,
|
||||
Self::Eq | Self::Neq => 8,
|
||||
Self::And => 7,
|
||||
Self::Or => 6,
|
||||
}
|
||||
}
|
||||
|
||||
@ -2624,6 +2639,7 @@ impl BinaryOperator {
|
||||
Self::Add | Self::Sub | Self::Mul | Self::Div | Self::Mod => Associativity::Left,
|
||||
Self::Pow => Associativity::Right,
|
||||
Self::Gt | Self::Gte | Self::Lt | Self::Lte | Self::Eq | Self::Neq => Associativity::Left, // I don't know if this is correct
|
||||
Self::And | Self::Or => Associativity::Left,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ use crate::{
|
||||
SourceRange,
|
||||
};
|
||||
|
||||
use super::ast::types::LabelledExpression;
|
||||
use super::{ast::types::LabelledExpression, token::NumericSuffix};
|
||||
|
||||
thread_local! {
|
||||
/// The current `ParseContext`. `None` if parsing is not currently happening on this thread.
|
||||
@ -96,10 +96,6 @@ impl ParseContext {
|
||||
*e = err;
|
||||
return;
|
||||
}
|
||||
|
||||
if e.source_range.start() > err.source_range.end() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
errors.push(err);
|
||||
});
|
||||
@ -287,38 +283,86 @@ fn non_code_node(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
alt((non_code_node_leading_whitespace, non_code_node_no_leading_whitespace)).parse_next(i)
|
||||
}
|
||||
|
||||
fn annotation(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
let at = at_sign.parse_next(i)?;
|
||||
let name = binding_name.parse_next(i)?;
|
||||
let mut end = name.end;
|
||||
|
||||
let properties = if peek(open_paren).parse_next(i).is_ok() {
|
||||
open_paren(i)?;
|
||||
ignore_whitespace(i);
|
||||
let properties: Vec<_> = separated(
|
||||
0..,
|
||||
separated_pair(
|
||||
terminated(identifier, opt(whitespace)),
|
||||
terminated(one_of((TokenType::Operator, "=")), opt(whitespace)),
|
||||
expression,
|
||||
)
|
||||
.map(|(key, value)| Node {
|
||||
start: key.start,
|
||||
end: value.end(),
|
||||
module_id: key.module_id,
|
||||
inner: ObjectProperty {
|
||||
key,
|
||||
value,
|
||||
digest: None,
|
||||
},
|
||||
}),
|
||||
comma_sep,
|
||||
)
|
||||
.parse_next(i)?;
|
||||
ignore_trailing_comma(i);
|
||||
ignore_whitespace(i);
|
||||
end = close_paren(i)?.end;
|
||||
Some(properties)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let value = NonCodeValue::Annotation { name, properties };
|
||||
Ok(Node::new(
|
||||
NonCodeNode { value, digest: None },
|
||||
at.start,
|
||||
end,
|
||||
at.module_id,
|
||||
))
|
||||
}
|
||||
|
||||
// Matches remaining three cases of NonCodeValue
|
||||
fn non_code_node_no_leading_whitespace(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
any.verify_map(|token: Token| {
|
||||
if token.is_code_token() {
|
||||
None
|
||||
} else {
|
||||
let value = match token.token_type {
|
||||
TokenType::Whitespace if token.value.contains("\n\n") => NonCodeValue::NewLine,
|
||||
TokenType::LineComment => NonCodeValue::BlockComment {
|
||||
value: token.value.trim_start_matches("//").trim().to_owned(),
|
||||
style: CommentStyle::Line,
|
||||
},
|
||||
TokenType::BlockComment => NonCodeValue::BlockComment {
|
||||
style: CommentStyle::Block,
|
||||
value: token
|
||||
.value
|
||||
.trim_start_matches("/*")
|
||||
.trim_end_matches("*/")
|
||||
.trim()
|
||||
.to_owned(),
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
Some(Node::new(
|
||||
NonCodeNode { value, digest: None },
|
||||
token.start,
|
||||
token.end,
|
||||
token.module_id,
|
||||
))
|
||||
}
|
||||
})
|
||||
.context(expected("Non-code token (comments or whitespace)"))
|
||||
alt((
|
||||
annotation,
|
||||
any.verify_map(|token: Token| {
|
||||
if token.is_code_token() {
|
||||
None
|
||||
} else {
|
||||
let value = match token.token_type {
|
||||
TokenType::Whitespace if token.value.contains("\n\n") => NonCodeValue::NewLine,
|
||||
TokenType::LineComment => NonCodeValue::BlockComment {
|
||||
value: token.value.trim_start_matches("//").trim().to_owned(),
|
||||
style: CommentStyle::Line,
|
||||
},
|
||||
TokenType::BlockComment => NonCodeValue::BlockComment {
|
||||
style: CommentStyle::Block,
|
||||
value: token
|
||||
.value
|
||||
.trim_start_matches("/*")
|
||||
.trim_end_matches("*/")
|
||||
.trim()
|
||||
.to_owned(),
|
||||
},
|
||||
_ => return None,
|
||||
};
|
||||
Some(Node::new(
|
||||
NonCodeNode { value, digest: None },
|
||||
token.start,
|
||||
token.end,
|
||||
token.module_id,
|
||||
))
|
||||
}
|
||||
})
|
||||
.context(expected("Non-code token (comments or whitespace)")),
|
||||
))
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
@ -351,22 +395,6 @@ fn pipe_expression(i: &mut TokenSlice) -> PResult<Node<PipeExpression>> {
|
||||
))
|
||||
.parse_next(i)?;
|
||||
|
||||
// All child parsers have been run.
|
||||
// First, ensure they all have a % in their args.
|
||||
let calls_without_substitution = tail.iter().find_map(|(_nc, call_expr, _nc2)| {
|
||||
if !call_expr.has_substitution_arg() {
|
||||
Some(call_expr.into())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
});
|
||||
if let Some(source_range) = calls_without_substitution {
|
||||
let err = CompilationError::fatal(
|
||||
source_range,
|
||||
"All expressions in a pipeline must use the % (substitution operator)",
|
||||
);
|
||||
return Err(ErrMode::Cut(err.into()));
|
||||
}
|
||||
// Time to structure the return value.
|
||||
let mut code_count = 0;
|
||||
let mut max_noncode_end = 0;
|
||||
@ -457,10 +485,17 @@ pub(crate) fn unsigned_number_literal(i: &mut TokenSlice) -> PResult<Node<Litera
|
||||
let (value, token) = any
|
||||
.try_map(|token: Token| match token.token_type {
|
||||
TokenType::Number => {
|
||||
let x: f64 = token.value.parse().map_err(|_| {
|
||||
let x: f64 = token.numeric_value().ok_or_else(|| {
|
||||
CompilationError::fatal(token.as_source_range(), format!("Invalid float: {}", token.value))
|
||||
})?;
|
||||
|
||||
if token.numeric_suffix().is_some() {
|
||||
ParseContext::err(CompilationError::err(
|
||||
(&token).into(),
|
||||
"Unit of Measure suffixes are experimental and currently do nothing.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok((LiteralValue::Number(x), token))
|
||||
}
|
||||
_ => Err(CompilationError::fatal(token.as_source_range(), "invalid literal")),
|
||||
@ -501,6 +536,8 @@ fn binary_operator(i: &mut TokenSlice) -> PResult<BinaryOperator> {
|
||||
">=" => BinaryOperator::Gte,
|
||||
"<" => BinaryOperator::Lt,
|
||||
"<=" => BinaryOperator::Lte,
|
||||
"|" => BinaryOperator::Or,
|
||||
"&" => BinaryOperator::And,
|
||||
_ => {
|
||||
return Err(CompilationError::fatal(
|
||||
token.as_source_range(),
|
||||
@ -754,7 +791,7 @@ fn array_end_start(i: &mut TokenSlice) -> PResult<Node<ArrayRangeExpression>> {
|
||||
}
|
||||
|
||||
fn object_property_same_key_and_val(i: &mut TokenSlice) -> PResult<Node<ObjectProperty>> {
|
||||
let key = identifier.context(expected("the property's key (the name or identifier of the property), e.g. in 'height: 4', 'height' is the property key")).parse_next(i)?;
|
||||
let key = nameable_identifier.context(expected("the property's key (the name or identifier of the property), e.g. in 'height: 4', 'height' is the property key")).parse_next(i)?;
|
||||
ignore_whitespace(i);
|
||||
Ok(Node {
|
||||
start: key.start,
|
||||
@ -778,7 +815,7 @@ fn object_property(i: &mut TokenSlice) -> PResult<Node<ObjectProperty>> {
|
||||
))
|
||||
.parse_next(i)?;
|
||||
ignore_whitespace(i);
|
||||
let expr = expression
|
||||
let expr = expression_but_not_ascription
|
||||
.context(expected(
|
||||
"the value which you're setting the property to, e.g. in 'height: 4', the value is 4",
|
||||
))
|
||||
@ -1086,7 +1123,7 @@ fn member_expression_dot(i: &mut TokenSlice) -> PResult<(LiteralIdentifier, usiz
|
||||
period.parse_next(i)?;
|
||||
let property = alt((
|
||||
sketch_keyword.map(Box::new).map(LiteralIdentifier::Identifier),
|
||||
identifier.map(Box::new).map(LiteralIdentifier::Identifier),
|
||||
nameable_identifier.map(Box::new).map(LiteralIdentifier::Identifier),
|
||||
))
|
||||
.parse_next(i)?;
|
||||
let end = property.end();
|
||||
@ -1099,7 +1136,7 @@ fn member_expression_subscript(i: &mut TokenSlice) -> PResult<(LiteralIdentifier
|
||||
let property = alt((
|
||||
sketch_keyword.map(Box::new).map(LiteralIdentifier::Identifier),
|
||||
literal.map(LiteralIdentifier::Literal),
|
||||
identifier.map(Box::new).map(LiteralIdentifier::Identifier),
|
||||
nameable_identifier.map(Box::new).map(LiteralIdentifier::Identifier),
|
||||
))
|
||||
.parse_next(i)?;
|
||||
|
||||
@ -1113,7 +1150,7 @@ fn member_expression_subscript(i: &mut TokenSlice) -> PResult<(LiteralIdentifier
|
||||
fn member_expression(i: &mut TokenSlice) -> PResult<Node<MemberExpression>> {
|
||||
// This is an identifier, followed by a sequence of members (aka properties)
|
||||
// First, the identifier.
|
||||
let id = identifier.context(expected("the identifier of the object whose property you're trying to access, e.g. in 'shape.size.width', 'shape' is the identifier")).parse_next(i)?;
|
||||
let id = nameable_identifier.context(expected("the identifier of the object whose property you're trying to access, e.g. in 'shape.size.width', 'shape' is the identifier")).parse_next(i)?;
|
||||
// Now a sequence of members.
|
||||
let member = alt((member_expression_dot, member_expression_subscript)).context(expected("a member/property, e.g. size.x and size['height'] and size[0] are all different ways to access a member/property of 'size'"));
|
||||
let mut members: Vec<_> = repeat(1.., member)
|
||||
@ -1186,6 +1223,7 @@ fn noncode_just_after_code(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
x @ NonCodeValue::InlineComment { .. } => x,
|
||||
x @ NonCodeValue::NewLineBlockComment { .. } => x,
|
||||
x @ NonCodeValue::NewLine => x,
|
||||
x @ NonCodeValue::Annotation { .. } => x,
|
||||
};
|
||||
Node::new(
|
||||
NonCodeNode { value, ..nc.inner },
|
||||
@ -1206,6 +1244,7 @@ fn noncode_just_after_code(i: &mut TokenSlice) -> PResult<Node<NonCodeNode>> {
|
||||
x @ NonCodeValue::InlineComment { .. } => x,
|
||||
x @ NonCodeValue::NewLineBlockComment { .. } => x,
|
||||
x @ NonCodeValue::NewLine => x,
|
||||
x @ NonCodeValue::Annotation { .. } => x,
|
||||
};
|
||||
Node::new(NonCodeNode { value, ..nc.inner }, nc.start, nc.end, nc.module_id)
|
||||
}
|
||||
@ -1245,7 +1284,7 @@ fn body_items_within_function(i: &mut TokenSlice) -> PResult<WithinFunction> {
|
||||
(import_stmt.map(BodyItem::ImportStatement), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
||||
Token { ref value, .. } if value == "return" =>
|
||||
(return_stmt.map(BodyItem::ReturnStatement), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
||||
token if !token.is_code_token() => {
|
||||
token if !token.is_code_token() || token.token_type == TokenType::At => {
|
||||
non_code_node.map(WithinFunction::NonCode)
|
||||
},
|
||||
_ =>
|
||||
@ -1444,7 +1483,7 @@ fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> {
|
||||
require_whitespace(i)?;
|
||||
|
||||
let (mut selector, path) = alt((
|
||||
string_literal.map(|s| (ImportSelector::None(None), Some(s))),
|
||||
string_literal.map(|s| (ImportSelector::None { alias: None }, Some(s))),
|
||||
glob.map(|t| {
|
||||
let s = t.as_source_range();
|
||||
(
|
||||
@ -1507,7 +1546,7 @@ fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> {
|
||||
));
|
||||
}
|
||||
|
||||
if let ImportSelector::None(ref mut a) = selector {
|
||||
if let ImportSelector::None { alias: ref mut a } = selector {
|
||||
if let Some(alias) = opt(preceded(
|
||||
(whitespace, import_as_keyword, whitespace),
|
||||
identifier.context(expected("an identifier to alias the import")),
|
||||
@ -1553,7 +1592,9 @@ fn import_stmt(i: &mut TokenSlice) -> PResult<BoxNode<ImportStatement>> {
|
||||
}
|
||||
|
||||
fn import_item(i: &mut TokenSlice) -> PResult<Node<ImportItem>> {
|
||||
let name = identifier.context(expected("an identifier to import")).parse_next(i)?;
|
||||
let name = nameable_identifier
|
||||
.context(expected("an identifier to import"))
|
||||
.parse_next(i)?;
|
||||
let start = name.start;
|
||||
let module_id = name.module_id;
|
||||
let alias = opt(preceded(
|
||||
@ -1622,6 +1663,24 @@ fn return_stmt(i: &mut TokenSlice) -> PResult<Node<ReturnStatement>> {
|
||||
|
||||
/// Parse a KCL expression.
|
||||
fn expression(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
let expr = expression_but_not_ascription.parse_next(i)?;
|
||||
let ty = opt((colon, opt(whitespace), argument_type)).parse_next(i)?;
|
||||
|
||||
// TODO this is probably not giving ascription the right precedence, but I have no idea how Winnow is handling that.
|
||||
// Since we're not creating AST nodes for ascription, I don't think it matters right now.
|
||||
if let Some((colon, _, _)) = ty {
|
||||
ParseContext::err(CompilationError::err(
|
||||
// Sadly there is no SourceRange for the type itself
|
||||
colon.into(),
|
||||
"Type ascription is experimental and currently does nothing.",
|
||||
));
|
||||
}
|
||||
|
||||
Ok(expr)
|
||||
}
|
||||
|
||||
// TODO once we remove the old record instantiation syntax, we can accept types ascription anywhere.
|
||||
fn expression_but_not_ascription(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
alt((
|
||||
pipe_expression.map(Box::new).map(Expr::PipeExpression),
|
||||
expression_but_not_pipe,
|
||||
@ -1678,7 +1737,7 @@ fn expr_allowed_in_pipe_expr(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
literal.map(Expr::Literal),
|
||||
fn_call.map(Box::new).map(Expr::CallExpression),
|
||||
fn_call_kw.map(Box::new).map(Expr::CallExpressionKw),
|
||||
identifier.map(Box::new).map(Expr::Identifier),
|
||||
nameable_identifier.map(Box::new).map(Expr::Identifier),
|
||||
array,
|
||||
object.map(Box::new).map(Expr::ObjectExpression),
|
||||
pipe_sub.map(Box::new).map(Expr::PipeSubstitution),
|
||||
@ -1697,7 +1756,7 @@ fn possible_operands(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
member_expression.map(Box::new).map(Expr::MemberExpression),
|
||||
literal.map(Expr::Literal),
|
||||
fn_call.map(Box::new).map(Expr::CallExpression),
|
||||
identifier.map(Box::new).map(Expr::Identifier),
|
||||
nameable_identifier.map(Box::new).map(Expr::Identifier),
|
||||
binary_expr_in_parens.map(Box::new).map(Expr::BinaryExpression),
|
||||
unnecessarily_bracketed,
|
||||
))
|
||||
@ -1873,6 +1932,24 @@ fn identifier(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
|
||||
.parse_next(i)
|
||||
}
|
||||
|
||||
fn nameable_identifier(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
|
||||
let result = identifier.parse_next(i)?;
|
||||
|
||||
if !result.is_nameable() {
|
||||
let desc = if result.name == "_" {
|
||||
"Underscores"
|
||||
} else {
|
||||
"Names with a leading underscore"
|
||||
};
|
||||
ParseContext::err(CompilationError::err(
|
||||
SourceRange::new(result.start, result.end, result.module_id),
|
||||
format!("{desc} cannot be referred to, only declared."),
|
||||
));
|
||||
}
|
||||
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
fn sketch_keyword(i: &mut TokenSlice) -> PResult<Node<Identifier>> {
|
||||
any.try_map(|token: Token| {
|
||||
if token.token_type == TokenType::Type && token.value == "sketch" {
|
||||
@ -2224,9 +2301,8 @@ fn question_mark(i: &mut TokenSlice) -> PResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn at_sign(i: &mut TokenSlice) -> PResult<()> {
|
||||
TokenType::At.parse_from(i)?;
|
||||
Ok(())
|
||||
fn at_sign(i: &mut TokenSlice) -> PResult<Token> {
|
||||
TokenType::At.parse_from(i)
|
||||
}
|
||||
|
||||
fn fun(i: &mut TokenSlice) -> PResult<Token> {
|
||||
@ -2257,7 +2333,7 @@ fn arguments(i: &mut TokenSlice) -> PResult<Vec<Expr>> {
|
||||
|
||||
fn labeled_argument(i: &mut TokenSlice) -> PResult<LabeledArg> {
|
||||
separated_pair(
|
||||
terminated(identifier, opt(whitespace)),
|
||||
terminated(nameable_identifier, opt(whitespace)),
|
||||
terminated(one_of((TokenType::Operator, "=")), opt(whitespace)),
|
||||
expression,
|
||||
)
|
||||
@ -2293,17 +2369,31 @@ fn argument_type(i: &mut TokenSlice) -> PResult<FnArgType> {
|
||||
.map_err(|err| CompilationError::fatal(token.as_source_range(), format!("Invalid type: {}", err)))
|
||||
}),
|
||||
// Primitive types
|
||||
one_of(TokenType::Type).map(|token: Token| {
|
||||
FnArgPrimitive::from_str(&token.value)
|
||||
.map(FnArgType::Primitive)
|
||||
.map_err(|err| CompilationError::fatal(token.as_source_range(), format!("Invalid type: {}", err)))
|
||||
}),
|
||||
(
|
||||
one_of(TokenType::Type),
|
||||
opt(delimited(open_paren, uom_for_type, close_paren)),
|
||||
)
|
||||
.map(|(token, suffix)| {
|
||||
if suffix.is_some() {
|
||||
ParseContext::err(CompilationError::err(
|
||||
(&token).into(),
|
||||
"Unit of Measure types are experimental and currently do nothing.",
|
||||
));
|
||||
}
|
||||
FnArgPrimitive::from_str(&token.value)
|
||||
.map(FnArgType::Primitive)
|
||||
.map_err(|err| CompilationError::fatal(token.as_source_range(), format!("Invalid type: {}", err)))
|
||||
}),
|
||||
))
|
||||
.parse_next(i)?
|
||||
.map_err(|e: CompilationError| ErrMode::Backtrack(ContextError::from(e)))?;
|
||||
Ok(type_)
|
||||
}
|
||||
|
||||
fn uom_for_type(i: &mut TokenSlice) -> PResult<NumericSuffix> {
|
||||
any.try_map(|t: Token| t.value.parse()).parse_next(i)
|
||||
}
|
||||
|
||||
struct ParamDescription {
|
||||
labeled: bool,
|
||||
arg_name: Token,
|
||||
@ -2490,7 +2580,7 @@ fn labelled_fn_call(i: &mut TokenSlice) -> PResult<Expr> {
|
||||
}
|
||||
|
||||
fn fn_call(i: &mut TokenSlice) -> PResult<Node<CallExpression>> {
|
||||
let fn_name = identifier(i)?;
|
||||
let fn_name = nameable_identifier(i)?;
|
||||
opt(whitespace).parse_next(i)?;
|
||||
let _ = terminated(open_paren, opt(whitespace)).parse_next(i)?;
|
||||
let args = arguments(i)?;
|
||||
@ -2531,7 +2621,7 @@ fn fn_call(i: &mut TokenSlice) -> PResult<Node<CallExpression>> {
|
||||
}
|
||||
|
||||
fn fn_call_kw(i: &mut TokenSlice) -> PResult<Node<CallExpressionKw>> {
|
||||
let fn_name = identifier(i)?;
|
||||
let fn_name = nameable_identifier(i)?;
|
||||
opt(whitespace).parse_next(i)?;
|
||||
let _ = open_paren.parse_next(i)?;
|
||||
ignore_whitespace(i);
|
||||
@ -3464,6 +3554,18 @@ mySk1 = startSketchAt([0, 0])"#;
|
||||
(result.0.unwrap(), result.1)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_no_fatal(p: &str) -> (Node<Program>, Vec<CompilationError>) {
|
||||
let result = crate::parsing::top_level_parse(p);
|
||||
let result = result.0.unwrap();
|
||||
assert!(
|
||||
result.1.iter().all(|e| e.severity != Severity::Fatal),
|
||||
"found: {:#?}",
|
||||
result.1
|
||||
);
|
||||
(result.0.unwrap(), result.1)
|
||||
}
|
||||
|
||||
#[track_caller]
|
||||
fn assert_err(p: &str, msg: &str, src_expected: [usize; 2]) {
|
||||
let result = crate::parsing::top_level_parse(p);
|
||||
@ -3557,6 +3659,22 @@ height = [obj["a"] -1, 0]"#;
|
||||
crate::parsing::top_level_parse("foo(42, fn(x) { return x + 1 })").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_annotation_fn() {
|
||||
crate::parsing::top_level_parse(
|
||||
r#"fn foo() {
|
||||
@annotated
|
||||
return 1
|
||||
}"#,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_annotation_settings() {
|
||||
crate::parsing::top_level_parse("@settings(units = mm)").unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_anon_fn_no_fn() {
|
||||
assert_err_contains("foo(42, (x) { return x + 1 })", "Anonymous function requires `fn`");
|
||||
@ -3861,6 +3979,25 @@ e
|
||||
assert_eq!(errs.len(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fn_decl_uom_ty() {
|
||||
let some_program_string = r#"fn foo(x: number(mm)): number(_) { return 1 }"#;
|
||||
let (_, errs) = assert_no_fatal(some_program_string);
|
||||
assert_eq!(errs.len(), 2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_underscore() {
|
||||
let (_, errs) = assert_no_fatal("_foo(_blah, _)");
|
||||
assert_eq!(errs.len(), 3, "found: {:#?}", errs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn error_type_ascription() {
|
||||
let (_, errs) = assert_no_fatal("a + b: number");
|
||||
assert_eq!(errs.len(), 1, "found: {:#?}", errs);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn zero_param_function() {
|
||||
let code = r#"
|
||||
@ -4039,19 +4176,6 @@ let myBox = box([0,0], -3, -16, -10)
|
||||
crate::parsing::top_level_parse(some_program_string).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn must_use_percent_in_pipeline_fn() {
|
||||
let some_program_string = r#"
|
||||
foo()
|
||||
|> bar(2)
|
||||
"#;
|
||||
assert_err(
|
||||
some_program_string,
|
||||
"All expressions in a pipeline must use the % (substitution operator)",
|
||||
[30, 36],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn arg_labels() {
|
||||
let input = r#"length: 3"#;
|
||||
@ -4244,6 +4368,20 @@ var baz = 2
|
||||
"#
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unary_not_on_keyword_bool() {
|
||||
let some_program_string = r#"!true"#;
|
||||
let module_id = ModuleId::default();
|
||||
let tokens = crate::parsing::token::lex(some_program_string, module_id).unwrap(); // Updated import path
|
||||
let actual = match unary_expression.parse(tokens.as_slice()) {
|
||||
// Use tokens.as_slice() for parsing
|
||||
Ok(x) => x,
|
||||
Err(e) => panic!("{e:?}"),
|
||||
};
|
||||
assert_eq!(actual.operator, UnaryOperator::Not);
|
||||
crate::parsing::top_level_parse(some_program_string).unwrap(); // Updated import path
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
@ -4502,6 +4640,11 @@ my14 = 4 ^ 2 - 3 ^ 2 * 2
|
||||
r#"x = 3
|
||||
obj = { x, y: 4}"#
|
||||
);
|
||||
snapshot_test!(bj, "true");
|
||||
snapshot_test!(bk, "truee");
|
||||
snapshot_test!(bl, "x = !true");
|
||||
snapshot_test!(bm, "x = true & false");
|
||||
snapshot_test!(bn, "x = true | false");
|
||||
snapshot_test!(kw_function_unnamed_first, r#"val = foo(x, y = z)"#);
|
||||
snapshot_test!(kw_function_all_named, r#"val = foo(x = a, y = b)"#);
|
||||
snapshot_test!(kw_function_decl_all_labeled, r#"fn foo(x, y) { return 1 }"#);
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
---
|
||||
source: kcl/src/parsing/parser.rs
|
||||
assertion_line: 4521
|
||||
expression: actual
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"body": [
|
||||
{
|
||||
"end": 4,
|
||||
"expression": {
|
||||
"end": 4,
|
||||
"raw": "true",
|
||||
"start": 0,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"start": 0,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 4,
|
||||
"start": 0
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
---
|
||||
source: kcl/src/parsing/parser.rs
|
||||
assertion_line: 4522
|
||||
expression: actual
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"body": [
|
||||
{
|
||||
"end": 5,
|
||||
"expression": {
|
||||
"end": 5,
|
||||
"name": "truee",
|
||||
"start": 0,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"start": 0,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 5,
|
||||
"start": 0
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
---
|
||||
source: kcl/src/parsing/parser.rs
|
||||
assertion_line: 4523
|
||||
expression: actual
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"body": [
|
||||
{
|
||||
"declaration": {
|
||||
"end": 9,
|
||||
"id": {
|
||||
"end": 1,
|
||||
"name": "x",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"argument": {
|
||||
"end": 9,
|
||||
"raw": "true",
|
||||
"start": 5,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"end": 9,
|
||||
"operator": "!",
|
||||
"start": 4,
|
||||
"type": "UnaryExpression",
|
||||
"type": "UnaryExpression"
|
||||
},
|
||||
"start": 0,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 9,
|
||||
"kind": "const",
|
||||
"start": 0,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
}
|
||||
],
|
||||
"end": 9,
|
||||
"start": 0
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
---
|
||||
source: kcl/src/parsing/parser.rs
|
||||
assertion_line: 4524
|
||||
expression: actual
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"body": [
|
||||
{
|
||||
"declaration": {
|
||||
"end": 16,
|
||||
"id": {
|
||||
"end": 1,
|
||||
"name": "x",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 16,
|
||||
"left": {
|
||||
"end": 8,
|
||||
"raw": "true",
|
||||
"start": 4,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"operator": "&",
|
||||
"right": {
|
||||
"end": 16,
|
||||
"raw": "false",
|
||||
"start": 11,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"start": 4,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 0,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 16,
|
||||
"kind": "const",
|
||||
"start": 0,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
}
|
||||
],
|
||||
"end": 16,
|
||||
"start": 0
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
---
|
||||
source: kcl/src/parsing/parser.rs
|
||||
assertion_line: 4525
|
||||
expression: actual
|
||||
snapshot_kind: text
|
||||
---
|
||||
{
|
||||
"body": [
|
||||
{
|
||||
"declaration": {
|
||||
"end": 16,
|
||||
"id": {
|
||||
"end": 1,
|
||||
"name": "x",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 16,
|
||||
"left": {
|
||||
"end": 8,
|
||||
"raw": "true",
|
||||
"start": 4,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"operator": "|",
|
||||
"right": {
|
||||
"end": 16,
|
||||
"raw": "false",
|
||||
"start": 11,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"start": 4,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 0,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 16,
|
||||
"kind": "const",
|
||||
"start": 0,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
}
|
||||
],
|
||||
"end": 16,
|
||||
"start": 0
|
||||
}
|
||||
@ -1,7 +1,7 @@
|
||||
// Clippy does not agree with rustc here for some reason.
|
||||
#![allow(clippy::needless_lifetimes)]
|
||||
|
||||
use std::{fmt, iter::Enumerate, num::NonZeroUsize};
|
||||
use std::{fmt, iter::Enumerate, num::NonZeroUsize, str::FromStr};
|
||||
|
||||
use anyhow::Result;
|
||||
use parse_display::Display;
|
||||
@ -17,6 +17,7 @@ use crate::{
|
||||
errors::KclError,
|
||||
parsing::ast::types::{ItemVisibility, VariableKind},
|
||||
source_range::{ModuleId, SourceRange},
|
||||
CompilationError,
|
||||
};
|
||||
|
||||
mod tokeniser;
|
||||
@ -24,6 +25,53 @@ mod tokeniser;
|
||||
#[cfg(test)]
|
||||
pub(crate) use tokeniser::RESERVED_WORDS;
|
||||
|
||||
// Note the ordering, it's important that `m` comes after `mm` and `cm`.
|
||||
pub const NUM_SUFFIXES: [&str; 9] = ["mm", "cm", "m", "inch", "in", "ft", "yd", "deg", "rad"];
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum NumericSuffix {
|
||||
None,
|
||||
Count,
|
||||
Mm,
|
||||
Cm,
|
||||
M,
|
||||
Inch,
|
||||
Ft,
|
||||
Yd,
|
||||
Deg,
|
||||
Rad,
|
||||
}
|
||||
|
||||
impl NumericSuffix {
|
||||
#[allow(dead_code)]
|
||||
pub fn is_none(self) -> bool {
|
||||
self == Self::None
|
||||
}
|
||||
|
||||
pub fn is_some(self) -> bool {
|
||||
self != Self::None
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for NumericSuffix {
|
||||
type Err = CompilationError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s {
|
||||
"_" => Ok(NumericSuffix::Count),
|
||||
"mm" => Ok(NumericSuffix::Mm),
|
||||
"cm" => Ok(NumericSuffix::Cm),
|
||||
"m" => Ok(NumericSuffix::M),
|
||||
"inch" | "in" => Ok(NumericSuffix::Inch),
|
||||
"ft" => Ok(NumericSuffix::Ft),
|
||||
"yd" => Ok(NumericSuffix::Yd),
|
||||
"deg" => Ok(NumericSuffix::Deg),
|
||||
"rad" => Ok(NumericSuffix::Rad),
|
||||
_ => Err(CompilationError::err(SourceRange::default(), "invalid unit of measure")),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub(crate) struct TokenStream {
|
||||
tokens: Vec<Token>,
|
||||
@ -369,6 +417,36 @@ impl Token {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn numeric_value(&self) -> Option<f64> {
|
||||
if self.token_type != TokenType::Number {
|
||||
return None;
|
||||
}
|
||||
let value = &self.value;
|
||||
let value = value
|
||||
.split_once(|c: char| c == '_' || c.is_ascii_alphabetic())
|
||||
.map(|(s, _)| s)
|
||||
.unwrap_or(value);
|
||||
value.parse().ok()
|
||||
}
|
||||
|
||||
pub fn numeric_suffix(&self) -> NumericSuffix {
|
||||
if self.token_type != TokenType::Number {
|
||||
return NumericSuffix::None;
|
||||
}
|
||||
|
||||
if self.value.ends_with('_') {
|
||||
return NumericSuffix::Count;
|
||||
}
|
||||
|
||||
for suffix in NUM_SUFFIXES {
|
||||
if self.value.ends_with(suffix) {
|
||||
return suffix.parse().unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
NumericSuffix::None
|
||||
}
|
||||
|
||||
/// Is this token the beginning of a variable/function declaration?
|
||||
/// If so, what kind?
|
||||
/// If not, returns None.
|
||||
|
||||
@ -50,7 +50,6 @@ lazy_static! {
|
||||
set.insert("record", TokenType::Keyword);
|
||||
set.insert("struct", TokenType::Keyword);
|
||||
set.insert("object", TokenType::Keyword);
|
||||
set.insert("_", TokenType::Keyword);
|
||||
|
||||
set.insert("string", TokenType::Type);
|
||||
set.insert("number", TokenType::Type);
|
||||
@ -147,9 +146,9 @@ fn line_comment(i: &mut Input<'_>) -> PResult<Token> {
|
||||
fn number(i: &mut Input<'_>) -> PResult<Token> {
|
||||
let number_parser = alt((
|
||||
// Digits before the decimal point.
|
||||
(digit1, opt(('.', digit1))).map(|_| ()),
|
||||
(digit1, opt(('.', digit1)), opt('_'), opt(alt(super::NUM_SUFFIXES))).map(|_| ()),
|
||||
// No digits before the decimal point.
|
||||
('.', digit1).map(|_| ()),
|
||||
('.', digit1, opt('_'), opt(alt(super::NUM_SUFFIXES))).map(|_| ()),
|
||||
));
|
||||
let (value, range) = number_parser.take().with_span().parse_next(i)?;
|
||||
Ok(Token::from_range(
|
||||
@ -188,7 +187,7 @@ fn word(i: &mut Input<'_>) -> PResult<Token> {
|
||||
|
||||
fn operator(i: &mut Input<'_>) -> PResult<Token> {
|
||||
let (value, range) = alt((
|
||||
">=", "<=", "==", "=>", "!=", "|>", "*", "+", "-", "/", "%", "=", "<", ">", r"\", "|", "^",
|
||||
">=", "<=", "==", "=>", "!=", "|>", "*", "+", "-", "/", "%", "=", "<", ">", r"\", "^", "|", "&",
|
||||
))
|
||||
.with_span()
|
||||
.parse_next(i)?;
|
||||
@ -366,6 +365,7 @@ mod tests {
|
||||
|
||||
use super::*;
|
||||
use crate::parsing::token::TokenSlice;
|
||||
|
||||
fn assert_parse_err<'i, P, O, E>(mut p: P, s: &'i str)
|
||||
where
|
||||
O: std::fmt::Debug,
|
||||
@ -379,7 +379,8 @@ mod tests {
|
||||
assert!(p.parse_next(&mut input).is_err(), "parsed {s} but should have failed");
|
||||
}
|
||||
|
||||
fn assert_parse_ok<'i, P, O, E>(mut p: P, s: &'i str)
|
||||
// Returns the token and whether any more input is remaining to tokenize.
|
||||
fn assert_parse_ok<'i, P, O, E>(mut p: P, s: &'i str) -> (O, bool)
|
||||
where
|
||||
E: std::fmt::Debug,
|
||||
O: std::fmt::Debug,
|
||||
@ -392,14 +393,27 @@ mod tests {
|
||||
};
|
||||
let res = p.parse_next(&mut input);
|
||||
assert!(res.is_ok(), "failed to parse {s}, got {}", res.unwrap_err());
|
||||
(res.unwrap(), !input.is_empty())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_number() {
|
||||
for valid in [
|
||||
"1", "1 abc", "1.1", "1.1 abv", "1.1 abv", "1", ".1", "5?", "5 + 6", "5 + a", "5.5", "1abc",
|
||||
for (valid, expected) in [
|
||||
("1", false),
|
||||
("1 abc", true),
|
||||
("1.1", false),
|
||||
("1.1 abv", true),
|
||||
("1.1 abv", true),
|
||||
("1", false),
|
||||
(".1", false),
|
||||
("5?", true),
|
||||
("5 + 6", true),
|
||||
("5 + a", true),
|
||||
("5.5", false),
|
||||
("1abc", true),
|
||||
] {
|
||||
assert_parse_ok(number, valid);
|
||||
let (_, remaining) = assert_parse_ok(number, valid);
|
||||
assert_eq!(expected, remaining, "`{valid}` expected another token to be {expected}");
|
||||
}
|
||||
|
||||
for invalid in ["a", "?", "?5"] {
|
||||
@ -415,6 +429,27 @@ mod tests {
|
||||
assert_eq!(number.parse(input).unwrap().value, "0.0000000000");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_number_suffix() {
|
||||
for (valid, expected_val, expected_next) in [
|
||||
("1_", 1.0, false),
|
||||
("1_mm", 1.0, false),
|
||||
("1_yd", 1.0, false),
|
||||
("1m", 1.0, false),
|
||||
("1inch", 1.0, false),
|
||||
("1toot", 1.0, true),
|
||||
("1.4inch t", 1.4, true),
|
||||
] {
|
||||
let (t, remaining) = assert_parse_ok(number, valid);
|
||||
assert_eq!(expected_next, remaining);
|
||||
assert_eq!(
|
||||
Some(expected_val),
|
||||
t.numeric_value(),
|
||||
"{valid} has incorrect numeric value, expected {expected_val} {t:?}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_word() {
|
||||
for valid in ["a", "a ", "a5", "a5a"] {
|
||||
@ -429,7 +464,7 @@ mod tests {
|
||||
#[test]
|
||||
fn test_operator() {
|
||||
for valid in [
|
||||
"+", "+ ", "-", "<=", "<= ", ">=", ">= ", "> ", "< ", "| ", "|> ", "^ ", "% ", "+* ",
|
||||
"+", "+ ", "-", "<=", "<= ", ">=", ">= ", "> ", "< ", "|> ", "^ ", "% ", "+* ", "| ", "& ",
|
||||
] {
|
||||
assert_parse_ok(operator, valid);
|
||||
}
|
||||
@ -715,4 +750,30 @@ const things = "things"
|
||||
}
|
||||
}
|
||||
}
|
||||
#[test]
|
||||
fn test_boolean_literal() {
|
||||
let module_id = ModuleId::default();
|
||||
let actual = lex("true", module_id).unwrap();
|
||||
let expected = Token {
|
||||
token_type: TokenType::Keyword,
|
||||
value: "true".to_owned(),
|
||||
start: 0,
|
||||
end: 4,
|
||||
module_id,
|
||||
};
|
||||
assert_eq!(actual.tokens[0], expected);
|
||||
}
|
||||
#[test]
|
||||
fn test_word_starting_with_keyword() {
|
||||
let module_id = ModuleId::default();
|
||||
let actual = lex("truee", module_id).unwrap();
|
||||
let expected = Token {
|
||||
token_type: TokenType::Word,
|
||||
value: "truee".to_owned(),
|
||||
start: 0,
|
||||
end: 5,
|
||||
module_id,
|
||||
};
|
||||
assert_eq!(actual.tokens[0], expected);
|
||||
}
|
||||
}
|
||||
|
||||
@ -127,7 +127,7 @@ async fn execute(test_name: &str, render_to_png: bool) {
|
||||
});
|
||||
|
||||
assert_snapshot(test_name, "Operations executed", || {
|
||||
insta::assert_json_snapshot!("ops", e.exec_state.operations);
|
||||
insta::assert_json_snapshot!("ops", e.exec_state.mod_local.operations);
|
||||
});
|
||||
}
|
||||
e => {
|
||||
@ -711,6 +711,27 @@ mod import_glob {
|
||||
super::execute(TEST_NAME, false).await
|
||||
}
|
||||
}
|
||||
mod import_whole {
|
||||
const TEST_NAME: &str = "import_whole";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[test]
|
||||
fn unparse() {
|
||||
super::unparse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, false).await
|
||||
}
|
||||
}
|
||||
mod import_side_effect {
|
||||
const TEST_NAME: &str = "import_side_effect";
|
||||
|
||||
@ -1572,3 +1593,66 @@ mod kw_fn_with_defaults {
|
||||
super::execute(TEST_NAME, false).await
|
||||
}
|
||||
}
|
||||
mod boolean_logical_and {
|
||||
const TEST_NAME: &str = "boolean_logical_and";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[test]
|
||||
fn unparse() {
|
||||
super::unparse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, false).await
|
||||
}
|
||||
}
|
||||
mod boolean_logical_or {
|
||||
const TEST_NAME: &str = "boolean_logical_or";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[test]
|
||||
fn unparse() {
|
||||
super::unparse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, false).await
|
||||
}
|
||||
}
|
||||
mod boolean_logical_multiple {
|
||||
const TEST_NAME: &str = "boolean_logical_multiple";
|
||||
|
||||
/// Test parsing KCL.
|
||||
#[test]
|
||||
fn parse() {
|
||||
super::parse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that parsing and unparsing KCL produces the original KCL input.
|
||||
#[test]
|
||||
fn unparse() {
|
||||
super::unparse(TEST_NAME)
|
||||
}
|
||||
|
||||
/// Test that KCL is executed correctly.
|
||||
#[tokio::test(flavor = "multi_thread")]
|
||||
async fn kcl_test_execute() {
|
||||
super::execute(TEST_NAME, false).await
|
||||
}
|
||||
}
|
||||
|
||||
@ -61,28 +61,34 @@ impl KwArgs {
|
||||
pub struct Args {
|
||||
/// Positional args.
|
||||
pub args: Vec<Arg>,
|
||||
/// Keyword arguments
|
||||
pub kw_args: KwArgs,
|
||||
pub source_range: SourceRange,
|
||||
pub ctx: ExecutorContext,
|
||||
/// If this call happens inside a pipe (|>) expression, this holds the LHS of that |>.
|
||||
/// Otherwise it's None.
|
||||
pipe_value: Option<Arg>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
pub fn new(args: Vec<Arg>, source_range: SourceRange, ctx: ExecutorContext) -> Self {
|
||||
pub fn new(args: Vec<Arg>, source_range: SourceRange, ctx: ExecutorContext, pipe_value: Option<Arg>) -> Self {
|
||||
Self {
|
||||
args,
|
||||
kw_args: Default::default(),
|
||||
source_range,
|
||||
ctx,
|
||||
pipe_value,
|
||||
}
|
||||
}
|
||||
|
||||
/// Collect the given keyword arguments.
|
||||
pub fn new_kw(kw_args: KwArgs, source_range: SourceRange, ctx: ExecutorContext) -> Self {
|
||||
pub fn new_kw(kw_args: KwArgs, source_range: SourceRange, ctx: ExecutorContext, pipe_value: Option<Arg>) -> Self {
|
||||
Self {
|
||||
args: Default::default(),
|
||||
kw_args,
|
||||
source_range,
|
||||
ctx,
|
||||
pipe_value,
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,6 +107,7 @@ impl Args {
|
||||
settings: Default::default(),
|
||||
context_type: crate::execution::ContextType::Mock,
|
||||
},
|
||||
pipe_value: None,
|
||||
})
|
||||
}
|
||||
|
||||
@ -138,6 +145,7 @@ impl Args {
|
||||
.unlabeled
|
||||
.as_ref()
|
||||
.or(self.args.first())
|
||||
.or(self.pipe_value.as_ref())
|
||||
.ok_or(KclError::Semantic(KclErrorDetails {
|
||||
source_ranges: vec![self.source_range],
|
||||
message: format!("This function requires a value for the special unlabeled first parameter, '{label}'"),
|
||||
@ -185,7 +193,7 @@ impl Args {
|
||||
exec_state: &'e mut ExecState,
|
||||
tag: &'a TagIdentifier,
|
||||
) -> Result<&'e crate::execution::TagEngineInfo, KclError> {
|
||||
if let KclValue::TagIdentifier(t) = exec_state.memory.get(&tag.value, self.source_range)? {
|
||||
if let KclValue::TagIdentifier(t) = exec_state.memory().get(&tag.value, self.source_range)? {
|
||||
Ok(t.info.as_ref().ok_or_else(|| {
|
||||
KclError::Type(KclErrorDetails {
|
||||
message: format!("Tag `{}` does not have engine info", tag.value),
|
||||
@ -251,12 +259,12 @@ impl Args {
|
||||
// Find all the solids on the same shared sketch.
|
||||
ids.extend(
|
||||
exec_state
|
||||
.memory
|
||||
.memory()
|
||||
.find_solids_on_sketch(solid.sketch.id)
|
||||
.iter()
|
||||
.flat_map(|eg| eg.get_all_edge_cut_ids()),
|
||||
);
|
||||
ids.extend(exec_state.dynamic_state.edge_cut_ids_on_sketch(sketch_id));
|
||||
ids.extend(exec_state.mod_local.dynamic_state.edge_cut_ids_on_sketch(sketch_id));
|
||||
traversed_sketches.push(sketch_id);
|
||||
}
|
||||
|
||||
|
||||
@ -144,7 +144,8 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
/// stepAngle = (1/10) * tau()
|
||||
///
|
||||
/// // Start the decagon sketch at this point.
|
||||
/// startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])
|
||||
/// startOfDecagonSketch = startSketchOn('XY')
|
||||
/// |> startProfileAt([(cos(0)*radius), (sin(0) * radius)], %)
|
||||
///
|
||||
/// // Use a `reduce` to draw the remaining decagon sides.
|
||||
/// // For each number in the array 1..10, run the given function,
|
||||
@ -164,7 +165,8 @@ pub async fn reduce(exec_state: &mut ExecState, args: Args) -> Result<KclValue,
|
||||
/// The `decagon` above is basically like this pseudo-code:
|
||||
/// fn decagon(radius):
|
||||
/// stepAngle = (1/10) * tau()
|
||||
/// startOfDecagonSketch = startSketchAt([(cos(0)*radius), (sin(0) * radius)])
|
||||
/// plane = startSketchOn('XY')
|
||||
/// startOfDecagonSketch = startProfileAt([(cos(0)*radius), (sin(0) * radius)], plane)
|
||||
///
|
||||
/// // Here's the reduce part.
|
||||
/// partialDecagon = startOfDecagonSketch
|
||||
|
||||
@ -134,7 +134,7 @@ async fn inner_chamfer(
|
||||
EdgeReference::Tag(edge_tag) => args.get_tag_engine_info(exec_state, &edge_tag)?.id,
|
||||
};
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.global.id_generator.next_uuid();
|
||||
args.batch_end_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::Solid3dFilletEdge {
|
||||
|
||||
@ -84,7 +84,7 @@ async fn inner_extrude(
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<SolidSet, KclError> {
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
// Extrude the element(s).
|
||||
let sketches: Vec<Sketch> = sketch_set.into();
|
||||
@ -93,7 +93,7 @@ async fn inner_extrude(
|
||||
// Before we extrude, we need to enable the sketch mode.
|
||||
// We do this here in case extrude is called out of order.
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::EnableSketchMode {
|
||||
animated: false,
|
||||
ortho: false,
|
||||
@ -121,7 +121,7 @@ async fn inner_extrude(
|
||||
|
||||
// Disable the sketch mode.
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::SketchModeDisable(mcmd::SketchModeDisable {}),
|
||||
)
|
||||
.await?;
|
||||
@ -140,7 +140,7 @@ pub(crate) async fn do_post_extrude(
|
||||
// Bring the object to the front of the scene.
|
||||
// See: https://github.com/KittyCAD/modeling-app/issues/806
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::ObjectBringToFront { object_id: sketch.id }),
|
||||
)
|
||||
.await?;
|
||||
@ -163,7 +163,7 @@ pub(crate) async fn do_post_extrude(
|
||||
|
||||
let solid3d_info = args
|
||||
.send_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid3dGetExtrusionFaceInfo {
|
||||
edge_id: any_edge_id,
|
||||
object_id: sketch.id,
|
||||
@ -196,7 +196,7 @@ pub(crate) async fn do_post_extrude(
|
||||
// Instead, the Typescript codebases (which handles WebSocket sends when compiled via Wasm)
|
||||
// uses this to build the artifact graph, which the UI needs.
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid3dGetOppositeEdge {
|
||||
edge_id: curve_id,
|
||||
object_id: sketch.id,
|
||||
@ -206,7 +206,7 @@ pub(crate) async fn do_post_extrude(
|
||||
.await?;
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid3dGetNextAdjacentEdge {
|
||||
edge_id: curve_id,
|
||||
object_id: sketch.id,
|
||||
@ -259,7 +259,7 @@ pub(crate) async fn do_post_extrude(
|
||||
|
||||
let extrude_surface = ExtrudeSurface::ExtrudePlane(crate::execution::ExtrudePlane {
|
||||
// pushing this values with a fake face_id to make extrudes mock-execute safe
|
||||
face_id: exec_state.id_generator.next_uuid(),
|
||||
face_id: exec_state.next_uuid(),
|
||||
tag: path.get_base().tag.clone(),
|
||||
geo_meta: GeoMeta {
|
||||
id: path.get_base().geo_meta.id,
|
||||
@ -305,8 +305,8 @@ fn analyze_faces(exec_state: &mut ExecState, args: &Args, face_infos: Vec<Extrus
|
||||
};
|
||||
if args.ctx.is_mock() {
|
||||
// Create fake IDs for start and end caps, to make extrudes mock-execute safe
|
||||
faces.start_cap_id = Some(exec_state.id_generator.next_uuid());
|
||||
faces.end_cap_id = Some(exec_state.id_generator.next_uuid());
|
||||
faces.start_cap_id = Some(exec_state.next_uuid());
|
||||
faces.end_cap_id = Some(exec_state.next_uuid());
|
||||
}
|
||||
for face_info in face_infos {
|
||||
match face_info.cap {
|
||||
|
||||
@ -143,7 +143,7 @@ async fn inner_fillet(
|
||||
for edge_tag in data.tags {
|
||||
let edge_id = edge_tag.get_engine_id(exec_state, &args)?;
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
args.batch_end_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::Solid3dFilletEdge {
|
||||
@ -225,11 +225,11 @@ pub async fn get_opposite_edge(exec_state: &mut ExecState, args: Args) -> Result
|
||||
}]
|
||||
async fn inner_get_opposite_edge(tag: TagIdentifier, exec_state: &mut ExecState, args: Args) -> Result<Uuid, KclError> {
|
||||
if args.ctx.is_mock() {
|
||||
return Ok(exec_state.id_generator.next_uuid());
|
||||
return Ok(exec_state.next_uuid());
|
||||
}
|
||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
let tagged_path = args.get_tag_engine_info(exec_state, &tag)?;
|
||||
|
||||
let resp = args
|
||||
@ -302,11 +302,11 @@ async fn inner_get_next_adjacent_edge(
|
||||
args: Args,
|
||||
) -> Result<Uuid, KclError> {
|
||||
if args.ctx.is_mock() {
|
||||
return Ok(exec_state.id_generator.next_uuid());
|
||||
return Ok(exec_state.next_uuid());
|
||||
}
|
||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
let tagged_path = args.get_tag_engine_info(exec_state, &tag)?;
|
||||
|
||||
let resp = args
|
||||
@ -387,11 +387,11 @@ async fn inner_get_previous_adjacent_edge(
|
||||
args: Args,
|
||||
) -> Result<Uuid, KclError> {
|
||||
if args.ctx.is_mock() {
|
||||
return Ok(exec_state.id_generator.next_uuid());
|
||||
return Ok(exec_state.next_uuid());
|
||||
}
|
||||
let face_id = args.get_adjacent_face_to_tag(exec_state, &tag, false).await?;
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
let tagged_path = args.get_tag_engine_info(exec_state, &tag)?;
|
||||
|
||||
let resp = args
|
||||
|
||||
@ -61,7 +61,7 @@ async fn inner_helix(
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Box<Solid>, KclError> {
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::EntityMakeHelix {
|
||||
|
||||
@ -300,13 +300,13 @@ async fn inner_import(
|
||||
|
||||
if args.ctx.is_mock() {
|
||||
return Ok(ImportedGeometry {
|
||||
id: exec_state.id_generator.next_uuid(),
|
||||
id: exec_state.next_uuid(),
|
||||
value: import_files.iter().map(|f| f.path.to_string()).collect(),
|
||||
meta: vec![args.source_range.into()],
|
||||
});
|
||||
}
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
let resp = args
|
||||
.send_modeling_cmd(
|
||||
id,
|
||||
|
||||
@ -142,7 +142,7 @@ async fn inner_loft(
|
||||
}));
|
||||
}
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::Loft {
|
||||
|
||||
@ -121,7 +121,7 @@ async fn inner_mirror_2d(
|
||||
let (axis, origin) = axis.axis_and_origin()?;
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::EntityMirror {
|
||||
ids: starting_sketches.iter().map(|sketch| sketch.id).collect(),
|
||||
axis,
|
||||
@ -134,7 +134,7 @@ async fn inner_mirror_2d(
|
||||
let edge_id = edge.get_engine_id(exec_state, &args)?;
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::EntityMirrorAcrossEdge {
|
||||
ids: starting_sketches.iter().map(|sketch| sketch.id).collect(),
|
||||
edge_id,
|
||||
|
||||
@ -179,7 +179,8 @@ pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Res
|
||||
/// p2 = [ l + x, l + y]
|
||||
/// p3 = [ l + x, -l + y]
|
||||
///
|
||||
/// return startSketchAt(p0)
|
||||
/// return startSketchOn('XY')
|
||||
/// |> startProfileAt(p0, %)
|
||||
/// |> lineTo(p1, %)
|
||||
/// |> lineTo(p2, %)
|
||||
/// |> lineTo(p3, %)
|
||||
@ -218,7 +219,8 @@ pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Res
|
||||
/// p2 = [ l + x, l + y]
|
||||
/// p3 = [ l + x, -l + y]
|
||||
///
|
||||
/// return startSketchAt(p0)
|
||||
/// return startSketchOn('XY')
|
||||
/// |> startProfileAt(p0, %)
|
||||
/// |> lineTo(p1, %)
|
||||
/// |> lineTo(p2, %)
|
||||
/// |> lineTo(p3, %)
|
||||
@ -274,7 +276,8 @@ pub async fn pattern_transform_2d(exec_state: &mut ExecState, args: Args) -> Res
|
||||
/// { rotation: { angle: 45 * i } },
|
||||
/// ]
|
||||
/// }
|
||||
/// startSketchAt([0, 0])
|
||||
/// startSketchOn('XY')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> polygon({
|
||||
/// radius: 10,
|
||||
/// numSides: 4,
|
||||
@ -379,7 +382,7 @@ async fn send_pattern_transform<T: GeometryTrait>(
|
||||
exec_state: &mut ExecState,
|
||||
args: &Args,
|
||||
) -> Result<Vec<T>, KclError> {
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
let resp = args
|
||||
.send_modeling_cmd(
|
||||
@ -1032,7 +1035,7 @@ async fn pattern_circular(
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Geometries, KclError> {
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
let num_repetitions = match data.repetitions() {
|
||||
RepetitionsNeeded::More(n) => n,
|
||||
RepetitionsNeeded::None => {
|
||||
|
||||
@ -209,7 +209,7 @@ async fn make_offset_plane_in_engine(plane: &Plane, exec_state: &mut ExecState,
|
||||
|
||||
// Set the color.
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::PlaneSetColor {
|
||||
color,
|
||||
plane_id: plane.id,
|
||||
|
||||
@ -264,7 +264,7 @@ async fn inner_revolve(
|
||||
|
||||
let angle = Angle::from_degrees(data.angle.unwrap_or(360.0));
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
match data.axis {
|
||||
AxisOrEdgeReference::Axis(axis) => {
|
||||
let (axis, origin) = axis.axis_and_origin()?;
|
||||
|
||||
@ -22,7 +22,8 @@ pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
///
|
||||
/// ```no_run
|
||||
/// w = 15
|
||||
/// cube = startSketchAt([0, 0])
|
||||
/// cube = startSketchOn('XY')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line([w, 0], %, $line1)
|
||||
/// |> line([0, w], %, $line2)
|
||||
/// |> line([-w, 0], %, $line3)
|
||||
@ -31,7 +32,8 @@ pub async fn segment_end(exec_state: &mut ExecState, args: Args) -> Result<KclVa
|
||||
/// |> extrude(5, %)
|
||||
///
|
||||
/// fn cylinder(radius, tag) {
|
||||
/// return startSketchAt([0, 0])
|
||||
/// return startSketchOn('XY')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> circle({ radius = radius, center = segEnd(tag) }, %)
|
||||
/// |> extrude(radius, %)
|
||||
/// }
|
||||
@ -141,7 +143,8 @@ pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
///
|
||||
/// ```no_run
|
||||
/// w = 15
|
||||
/// cube = startSketchAt([0, 0])
|
||||
/// cube = startSketchOn('XY')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> line([w, 0], %, $line1)
|
||||
/// |> line([0, w], %, $line2)
|
||||
/// |> line([-w, 0], %, $line3)
|
||||
@ -150,7 +153,8 @@ pub async fn segment_start(exec_state: &mut ExecState, args: Args) -> Result<Kcl
|
||||
/// |> extrude(5, %)
|
||||
///
|
||||
/// fn cylinder(radius, tag) {
|
||||
/// return startSketchAt([0, 0])
|
||||
/// return startSketchOn('XY')
|
||||
/// |> startProfileAt([0, 0], %)
|
||||
/// |> circle({ radius = radius, center = segStart(tag) }, %)
|
||||
/// |> extrude(radius, %)
|
||||
/// }
|
||||
|
||||
@ -100,7 +100,7 @@ async fn inner_circle(
|
||||
let angle_start = Angle::zero();
|
||||
let angle_end = Angle::turn();
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
@ -269,7 +269,7 @@ async fn inner_polygon(
|
||||
// Draw all the lines with unique IDs and modified tags
|
||||
for vertex in vertices.iter().skip(1) {
|
||||
let from = sketch.current_pen_position()?;
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
@ -304,7 +304,7 @@ async fn inner_polygon(
|
||||
|
||||
// Close the polygon by connecting back to the first vertex with a new ID
|
||||
let from = sketch.current_pen_position()?;
|
||||
let close_id = exec_state.id_generator.next_uuid();
|
||||
let close_id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
close_id,
|
||||
@ -337,7 +337,7 @@ async fn inner_polygon(
|
||||
sketch.paths.push(current_path);
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::ClosePath { path_id: sketch.id }),
|
||||
)
|
||||
.await?;
|
||||
|
||||
@ -230,7 +230,7 @@ async fn inner_shell(
|
||||
}
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid3dShellFace {
|
||||
hollow: false,
|
||||
face_ids,
|
||||
@ -316,7 +316,7 @@ async fn inner_hollow(
|
||||
args.flush_batch_for_solid_set(exec_state, solid.clone().into()).await?;
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid3dShellFace {
|
||||
hollow: true,
|
||||
face_ids: Vec::new(), // This is empty because we want to hollow the entire object.
|
||||
|
||||
@ -124,7 +124,7 @@ async fn inner_line_to(
|
||||
args: Args,
|
||||
) -> Result<Sketch, KclError> {
|
||||
let from = sketch.current_pen_position()?;
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
@ -299,7 +299,7 @@ async fn inner_line(
|
||||
let from = sketch.current_pen_position()?;
|
||||
let to = [from.x + delta[0], from.y + delta[1]];
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
@ -488,7 +488,7 @@ async fn inner_angled_line(
|
||||
|
||||
let to: [f64; 2] = [from.x + delta[0], from.y + delta[1]];
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
@ -889,6 +889,7 @@ pub async fn start_sketch_at(exec_state: &mut ExecState, args: Args) -> Result<K
|
||||
/// ```
|
||||
#[stdlib {
|
||||
name = "startSketchAt",
|
||||
deprecated = true,
|
||||
}]
|
||||
async fn inner_start_sketch_at(data: [f64; 2], exec_state: &mut ExecState, args: Args) -> Result<Sketch, KclError> {
|
||||
// Let's assume it's the XY plane for now, this is just for backwards compatibility.
|
||||
@ -1230,7 +1231,7 @@ pub(crate) async fn inner_start_profile_at(
|
||||
// Hide whatever plane we are sketching on.
|
||||
// This is especially helpful for offset planes, which would be visible otherwise.
|
||||
args.batch_end_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::ObjectVisible {
|
||||
object_id: plane.id,
|
||||
hidden: true,
|
||||
@ -1243,7 +1244,7 @@ pub(crate) async fn inner_start_profile_at(
|
||||
|
||||
// Enter sketch mode on the surface.
|
||||
// We call this here so you can reuse the sketch surface for multiple sketches.
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::EnableSketchMode {
|
||||
@ -1261,8 +1262,8 @@ pub(crate) async fn inner_start_profile_at(
|
||||
)
|
||||
.await?;
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let path_id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
let path_id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(path_id, ModelingCmd::from(mcmd::StartPath {}))
|
||||
.await?;
|
||||
@ -1427,7 +1428,7 @@ pub(crate) async fn inner_close(
|
||||
let from = sketch.current_pen_position()?;
|
||||
let to: Point2d = sketch.start.from.into();
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(id, ModelingCmd::from(mcmd::ClosePath { path_id: sketch.id }))
|
||||
.await?;
|
||||
@ -1436,7 +1437,7 @@ pub(crate) async fn inner_close(
|
||||
if let SketchSurface::Plane(_) = sketch.on {
|
||||
// We were on a plane, disable the sketch mode.
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::SketchModeDisable(mcmd::SketchModeDisable {}),
|
||||
)
|
||||
.await?;
|
||||
@ -1573,7 +1574,7 @@ pub(crate) async fn inner_arc(
|
||||
}
|
||||
let ccw = angle_start < angle_end;
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
@ -1652,7 +1653,7 @@ pub(crate) async fn inner_arc_to(
|
||||
args: Args,
|
||||
) -> Result<Sketch, KclError> {
|
||||
let from: Point2d = sketch.current_pen_position()?;
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
// The start point is taken from the path you are extending.
|
||||
args.batch_modeling_cmd(
|
||||
@ -1798,7 +1799,7 @@ async fn inner_tangential_arc(
|
||||
let tangent_info = sketch.get_tangential_info_from_paths(); //this function desperately needs some documentation
|
||||
let tan_previous_point = tangent_info.tan_previous_point(from.into());
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
let (center, to, ccw) = match data {
|
||||
TangentialArcData::RadiusAndOffset { radius, offset } => {
|
||||
@ -1935,7 +1936,7 @@ async fn inner_tangential_arc_to(
|
||||
});
|
||||
|
||||
let delta = [to_x - from.x, to_y - from.y];
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
args.batch_modeling_cmd(id, tan_arc_to(&sketch, &delta)).await?;
|
||||
|
||||
let current_path = Path::TangentialArcTo {
|
||||
@ -2018,7 +2019,7 @@ async fn inner_tangential_arc_to_relative(
|
||||
}));
|
||||
}
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
args.batch_modeling_cmd(id, tan_arc_to(&sketch, &delta)).await?;
|
||||
|
||||
let current_path = Path::TangentialArcTo {
|
||||
@ -2138,7 +2139,7 @@ async fn inner_bezier_curve(
|
||||
let delta = data.to;
|
||||
let to = [from.x + data.to[0], from.y + data.to[1]];
|
||||
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
@ -2230,7 +2231,7 @@ async fn inner_hole(
|
||||
let hole_sketches: Vec<Sketch> = hole_sketch.into();
|
||||
for hole_sketch in hole_sketches {
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::Solid2dAddHole {
|
||||
object_id: sketch.id,
|
||||
hole_id: hole_sketch.id,
|
||||
@ -2241,7 +2242,7 @@ async fn inner_hole(
|
||||
// suggestion (mike)
|
||||
// we also hide the source hole since its essentially "consumed" by this operation
|
||||
args.batch_modeling_cmd(
|
||||
exec_state.id_generator.next_uuid(),
|
||||
exec_state.next_uuid(),
|
||||
ModelingCmd::from(mcmd::ObjectVisible {
|
||||
object_id: hole_sketch.id,
|
||||
hidden: true,
|
||||
|
||||
@ -87,7 +87,7 @@ async fn inner_sweep(
|
||||
exec_state: &mut ExecState,
|
||||
args: Args,
|
||||
) -> Result<Box<Solid>, KclError> {
|
||||
let id = exec_state.id_generator.next_uuid();
|
||||
let id = exec_state.next_uuid();
|
||||
args.batch_modeling_cmd(
|
||||
id,
|
||||
ModelingCmd::from(mcmd::Sweep {
|
||||
|
||||
@ -41,7 +41,7 @@ pub async fn execute_and_snapshot_ast(
|
||||
let ctx = new_context(units, true, project_directory).await?;
|
||||
do_execute_and_snapshot(&ctx, ast)
|
||||
.await
|
||||
.map(|(state, snap)| (state.memory, state.operations, snap))
|
||||
.map(|(state, snap)| (state.mod_local.memory, state.mod_local.operations, snap))
|
||||
}
|
||||
|
||||
pub async fn execute_and_snapshot_no_auth(
|
||||
|
||||
@ -3,10 +3,10 @@ use std::fmt::Write;
|
||||
use crate::parsing::{
|
||||
ast::types::{
|
||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression,
|
||||
CallExpressionKw, DefaultParamVal, Expr, FnArgType, FormatOptions, FunctionExpression, IfExpression,
|
||||
ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier, LiteralValue,
|
||||
MemberExpression, MemberObject, Node, NonCodeValue, ObjectExpression, Parameter, PipeExpression, Program,
|
||||
TagDeclarator, UnaryExpression, VariableDeclaration, VariableKind,
|
||||
CallExpressionKw, CommentStyle, DefaultParamVal, Expr, FnArgType, FormatOptions, FunctionExpression,
|
||||
IfExpression, ImportSelector, ImportStatement, ItemVisibility, LabeledArg, Literal, LiteralIdentifier,
|
||||
LiteralValue, MemberExpression, MemberObject, Node, NonCodeNode, NonCodeValue, ObjectExpression, Parameter,
|
||||
PipeExpression, Program, TagDeclarator, UnaryExpression, VariableDeclaration, VariableKind,
|
||||
},
|
||||
PIPE_OPERATOR,
|
||||
};
|
||||
@ -55,7 +55,7 @@ impl Program {
|
||||
self.non_code_meta
|
||||
.start_nodes
|
||||
.iter()
|
||||
.map(|start| start.format(&indentation))
|
||||
.map(|start| start.recast(options, indentation_level))
|
||||
.collect()
|
||||
}
|
||||
} else {
|
||||
@ -76,7 +76,7 @@ impl Program {
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, custom_white_space_or_comment)| {
|
||||
let formatted = custom_white_space_or_comment.format(&indentation);
|
||||
let formatted = custom_white_space_or_comment.recast(options, indentation_level);
|
||||
if i == 0 && !formatted.trim().is_empty() {
|
||||
if let NonCodeValue::BlockComment { .. } = custom_white_space_or_comment.value {
|
||||
format!("\n{}", formatted)
|
||||
@ -115,7 +115,75 @@ impl NonCodeValue {
|
||||
fn should_cause_array_newline(&self) -> bool {
|
||||
match self {
|
||||
Self::InlineComment { .. } => false,
|
||||
Self::BlockComment { .. } | Self::NewLineBlockComment { .. } | Self::NewLine => true,
|
||||
Self::BlockComment { .. } | Self::NewLineBlockComment { .. } | Self::NewLine | Self::Annotation { .. } => {
|
||||
true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Node<NonCodeNode> {
|
||||
fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||
let indentation = options.get_indentation(indentation_level);
|
||||
match &self.value {
|
||||
NonCodeValue::InlineComment {
|
||||
value,
|
||||
style: CommentStyle::Line,
|
||||
} => format!(" // {}\n", value),
|
||||
NonCodeValue::InlineComment {
|
||||
value,
|
||||
style: CommentStyle::Block,
|
||||
} => format!(" /* {} */", value),
|
||||
NonCodeValue::BlockComment { value, style } => match style {
|
||||
CommentStyle::Block => format!("{}/* {} */", indentation, value),
|
||||
CommentStyle::Line => {
|
||||
if value.trim().is_empty() {
|
||||
format!("{}//\n", indentation)
|
||||
} else {
|
||||
format!("{}// {}\n", indentation, value.trim())
|
||||
}
|
||||
}
|
||||
},
|
||||
NonCodeValue::NewLineBlockComment { value, style } => {
|
||||
let add_start_new_line = if self.start == 0 { "" } else { "\n\n" };
|
||||
match style {
|
||||
CommentStyle::Block => format!("{}{}/* {} */\n", add_start_new_line, indentation, value),
|
||||
CommentStyle::Line => {
|
||||
if value.trim().is_empty() {
|
||||
format!("{}{}//\n", add_start_new_line, indentation)
|
||||
} else {
|
||||
format!("{}{}// {}\n", add_start_new_line, indentation, value.trim())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
NonCodeValue::NewLine => "\n\n".to_string(),
|
||||
NonCodeValue::Annotation { name, properties } => {
|
||||
let mut result = "@".to_owned();
|
||||
result.push_str(&name.name);
|
||||
if let Some(properties) = properties {
|
||||
result.push('(');
|
||||
result.push_str(
|
||||
&properties
|
||||
.iter()
|
||||
.map(|prop| {
|
||||
format!(
|
||||
"{} = {}",
|
||||
prop.key.name,
|
||||
prop.value
|
||||
.recast(options, indentation_level + 1, ExprContext::Other)
|
||||
.trim()
|
||||
)
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", "),
|
||||
);
|
||||
result.push(')');
|
||||
result.push('\n');
|
||||
}
|
||||
|
||||
result
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -146,11 +214,11 @@ impl ImportStatement {
|
||||
string.push_str(" from ");
|
||||
}
|
||||
ImportSelector::Glob(_) => string.push_str("* from "),
|
||||
ImportSelector::None(_) => {}
|
||||
ImportSelector::None { .. } => {}
|
||||
}
|
||||
string.push_str(&format!("\"{}\"", self.path));
|
||||
|
||||
if let ImportSelector::None(Some(alias)) = &self.selector {
|
||||
if let ImportSelector::None { alias: Some(alias) } = &self.selector {
|
||||
string.push_str(" as ");
|
||||
string.push_str(&alias.name);
|
||||
}
|
||||
@ -343,7 +411,7 @@ impl ArrayExpression {
|
||||
.iter()
|
||||
.map(|nc| {
|
||||
found_line_comment |= nc.value.should_cause_array_newline();
|
||||
nc.format("")
|
||||
nc.recast(options, 0)
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
} else {
|
||||
@ -481,7 +549,7 @@ impl ObjectExpression {
|
||||
let format_items: Vec<_> = (0..num_items)
|
||||
.flat_map(|i| {
|
||||
if let Some(noncode) = self.non_code_meta.non_code_nodes.get(&i) {
|
||||
noncode.iter().map(|nc| nc.format("")).collect::<Vec<_>>()
|
||||
noncode.iter().map(|nc| nc.recast(options, 0)).collect::<Vec<_>>()
|
||||
} else {
|
||||
let prop = props.next().unwrap();
|
||||
// Use a comma unless it's the last item
|
||||
@ -617,10 +685,13 @@ impl Node<PipeExpression> {
|
||||
if let Some(non_code_meta_value) = non_code_meta.non_code_nodes.get(&index) {
|
||||
for val in non_code_meta_value {
|
||||
let formatted = if val.end == self.end {
|
||||
let indentation = options.get_indentation(indentation_level);
|
||||
val.format(&indentation).trim_end_matches('\n').to_string()
|
||||
val.recast(options, indentation_level)
|
||||
.trim_end_matches('\n')
|
||||
.to_string()
|
||||
} else {
|
||||
val.format(&indentation).trim_end_matches('\n').to_string()
|
||||
val.recast(options, indentation_level + 1)
|
||||
.trim_end_matches('\n')
|
||||
.to_string()
|
||||
};
|
||||
if let NonCodeValue::BlockComment { .. } = val.value {
|
||||
s += "\n";
|
||||
@ -1252,7 +1323,8 @@ part001 = startSketchOn('XY')
|
||||
|
||||
#[test]
|
||||
fn test_recast_large_file() {
|
||||
let some_program_string = r#"// define nts
|
||||
let some_program_string = r#"@settings(units=mm)
|
||||
// define nts
|
||||
radius = 6.0
|
||||
width = 144.0
|
||||
length = 83.0
|
||||
@ -1376,7 +1448,8 @@ tabs_l = startSketchOn({
|
||||
// Its VERY important this comes back with zero new lines.
|
||||
assert_eq!(
|
||||
recasted,
|
||||
r#"// define nts
|
||||
r#"@settings(units = mm)
|
||||
// define nts
|
||||
radius = 6.0
|
||||
width = 144.0
|
||||
length = 83.0
|
||||
|
||||
703
src/wasm-lib/kcl/tests/boolean_logical_and/ast.snap
Normal file
@ -0,0 +1,703 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Result of parsing boolean_logical_and.kcl
|
||||
---
|
||||
{
|
||||
"Ok": {
|
||||
"body": [
|
||||
{
|
||||
"declaration": {
|
||||
"end": 17,
|
||||
"id": {
|
||||
"end": 2,
|
||||
"name": "aa",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 17,
|
||||
"left": {
|
||||
"end": 9,
|
||||
"raw": "true",
|
||||
"start": 5,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"operator": "&",
|
||||
"right": {
|
||||
"end": 17,
|
||||
"raw": "false",
|
||||
"start": 12,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"start": 5,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 0,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 17,
|
||||
"kind": "const",
|
||||
"start": 0,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 48,
|
||||
"id": {
|
||||
"end": 19,
|
||||
"name": "a",
|
||||
"start": 18,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"cond": {
|
||||
"end": 27,
|
||||
"name": "aa",
|
||||
"start": 25,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"digest": null,
|
||||
"else_ifs": [],
|
||||
"end": 48,
|
||||
"final_else": {
|
||||
"body": [
|
||||
{
|
||||
"end": 46,
|
||||
"expression": {
|
||||
"end": 46,
|
||||
"raw": "2",
|
||||
"start": 45,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 45,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 47,
|
||||
"start": 45
|
||||
},
|
||||
"start": 22,
|
||||
"then_val": {
|
||||
"body": [
|
||||
{
|
||||
"end": 33,
|
||||
"expression": {
|
||||
"end": 33,
|
||||
"raw": "1",
|
||||
"start": 32,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 32,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 34,
|
||||
"start": 32
|
||||
},
|
||||
"type": "IfExpression",
|
||||
"type": "IfExpression"
|
||||
},
|
||||
"start": 18,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 48,
|
||||
"kind": "const",
|
||||
"start": 18,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"end": 128,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 62,
|
||||
"left": {
|
||||
"end": 57,
|
||||
"name": "a",
|
||||
"start": 56,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"operator": "==",
|
||||
"right": {
|
||||
"end": 62,
|
||||
"raw": "2",
|
||||
"start": 61,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 56,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 127,
|
||||
"raw": "\"right branch of and is false makes the whole expression false\"",
|
||||
"start": 64,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "right branch of and is false makes the whole expression false"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 55,
|
||||
"name": "assert",
|
||||
"start": 49,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 128,
|
||||
"start": 49,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 49,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 147,
|
||||
"id": {
|
||||
"end": 132,
|
||||
"name": "bb",
|
||||
"start": 130,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 147,
|
||||
"left": {
|
||||
"end": 140,
|
||||
"raw": "false",
|
||||
"start": 135,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"operator": "&",
|
||||
"right": {
|
||||
"end": 147,
|
||||
"raw": "true",
|
||||
"start": 143,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"start": 135,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 130,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 147,
|
||||
"kind": "const",
|
||||
"start": 130,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 178,
|
||||
"id": {
|
||||
"end": 149,
|
||||
"name": "b",
|
||||
"start": 148,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"cond": {
|
||||
"end": 157,
|
||||
"name": "bb",
|
||||
"start": 155,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"digest": null,
|
||||
"else_ifs": [],
|
||||
"end": 178,
|
||||
"final_else": {
|
||||
"body": [
|
||||
{
|
||||
"end": 176,
|
||||
"expression": {
|
||||
"end": 176,
|
||||
"raw": "2",
|
||||
"start": 175,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 175,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 177,
|
||||
"start": 175
|
||||
},
|
||||
"start": 152,
|
||||
"then_val": {
|
||||
"body": [
|
||||
{
|
||||
"end": 163,
|
||||
"expression": {
|
||||
"end": 163,
|
||||
"raw": "1",
|
||||
"start": 162,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 162,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 164,
|
||||
"start": 162
|
||||
},
|
||||
"type": "IfExpression",
|
||||
"type": "IfExpression"
|
||||
},
|
||||
"start": 148,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 178,
|
||||
"kind": "const",
|
||||
"start": 148,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"end": 257,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 192,
|
||||
"left": {
|
||||
"end": 187,
|
||||
"name": "b",
|
||||
"start": 186,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"operator": "==",
|
||||
"right": {
|
||||
"end": 192,
|
||||
"raw": "2",
|
||||
"start": 191,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 186,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 256,
|
||||
"raw": "\"left branch of and is false makes the whole expression false\"",
|
||||
"start": 194,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "left branch of and is false makes the whole expression false"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 185,
|
||||
"name": "assert",
|
||||
"start": 179,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 257,
|
||||
"start": 179,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 179,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 275,
|
||||
"id": {
|
||||
"end": 261,
|
||||
"name": "cc",
|
||||
"start": 259,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 275,
|
||||
"left": {
|
||||
"end": 268,
|
||||
"raw": "true",
|
||||
"start": 264,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"operator": "&",
|
||||
"right": {
|
||||
"end": 275,
|
||||
"raw": "true",
|
||||
"start": 271,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"start": 264,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 259,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 275,
|
||||
"kind": "const",
|
||||
"start": 259,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 306,
|
||||
"id": {
|
||||
"end": 277,
|
||||
"name": "c",
|
||||
"start": 276,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"cond": {
|
||||
"end": 285,
|
||||
"name": "cc",
|
||||
"start": 283,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"digest": null,
|
||||
"else_ifs": [],
|
||||
"end": 306,
|
||||
"final_else": {
|
||||
"body": [
|
||||
{
|
||||
"end": 304,
|
||||
"expression": {
|
||||
"end": 304,
|
||||
"raw": "2",
|
||||
"start": 303,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 303,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 305,
|
||||
"start": 303
|
||||
},
|
||||
"start": 280,
|
||||
"then_val": {
|
||||
"body": [
|
||||
{
|
||||
"end": 291,
|
||||
"expression": {
|
||||
"end": 291,
|
||||
"raw": "1",
|
||||
"start": 290,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 290,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 292,
|
||||
"start": 290
|
||||
},
|
||||
"type": "IfExpression",
|
||||
"type": "IfExpression"
|
||||
},
|
||||
"start": 276,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 306,
|
||||
"kind": "const",
|
||||
"start": 276,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"end": 386,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 320,
|
||||
"left": {
|
||||
"end": 315,
|
||||
"name": "c",
|
||||
"start": 314,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"operator": "==",
|
||||
"right": {
|
||||
"end": 320,
|
||||
"raw": "1",
|
||||
"start": 319,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 314,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 385,
|
||||
"raw": "\"both branches of and are true makes the whole expression true\"",
|
||||
"start": 322,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "both branches of and are true makes the whole expression true"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 313,
|
||||
"name": "assert",
|
||||
"start": 307,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 386,
|
||||
"start": 307,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 307,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 406,
|
||||
"id": {
|
||||
"end": 390,
|
||||
"name": "dd",
|
||||
"start": 388,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 406,
|
||||
"left": {
|
||||
"end": 398,
|
||||
"raw": "false",
|
||||
"start": 393,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"operator": "&",
|
||||
"right": {
|
||||
"end": 406,
|
||||
"raw": "false",
|
||||
"start": 401,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"start": 393,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 388,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 406,
|
||||
"kind": "const",
|
||||
"start": 388,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 437,
|
||||
"id": {
|
||||
"end": 408,
|
||||
"name": "d",
|
||||
"start": 407,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"cond": {
|
||||
"end": 416,
|
||||
"name": "dd",
|
||||
"start": 414,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"digest": null,
|
||||
"else_ifs": [],
|
||||
"end": 437,
|
||||
"final_else": {
|
||||
"body": [
|
||||
{
|
||||
"end": 435,
|
||||
"expression": {
|
||||
"end": 435,
|
||||
"raw": "2",
|
||||
"start": 434,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 434,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 436,
|
||||
"start": 434
|
||||
},
|
||||
"start": 411,
|
||||
"then_val": {
|
||||
"body": [
|
||||
{
|
||||
"end": 422,
|
||||
"expression": {
|
||||
"end": 422,
|
||||
"raw": "1",
|
||||
"start": 421,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 421,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 423,
|
||||
"start": 421
|
||||
},
|
||||
"type": "IfExpression",
|
||||
"type": "IfExpression"
|
||||
},
|
||||
"start": 407,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 437,
|
||||
"kind": "const",
|
||||
"start": 407,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"end": 519,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 451,
|
||||
"left": {
|
||||
"end": 446,
|
||||
"name": "d",
|
||||
"start": 445,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"operator": "==",
|
||||
"right": {
|
||||
"end": 451,
|
||||
"raw": "2",
|
||||
"start": 450,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 445,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 518,
|
||||
"raw": "\"both branches of and are false makes the whole expression false\"",
|
||||
"start": 453,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "both branches of and are false makes the whole expression false"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 444,
|
||||
"name": "assert",
|
||||
"start": 438,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 519,
|
||||
"start": 438,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 438,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 520,
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"2": [
|
||||
{
|
||||
"end": 130,
|
||||
"start": 128,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
],
|
||||
"5": [
|
||||
{
|
||||
"end": 259,
|
||||
"start": 257,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
],
|
||||
"8": [
|
||||
{
|
||||
"end": 388,
|
||||
"start": 386,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": []
|
||||
},
|
||||
"start": 0
|
||||
}
|
||||
}
|
||||
31
src/wasm-lib/kcl/tests/boolean_logical_and/input.kcl
Normal file
@ -0,0 +1,31 @@
|
||||
aa = true & false
|
||||
a = if aa {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
assert(a == 2, "right branch of and is false makes the whole expression false")
|
||||
|
||||
bb = false & true
|
||||
b = if bb {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
assert(b == 2, "left branch of and is false makes the whole expression false")
|
||||
|
||||
cc = true & true
|
||||
c = if cc {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
assert(c == 1, "both branches of and are true makes the whole expression true")
|
||||
|
||||
dd = false & false
|
||||
d = if dd {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
assert(d == 2, "both branches of and are false makes the whole expression false")
|
||||
5
src/wasm-lib/kcl/tests/boolean_logical_and/ops.snap
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Operations executed boolean_logical_and.kcl
|
||||
---
|
||||
[]
|
||||
167
src/wasm-lib/kcl/tests/boolean_logical_and/program_memory.snap
Normal file
@ -0,0 +1,167 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing boolean_logical_and.kcl
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"a": {
|
||||
"type": "Number",
|
||||
"value": 2.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
45,
|
||||
46,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"aa": {
|
||||
"type": "Bool",
|
||||
"value": false,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
9,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
12,
|
||||
17,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"b": {
|
||||
"type": "Number",
|
||||
"value": 2.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
175,
|
||||
176,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"bb": {
|
||||
"type": "Bool",
|
||||
"value": false,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
135,
|
||||
140,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
143,
|
||||
147,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"type": "Number",
|
||||
"value": 1.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
290,
|
||||
291,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"cc": {
|
||||
"type": "Bool",
|
||||
"value": true,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
264,
|
||||
268,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
271,
|
||||
275,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"d": {
|
||||
"type": "Number",
|
||||
"value": 2.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
434,
|
||||
435,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dd": {
|
||||
"type": "Bool",
|
||||
"value": false,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
393,
|
||||
398,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
401,
|
||||
406,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
}
|
||||
422
src/wasm-lib/kcl/tests/boolean_logical_multiple/ast.snap
Normal file
@ -0,0 +1,422 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Result of parsing boolean_logical_multiple.kcl
|
||||
---
|
||||
{
|
||||
"Ok": {
|
||||
"body": [
|
||||
{
|
||||
"declaration": {
|
||||
"end": 25,
|
||||
"id": {
|
||||
"end": 2,
|
||||
"name": "ii",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 25,
|
||||
"left": {
|
||||
"end": 9,
|
||||
"raw": "true",
|
||||
"start": 5,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"operator": "|",
|
||||
"right": {
|
||||
"end": 25,
|
||||
"left": {
|
||||
"end": 17,
|
||||
"raw": "false",
|
||||
"start": 12,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"operator": "&",
|
||||
"right": {
|
||||
"end": 25,
|
||||
"raw": "false",
|
||||
"start": 20,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"start": 12,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 5,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 0,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 25,
|
||||
"kind": "const",
|
||||
"start": 0,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 56,
|
||||
"id": {
|
||||
"end": 27,
|
||||
"name": "i",
|
||||
"start": 26,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"cond": {
|
||||
"end": 35,
|
||||
"name": "ii",
|
||||
"start": 33,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"digest": null,
|
||||
"else_ifs": [],
|
||||
"end": 56,
|
||||
"final_else": {
|
||||
"body": [
|
||||
{
|
||||
"end": 54,
|
||||
"expression": {
|
||||
"end": 54,
|
||||
"raw": "2",
|
||||
"start": 53,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 53,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 55,
|
||||
"start": 53
|
||||
},
|
||||
"start": 30,
|
||||
"then_val": {
|
||||
"body": [
|
||||
{
|
||||
"end": 41,
|
||||
"expression": {
|
||||
"end": 41,
|
||||
"raw": "1",
|
||||
"start": 40,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 40,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 42,
|
||||
"start": 40
|
||||
},
|
||||
"type": "IfExpression",
|
||||
"type": "IfExpression"
|
||||
},
|
||||
"start": 26,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 56,
|
||||
"kind": "const",
|
||||
"start": 26,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"end": 108,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 70,
|
||||
"left": {
|
||||
"end": 65,
|
||||
"name": "i",
|
||||
"start": 64,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"operator": "==",
|
||||
"right": {
|
||||
"end": 70,
|
||||
"raw": "1",
|
||||
"start": 69,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 64,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 107,
|
||||
"raw": "\"and has higher precedence than or\"",
|
||||
"start": 72,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "and has higher precedence than or"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 63,
|
||||
"name": "assert",
|
||||
"start": 57,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 108,
|
||||
"start": 57,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 57,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 151,
|
||||
"id": {
|
||||
"end": 112,
|
||||
"name": "jj",
|
||||
"start": 110,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 151,
|
||||
"left": {
|
||||
"end": 136,
|
||||
"left": {
|
||||
"end": 120,
|
||||
"raw": "false",
|
||||
"start": 115,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"operator": "|",
|
||||
"right": {
|
||||
"end": 136,
|
||||
"left": {
|
||||
"end": 127,
|
||||
"raw": "true",
|
||||
"start": 123,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"operator": "&",
|
||||
"right": {
|
||||
"argument": {
|
||||
"end": 136,
|
||||
"raw": "false",
|
||||
"start": 131,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"end": 136,
|
||||
"operator": "!",
|
||||
"start": 130,
|
||||
"type": "UnaryExpression",
|
||||
"type": "UnaryExpression"
|
||||
},
|
||||
"start": 123,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 115,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"operator": "|",
|
||||
"right": {
|
||||
"end": 151,
|
||||
"left": {
|
||||
"end": 144,
|
||||
"raw": "false",
|
||||
"start": 139,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"operator": "&",
|
||||
"right": {
|
||||
"end": 151,
|
||||
"raw": "true",
|
||||
"start": 147,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"start": 139,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 115,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 110,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 151,
|
||||
"kind": "const",
|
||||
"start": 110,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 182,
|
||||
"id": {
|
||||
"end": 153,
|
||||
"name": "j",
|
||||
"start": 152,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"cond": {
|
||||
"end": 161,
|
||||
"name": "jj",
|
||||
"start": 159,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"digest": null,
|
||||
"else_ifs": [],
|
||||
"end": 182,
|
||||
"final_else": {
|
||||
"body": [
|
||||
{
|
||||
"end": 180,
|
||||
"expression": {
|
||||
"end": 180,
|
||||
"raw": "2",
|
||||
"start": 179,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 179,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 181,
|
||||
"start": 179
|
||||
},
|
||||
"start": 156,
|
||||
"then_val": {
|
||||
"body": [
|
||||
{
|
||||
"end": 167,
|
||||
"expression": {
|
||||
"end": 167,
|
||||
"raw": "1",
|
||||
"start": 166,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 166,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 168,
|
||||
"start": 166
|
||||
},
|
||||
"type": "IfExpression",
|
||||
"type": "IfExpression"
|
||||
},
|
||||
"start": 152,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 182,
|
||||
"kind": "const",
|
||||
"start": 152,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"end": 227,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 196,
|
||||
"left": {
|
||||
"end": 191,
|
||||
"name": "j",
|
||||
"start": 190,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"operator": "==",
|
||||
"right": {
|
||||
"end": 196,
|
||||
"raw": "1",
|
||||
"start": 195,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 190,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 226,
|
||||
"raw": "\"multiple logical operators\"",
|
||||
"start": 198,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "multiple logical operators"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 189,
|
||||
"name": "assert",
|
||||
"start": 183,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 227,
|
||||
"start": 183,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 183,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 228,
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"2": [
|
||||
{
|
||||
"end": 110,
|
||||
"start": 108,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": []
|
||||
},
|
||||
"start": 0
|
||||
}
|
||||
}
|
||||
15
src/wasm-lib/kcl/tests/boolean_logical_multiple/input.kcl
Normal file
@ -0,0 +1,15 @@
|
||||
ii = true | false & false
|
||||
i = if ii {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
assert(i == 1, "and has higher precedence than or")
|
||||
|
||||
jj = false | true & !false | false & true
|
||||
j = if jj {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
assert(j == 1, "multiple logical operators")
|
||||
5
src/wasm-lib/kcl/tests/boolean_logical_multiple/ops.snap
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Operations executed boolean_logical_multiple.kcl
|
||||
---
|
||||
[]
|
||||
@ -0,0 +1,129 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing boolean_logical_multiple.kcl
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"i": {
|
||||
"type": "Number",
|
||||
"value": 1.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
40,
|
||||
41,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"ii": {
|
||||
"type": "Bool",
|
||||
"value": true,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
9,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
12,
|
||||
17,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
20,
|
||||
25,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"j": {
|
||||
"type": "Number",
|
||||
"value": 1.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
166,
|
||||
167,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"jj": {
|
||||
"type": "Bool",
|
||||
"value": true,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
115,
|
||||
120,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
123,
|
||||
127,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
130,
|
||||
136,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
139,
|
||||
144,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
147,
|
||||
151,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
}
|
||||
703
src/wasm-lib/kcl/tests/boolean_logical_or/ast.snap
Normal file
@ -0,0 +1,703 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Result of parsing boolean_logical_or.kcl
|
||||
---
|
||||
{
|
||||
"Ok": {
|
||||
"body": [
|
||||
{
|
||||
"declaration": {
|
||||
"end": 17,
|
||||
"id": {
|
||||
"end": 2,
|
||||
"name": "aa",
|
||||
"start": 0,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 17,
|
||||
"left": {
|
||||
"end": 9,
|
||||
"raw": "true",
|
||||
"start": 5,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"operator": "|",
|
||||
"right": {
|
||||
"end": 17,
|
||||
"raw": "false",
|
||||
"start": 12,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"start": 5,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 0,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 17,
|
||||
"kind": "const",
|
||||
"start": 0,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 48,
|
||||
"id": {
|
||||
"end": 19,
|
||||
"name": "a",
|
||||
"start": 18,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"cond": {
|
||||
"end": 27,
|
||||
"name": "aa",
|
||||
"start": 25,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"digest": null,
|
||||
"else_ifs": [],
|
||||
"end": 48,
|
||||
"final_else": {
|
||||
"body": [
|
||||
{
|
||||
"end": 46,
|
||||
"expression": {
|
||||
"end": 46,
|
||||
"raw": "2",
|
||||
"start": 45,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 45,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 47,
|
||||
"start": 45
|
||||
},
|
||||
"start": 22,
|
||||
"then_val": {
|
||||
"body": [
|
||||
{
|
||||
"end": 33,
|
||||
"expression": {
|
||||
"end": 33,
|
||||
"raw": "1",
|
||||
"start": 32,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 32,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 34,
|
||||
"start": 32
|
||||
},
|
||||
"type": "IfExpression",
|
||||
"type": "IfExpression"
|
||||
},
|
||||
"start": 18,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 48,
|
||||
"kind": "const",
|
||||
"start": 18,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"end": 124,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 62,
|
||||
"left": {
|
||||
"end": 57,
|
||||
"name": "a",
|
||||
"start": 56,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"operator": "==",
|
||||
"right": {
|
||||
"end": 62,
|
||||
"raw": "1",
|
||||
"start": 61,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 56,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 123,
|
||||
"raw": "\"left branch of or is true makes the whole expression true\"",
|
||||
"start": 64,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "left branch of or is true makes the whole expression true"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 55,
|
||||
"name": "assert",
|
||||
"start": 49,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 124,
|
||||
"start": 49,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 49,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 143,
|
||||
"id": {
|
||||
"end": 128,
|
||||
"name": "bb",
|
||||
"start": 126,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 143,
|
||||
"left": {
|
||||
"end": 136,
|
||||
"raw": "false",
|
||||
"start": 131,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"operator": "|",
|
||||
"right": {
|
||||
"end": 143,
|
||||
"raw": "true",
|
||||
"start": 139,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"start": 131,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 126,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 143,
|
||||
"kind": "const",
|
||||
"start": 126,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 174,
|
||||
"id": {
|
||||
"end": 145,
|
||||
"name": "b",
|
||||
"start": 144,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"cond": {
|
||||
"end": 153,
|
||||
"name": "bb",
|
||||
"start": 151,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"digest": null,
|
||||
"else_ifs": [],
|
||||
"end": 174,
|
||||
"final_else": {
|
||||
"body": [
|
||||
{
|
||||
"end": 172,
|
||||
"expression": {
|
||||
"end": 172,
|
||||
"raw": "2",
|
||||
"start": 171,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 171,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 173,
|
||||
"start": 171
|
||||
},
|
||||
"start": 148,
|
||||
"then_val": {
|
||||
"body": [
|
||||
{
|
||||
"end": 159,
|
||||
"expression": {
|
||||
"end": 159,
|
||||
"raw": "1",
|
||||
"start": 158,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 158,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 160,
|
||||
"start": 158
|
||||
},
|
||||
"type": "IfExpression",
|
||||
"type": "IfExpression"
|
||||
},
|
||||
"start": 144,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 174,
|
||||
"kind": "const",
|
||||
"start": 144,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"end": 251,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 188,
|
||||
"left": {
|
||||
"end": 183,
|
||||
"name": "b",
|
||||
"start": 182,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"operator": "==",
|
||||
"right": {
|
||||
"end": 188,
|
||||
"raw": "1",
|
||||
"start": 187,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 182,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 250,
|
||||
"raw": "\"right branch of or is true makes the whole expression true\"",
|
||||
"start": 190,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "right branch of or is true makes the whole expression true"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 181,
|
||||
"name": "assert",
|
||||
"start": 175,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 251,
|
||||
"start": 175,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 175,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 269,
|
||||
"id": {
|
||||
"end": 255,
|
||||
"name": "cc",
|
||||
"start": 253,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 269,
|
||||
"left": {
|
||||
"end": 262,
|
||||
"raw": "true",
|
||||
"start": 258,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"operator": "|",
|
||||
"right": {
|
||||
"end": 269,
|
||||
"raw": "true",
|
||||
"start": 265,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": true
|
||||
},
|
||||
"start": 258,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 253,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 269,
|
||||
"kind": "const",
|
||||
"start": 253,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 300,
|
||||
"id": {
|
||||
"end": 271,
|
||||
"name": "c",
|
||||
"start": 270,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"cond": {
|
||||
"end": 279,
|
||||
"name": "cc",
|
||||
"start": 277,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"digest": null,
|
||||
"else_ifs": [],
|
||||
"end": 300,
|
||||
"final_else": {
|
||||
"body": [
|
||||
{
|
||||
"end": 298,
|
||||
"expression": {
|
||||
"end": 298,
|
||||
"raw": "2",
|
||||
"start": 297,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 297,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 299,
|
||||
"start": 297
|
||||
},
|
||||
"start": 274,
|
||||
"then_val": {
|
||||
"body": [
|
||||
{
|
||||
"end": 285,
|
||||
"expression": {
|
||||
"end": 285,
|
||||
"raw": "1",
|
||||
"start": 284,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 284,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 286,
|
||||
"start": 284
|
||||
},
|
||||
"type": "IfExpression",
|
||||
"type": "IfExpression"
|
||||
},
|
||||
"start": 270,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 300,
|
||||
"kind": "const",
|
||||
"start": 270,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"end": 379,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 314,
|
||||
"left": {
|
||||
"end": 309,
|
||||
"name": "c",
|
||||
"start": 308,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"operator": "==",
|
||||
"right": {
|
||||
"end": 314,
|
||||
"raw": "1",
|
||||
"start": 313,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 308,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 378,
|
||||
"raw": "\"both branches of or are true makes the whole expression true\"",
|
||||
"start": 316,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "both branches of or are true makes the whole expression true"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 307,
|
||||
"name": "assert",
|
||||
"start": 301,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 379,
|
||||
"start": 301,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 301,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 399,
|
||||
"id": {
|
||||
"end": 383,
|
||||
"name": "dd",
|
||||
"start": 381,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"end": 399,
|
||||
"left": {
|
||||
"end": 391,
|
||||
"raw": "false",
|
||||
"start": 386,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"operator": "|",
|
||||
"right": {
|
||||
"end": 399,
|
||||
"raw": "false",
|
||||
"start": 394,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": false
|
||||
},
|
||||
"start": 386,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
"start": 381,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 399,
|
||||
"kind": "const",
|
||||
"start": 381,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"declaration": {
|
||||
"end": 430,
|
||||
"id": {
|
||||
"end": 401,
|
||||
"name": "d",
|
||||
"start": 400,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"init": {
|
||||
"cond": {
|
||||
"end": 409,
|
||||
"name": "dd",
|
||||
"start": 407,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"digest": null,
|
||||
"else_ifs": [],
|
||||
"end": 430,
|
||||
"final_else": {
|
||||
"body": [
|
||||
{
|
||||
"end": 428,
|
||||
"expression": {
|
||||
"end": 428,
|
||||
"raw": "2",
|
||||
"start": 427,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 427,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 429,
|
||||
"start": 427
|
||||
},
|
||||
"start": 404,
|
||||
"then_val": {
|
||||
"body": [
|
||||
{
|
||||
"end": 415,
|
||||
"expression": {
|
||||
"end": 415,
|
||||
"raw": "1",
|
||||
"start": 414,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 1.0
|
||||
},
|
||||
"start": 414,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 416,
|
||||
"start": 414
|
||||
},
|
||||
"type": "IfExpression",
|
||||
"type": "IfExpression"
|
||||
},
|
||||
"start": 400,
|
||||
"type": "VariableDeclarator"
|
||||
},
|
||||
"end": 430,
|
||||
"kind": "const",
|
||||
"start": 400,
|
||||
"type": "VariableDeclaration",
|
||||
"type": "VariableDeclaration"
|
||||
},
|
||||
{
|
||||
"end": 511,
|
||||
"expression": {
|
||||
"arguments": [
|
||||
{
|
||||
"end": 444,
|
||||
"left": {
|
||||
"end": 439,
|
||||
"name": "d",
|
||||
"start": 438,
|
||||
"type": "Identifier",
|
||||
"type": "Identifier"
|
||||
},
|
||||
"operator": "==",
|
||||
"right": {
|
||||
"end": 444,
|
||||
"raw": "2",
|
||||
"start": 443,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": 2.0
|
||||
},
|
||||
"start": 438,
|
||||
"type": "BinaryExpression",
|
||||
"type": "BinaryExpression"
|
||||
},
|
||||
{
|
||||
"end": 510,
|
||||
"raw": "\"both branches of or are false makes the whole expression false\"",
|
||||
"start": 446,
|
||||
"type": "Literal",
|
||||
"type": "Literal",
|
||||
"value": "both branches of or are false makes the whole expression false"
|
||||
}
|
||||
],
|
||||
"callee": {
|
||||
"end": 437,
|
||||
"name": "assert",
|
||||
"start": 431,
|
||||
"type": "Identifier"
|
||||
},
|
||||
"end": 511,
|
||||
"start": 431,
|
||||
"type": "CallExpression",
|
||||
"type": "CallExpression"
|
||||
},
|
||||
"start": 431,
|
||||
"type": "ExpressionStatement",
|
||||
"type": "ExpressionStatement"
|
||||
}
|
||||
],
|
||||
"end": 512,
|
||||
"nonCodeMeta": {
|
||||
"nonCodeNodes": {
|
||||
"2": [
|
||||
{
|
||||
"end": 126,
|
||||
"start": 124,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
],
|
||||
"5": [
|
||||
{
|
||||
"end": 253,
|
||||
"start": 251,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
],
|
||||
"8": [
|
||||
{
|
||||
"end": 381,
|
||||
"start": 379,
|
||||
"type": "NonCodeNode",
|
||||
"value": {
|
||||
"type": "newLine"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"startNodes": []
|
||||
},
|
||||
"start": 0
|
||||
}
|
||||
}
|
||||
31
src/wasm-lib/kcl/tests/boolean_logical_or/input.kcl
Normal file
@ -0,0 +1,31 @@
|
||||
aa = true | false
|
||||
a = if aa {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
assert(a == 1, "left branch of or is true makes the whole expression true")
|
||||
|
||||
bb = false | true
|
||||
b = if bb {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
assert(b == 1, "right branch of or is true makes the whole expression true")
|
||||
|
||||
cc = true | true
|
||||
c = if cc {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
assert(c == 1, "both branches of or are true makes the whole expression true")
|
||||
|
||||
dd = false | false
|
||||
d = if dd {
|
||||
1
|
||||
} else {
|
||||
2
|
||||
}
|
||||
assert(d == 2, "both branches of or are false makes the whole expression false")
|
||||
5
src/wasm-lib/kcl/tests/boolean_logical_or/ops.snap
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Operations executed boolean_logical_or.kcl
|
||||
---
|
||||
[]
|
||||
167
src/wasm-lib/kcl/tests/boolean_logical_or/program_memory.snap
Normal file
@ -0,0 +1,167 @@
|
||||
---
|
||||
source: kcl/src/simulation_tests.rs
|
||||
description: Program memory after executing boolean_logical_or.kcl
|
||||
---
|
||||
{
|
||||
"environments": [
|
||||
{
|
||||
"bindings": {
|
||||
"HALF_TURN": {
|
||||
"type": "Number",
|
||||
"value": 180.0,
|
||||
"__meta": []
|
||||
},
|
||||
"QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 90.0,
|
||||
"__meta": []
|
||||
},
|
||||
"THREE_QUARTER_TURN": {
|
||||
"type": "Number",
|
||||
"value": 270.0,
|
||||
"__meta": []
|
||||
},
|
||||
"ZERO": {
|
||||
"type": "Number",
|
||||
"value": 0.0,
|
||||
"__meta": []
|
||||
},
|
||||
"a": {
|
||||
"type": "Number",
|
||||
"value": 1.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
32,
|
||||
33,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"aa": {
|
||||
"type": "Bool",
|
||||
"value": true,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
5,
|
||||
9,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
12,
|
||||
17,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"b": {
|
||||
"type": "Number",
|
||||
"value": 1.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
158,
|
||||
159,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"bb": {
|
||||
"type": "Bool",
|
||||
"value": true,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
131,
|
||||
136,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
139,
|
||||
143,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"c": {
|
||||
"type": "Number",
|
||||
"value": 1.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
284,
|
||||
285,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"cc": {
|
||||
"type": "Bool",
|
||||
"value": true,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
258,
|
||||
262,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
265,
|
||||
269,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"d": {
|
||||
"type": "Number",
|
||||
"value": 2.0,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
427,
|
||||
428,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"dd": {
|
||||
"type": "Bool",
|
||||
"value": false,
|
||||
"__meta": [
|
||||
{
|
||||
"sourceRange": [
|
||||
386,
|
||||
391,
|
||||
0
|
||||
]
|
||||
},
|
||||
{
|
||||
"sourceRange": [
|
||||
394,
|
||||
399,
|
||||
0
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
"parent": null
|
||||
}
|
||||
],
|
||||
"currentEnv": 0,
|
||||
"return": null
|
||||
}
|
||||
|
Before Width: | Height: | Size: 122 KiB After Width: | Height: | Size: 132 KiB |
@ -5,8 +5,9 @@ description: Error from executing import_side_effect.kcl
|
||||
KCL Semantic error
|
||||
|
||||
× semantic: Error loading imported file. Open it to view more details.
|
||||
│ export_side_effect.kcl: Cannot send modeling commands while importing.
|
||||
│ Wrap your code in a function if you want to import the file.
|
||||
│ tests/import_side_effect/export_side_effect.kcl: Cannot send modeling
|
||||
│ commands while importing. Wrap your code in a function if you want to
|
||||
│ import the file.
|
||||
╭────
|
||||
1 │ import foo from "export_side_effect.kcl"
|
||||
· ────────────────────────────────────────
|
||||
|
||||