internal: KCL modules, part 1 (#4149)
Addresses #4080. (Not ready to close it yet.) # Important Requires a fix for #4147 before it can work in ZMA. # Overview ```kcl // numbers.kcl export fn inc = (x) => { return x + 1 } ``` ```kcl import inc from "numbers.kcl" answer = inc(41) ``` This also implements multiple imports with optional renaming. ```kcl import inc, dec from "numbers.kcl" import identity as id, length as len from "utils.kcl" ``` Note: Imported files _must_ be in the same directory. Things for a follow-up PR: - #4147. Currently, we cannot read files in WebAssembly, i.e. ZMA. - Docs - Should be an error to `import` anywhere besides the top level. Needs parser restructuring to track the context of a "function body". - Should be an error to have `export` anywhere besides the top level. It has no effect, but we should tell people it's not valid instead of silently ignoring it. - Error message for cycle detection is funky because the Rust side doesn't actually know the name of the first file. Message will say "b -> a -> b" instead of "a -> b -> a" when "a" is the top-level file. - Cache imported files so that they don't need to be re-parsed and re-executed.
This commit is contained in:
@ -82690,6 +82690,58 @@
|
|||||||
},
|
},
|
||||||
"BodyItem": {
|
"BodyItem": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"items",
|
||||||
|
"path",
|
||||||
|
"raw_path",
|
||||||
|
"start",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ImportStatement"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ImportItem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"raw_path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -82763,6 +82815,9 @@
|
|||||||
"$ref": "#/components/schemas/VariableDeclarator"
|
"$ref": "#/components/schemas/VariableDeclarator"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"visibility": {
|
||||||
|
"$ref": "#/components/schemas/ItemVisibility"
|
||||||
|
},
|
||||||
"kind": {
|
"kind": {
|
||||||
"$ref": "#/components/schemas/VariableKind"
|
"$ref": "#/components/schemas/VariableKind"
|
||||||
},
|
},
|
||||||
@ -82822,6 +82877,54 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"ImportItem": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"name",
|
||||||
|
"start"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Name of the item to import.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"alias": {
|
||||||
|
"description": "Rename the item using an identifier after \"as\".",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Expr": {
|
"Expr": {
|
||||||
"description": "An expression can be evaluated to yield a single KCL value.",
|
"description": "An expression can be evaluated to yield a single KCL value.",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
@ -84475,6 +84578,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ItemVisibility": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"default",
|
||||||
|
"export"
|
||||||
|
]
|
||||||
|
},
|
||||||
"VariableKind": {
|
"VariableKind": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
@ -86338,6 +86448,58 @@
|
|||||||
},
|
},
|
||||||
"BodyItem": {
|
"BodyItem": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"items",
|
||||||
|
"path",
|
||||||
|
"raw_path",
|
||||||
|
"start",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ImportStatement"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ImportItem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"raw_path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -86411,6 +86573,9 @@
|
|||||||
"$ref": "#/components/schemas/VariableDeclarator"
|
"$ref": "#/components/schemas/VariableDeclarator"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"visibility": {
|
||||||
|
"$ref": "#/components/schemas/ItemVisibility"
|
||||||
|
},
|
||||||
"kind": {
|
"kind": {
|
||||||
"$ref": "#/components/schemas/VariableKind"
|
"$ref": "#/components/schemas/VariableKind"
|
||||||
},
|
},
|
||||||
@ -86470,6 +86635,54 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"ImportItem": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"name",
|
||||||
|
"start"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Name of the item to import.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"alias": {
|
||||||
|
"description": "Rename the item using an identifier after \"as\".",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Expr": {
|
"Expr": {
|
||||||
"description": "An expression can be evaluated to yield a single KCL value.",
|
"description": "An expression can be evaluated to yield a single KCL value.",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
@ -88123,6 +88336,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ItemVisibility": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"default",
|
||||||
|
"export"
|
||||||
|
]
|
||||||
|
},
|
||||||
"VariableKind": {
|
"VariableKind": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
@ -89990,6 +90210,58 @@
|
|||||||
},
|
},
|
||||||
"BodyItem": {
|
"BodyItem": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"items",
|
||||||
|
"path",
|
||||||
|
"raw_path",
|
||||||
|
"start",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ImportStatement"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ImportItem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"raw_path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -90063,6 +90335,9 @@
|
|||||||
"$ref": "#/components/schemas/VariableDeclarator"
|
"$ref": "#/components/schemas/VariableDeclarator"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"visibility": {
|
||||||
|
"$ref": "#/components/schemas/ItemVisibility"
|
||||||
|
},
|
||||||
"kind": {
|
"kind": {
|
||||||
"$ref": "#/components/schemas/VariableKind"
|
"$ref": "#/components/schemas/VariableKind"
|
||||||
},
|
},
|
||||||
@ -90122,6 +90397,54 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"ImportItem": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"name",
|
||||||
|
"start"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Name of the item to import.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"alias": {
|
||||||
|
"description": "Rename the item using an identifier after \"as\".",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Expr": {
|
"Expr": {
|
||||||
"description": "An expression can be evaluated to yield a single KCL value.",
|
"description": "An expression can be evaluated to yield a single KCL value.",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
@ -91775,6 +92098,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ItemVisibility": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"default",
|
||||||
|
"export"
|
||||||
|
]
|
||||||
|
},
|
||||||
"VariableKind": {
|
"VariableKind": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
@ -114494,6 +114824,58 @@
|
|||||||
},
|
},
|
||||||
"BodyItem": {
|
"BodyItem": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"items",
|
||||||
|
"path",
|
||||||
|
"raw_path",
|
||||||
|
"start",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ImportStatement"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ImportItem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"raw_path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -114567,6 +114949,9 @@
|
|||||||
"$ref": "#/components/schemas/VariableDeclarator"
|
"$ref": "#/components/schemas/VariableDeclarator"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"visibility": {
|
||||||
|
"$ref": "#/components/schemas/ItemVisibility"
|
||||||
|
},
|
||||||
"kind": {
|
"kind": {
|
||||||
"$ref": "#/components/schemas/VariableKind"
|
"$ref": "#/components/schemas/VariableKind"
|
||||||
},
|
},
|
||||||
@ -114626,6 +115011,54 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"ImportItem": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"name",
|
||||||
|
"start"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Name of the item to import.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"alias": {
|
||||||
|
"description": "Rename the item using an identifier after \"as\".",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Expr": {
|
"Expr": {
|
||||||
"description": "An expression can be evaluated to yield a single KCL value.",
|
"description": "An expression can be evaluated to yield a single KCL value.",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
@ -116279,6 +116712,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ItemVisibility": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"default",
|
||||||
|
"export"
|
||||||
|
]
|
||||||
|
},
|
||||||
"VariableKind": {
|
"VariableKind": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
@ -118535,6 +118975,58 @@
|
|||||||
},
|
},
|
||||||
"BodyItem": {
|
"BodyItem": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"items",
|
||||||
|
"path",
|
||||||
|
"raw_path",
|
||||||
|
"start",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ImportStatement"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ImportItem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"raw_path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -118608,6 +119100,9 @@
|
|||||||
"$ref": "#/components/schemas/VariableDeclarator"
|
"$ref": "#/components/schemas/VariableDeclarator"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"visibility": {
|
||||||
|
"$ref": "#/components/schemas/ItemVisibility"
|
||||||
|
},
|
||||||
"kind": {
|
"kind": {
|
||||||
"$ref": "#/components/schemas/VariableKind"
|
"$ref": "#/components/schemas/VariableKind"
|
||||||
},
|
},
|
||||||
@ -118667,6 +119162,54 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"ImportItem": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"name",
|
||||||
|
"start"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Name of the item to import.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"alias": {
|
||||||
|
"description": "Rename the item using an identifier after \"as\".",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Expr": {
|
"Expr": {
|
||||||
"description": "An expression can be evaluated to yield a single KCL value.",
|
"description": "An expression can be evaluated to yield a single KCL value.",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
@ -120320,6 +120863,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ItemVisibility": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"default",
|
||||||
|
"export"
|
||||||
|
]
|
||||||
|
},
|
||||||
"VariableKind": {
|
"VariableKind": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
@ -122183,6 +122733,58 @@
|
|||||||
},
|
},
|
||||||
"BodyItem": {
|
"BodyItem": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"items",
|
||||||
|
"path",
|
||||||
|
"raw_path",
|
||||||
|
"start",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ImportStatement"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ImportItem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"raw_path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -122256,6 +122858,9 @@
|
|||||||
"$ref": "#/components/schemas/VariableDeclarator"
|
"$ref": "#/components/schemas/VariableDeclarator"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"visibility": {
|
||||||
|
"$ref": "#/components/schemas/ItemVisibility"
|
||||||
|
},
|
||||||
"kind": {
|
"kind": {
|
||||||
"$ref": "#/components/schemas/VariableKind"
|
"$ref": "#/components/schemas/VariableKind"
|
||||||
},
|
},
|
||||||
@ -122315,6 +122920,54 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"ImportItem": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"name",
|
||||||
|
"start"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Name of the item to import.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"alias": {
|
||||||
|
"description": "Rename the item using an identifier after \"as\".",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Expr": {
|
"Expr": {
|
||||||
"description": "An expression can be evaluated to yield a single KCL value.",
|
"description": "An expression can be evaluated to yield a single KCL value.",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
@ -123968,6 +124621,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ItemVisibility": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"default",
|
||||||
|
"export"
|
||||||
|
]
|
||||||
|
},
|
||||||
"VariableKind": {
|
"VariableKind": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
@ -125829,6 +126489,58 @@
|
|||||||
},
|
},
|
||||||
"BodyItem": {
|
"BodyItem": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
|
{
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"items",
|
||||||
|
"path",
|
||||||
|
"raw_path",
|
||||||
|
"start",
|
||||||
|
"type"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"type": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"ImportStatement"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"items": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ImportItem"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"raw_path": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"type": "object",
|
"type": "object",
|
||||||
"required": [
|
"required": [
|
||||||
@ -125902,6 +126614,9 @@
|
|||||||
"$ref": "#/components/schemas/VariableDeclarator"
|
"$ref": "#/components/schemas/VariableDeclarator"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"visibility": {
|
||||||
|
"$ref": "#/components/schemas/ItemVisibility"
|
||||||
|
},
|
||||||
"kind": {
|
"kind": {
|
||||||
"$ref": "#/components/schemas/VariableKind"
|
"$ref": "#/components/schemas/VariableKind"
|
||||||
},
|
},
|
||||||
@ -125961,6 +126676,54 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"ImportItem": {
|
||||||
|
"type": "object",
|
||||||
|
"required": [
|
||||||
|
"end",
|
||||||
|
"name",
|
||||||
|
"start"
|
||||||
|
],
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"description": "Name of the item to import.",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"alias": {
|
||||||
|
"description": "Rename the item using an identifier after \"as\".",
|
||||||
|
"allOf": [
|
||||||
|
{
|
||||||
|
"$ref": "#/components/schemas/Identifier"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"nullable": true
|
||||||
|
},
|
||||||
|
"start": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"digest": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "integer",
|
||||||
|
"format": "uint8",
|
||||||
|
"minimum": 0.0
|
||||||
|
},
|
||||||
|
"maxItems": 32,
|
||||||
|
"minItems": 32,
|
||||||
|
"nullable": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"Expr": {
|
"Expr": {
|
||||||
"description": "An expression can be evaluated to yield a single KCL value.",
|
"description": "An expression can be evaluated to yield a single KCL value.",
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
@ -127614,6 +128377,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"ItemVisibility": {
|
||||||
|
"type": "string",
|
||||||
|
"enum": [
|
||||||
|
"default",
|
||||||
|
"export"
|
||||||
|
]
|
||||||
|
},
|
||||||
"VariableKind": {
|
"VariableKind": {
|
||||||
"oneOf": [
|
"oneOf": [
|
||||||
{
|
{
|
||||||
|
@ -18,6 +18,27 @@ layout: manual
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `type` |enum: `ImportStatement`| | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `items` |`[` [`ImportItem`](/docs/kcl/types/ImportItem) `]`| | No |
|
||||||
|
| `path` |`string`| | No |
|
||||||
|
| `raw_path` |`string`| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
|
||||||
|
|
||||||
|
----
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Properties
|
## Properties
|
||||||
|
|
||||||
| Property | Type | Description | Required |
|
| Property | Type | Description | Required |
|
||||||
@ -45,6 +66,7 @@ layout: manual
|
|||||||
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No |
|
| `declarations` |`[` [`VariableDeclarator`](/docs/kcl/types/VariableDeclarator) `]`| | No |
|
||||||
|
| `visibility` |[`ItemVisibility`](/docs/kcl/types/ItemVisibility)| | No |
|
||||||
| `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No |
|
| `kind` |[`VariableKind`](/docs/kcl/types/VariableKind)| | No |
|
||||||
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
|
||||||
|
24
docs/kcl/types/ImportItem.md
Normal file
24
docs/kcl/types/ImportItem.md
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
title: "ImportItem"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**Type:** `object`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## Properties
|
||||||
|
|
||||||
|
| Property | Type | Description | Required |
|
||||||
|
|----------|------|-------------|----------|
|
||||||
|
| `name` |[`Identifier`](/docs/kcl/types/Identifier)| Name of the item to import. | No |
|
||||||
|
| `alias` |[`Identifier`](/docs/kcl/types/Identifier)| Rename the item using an identifier after "as". | No |
|
||||||
|
| `start` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `end` |[`EnvironmentRef`](/docs/kcl/types/EnvironmentRef)| | No |
|
||||||
|
| `digest` |`[, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`, `integer`]`| | No |
|
||||||
|
|
||||||
|
|
16
docs/kcl/types/ItemVisibility.md
Normal file
16
docs/kcl/types/ItemVisibility.md
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
title: "ItemVisibility"
|
||||||
|
excerpt: ""
|
||||||
|
layout: manual
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
**enum:** `default`, `export`
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,6 +1,9 @@
|
|||||||
import { styleTags, tags as t } from '@lezer/highlight'
|
import { styleTags, tags as t } from '@lezer/highlight'
|
||||||
|
|
||||||
export const kclHighlight = styleTags({
|
export const kclHighlight = styleTags({
|
||||||
|
'import export': t.moduleKeyword,
|
||||||
|
ImportItemAs: t.definitionKeyword,
|
||||||
|
ImportFrom: t.moduleKeyword,
|
||||||
'fn var let const': t.definitionKeyword,
|
'fn var let const': t.definitionKeyword,
|
||||||
'if else': t.controlKeyword,
|
'if else': t.controlKeyword,
|
||||||
return: t.controlKeyword,
|
return: t.controlKeyword,
|
||||||
|
@ -15,8 +15,9 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
statement[@isGroup=Statement] {
|
statement[@isGroup=Statement] {
|
||||||
FunctionDeclaration { kw<"fn"> VariableDefinition Equals ParamList Arrow Body } |
|
ImportStatement { kw<"import"> ImportItems ImportFrom String } |
|
||||||
VariableDeclaration { (kw<"var"> | kw<"let"> | kw<"const">)? VariableDefinition Equals expression } |
|
FunctionDeclaration { kw<"export">? kw<"fn"> VariableDefinition Equals ParamList Arrow Body } |
|
||||||
|
VariableDeclaration { kw<"export">? (kw<"var"> | kw<"let"> | kw<"const">)? VariableDefinition Equals expression } |
|
||||||
ReturnStatement { kw<"return"> expression } |
|
ReturnStatement { kw<"return"> expression } |
|
||||||
ExpressionStatement { expression }
|
ExpressionStatement { expression }
|
||||||
}
|
}
|
||||||
@ -25,6 +26,9 @@ ParamList { "(" commaSep<Parameter { VariableDefinition "?"? (":" type)? }> ")"
|
|||||||
|
|
||||||
Body { "{" statement* "}" }
|
Body { "{" statement* "}" }
|
||||||
|
|
||||||
|
ImportItems { commaSep1NoTrailingComma<ImportItem> }
|
||||||
|
ImportItem { identifier (ImportItemAs identifier)? }
|
||||||
|
|
||||||
expression[@isGroup=Expression] {
|
expression[@isGroup=Expression] {
|
||||||
String |
|
String |
|
||||||
Number |
|
Number |
|
||||||
@ -74,6 +78,8 @@ kw<term> { @specialize[@name={term}]<identifier, term> }
|
|||||||
|
|
||||||
commaSep<term> { (term ("," term)*)? ","? }
|
commaSep<term> { (term ("," term)*)? ","? }
|
||||||
|
|
||||||
|
commaSep1NoTrailingComma<term> { term ("," term)* }
|
||||||
|
|
||||||
@tokens {
|
@tokens {
|
||||||
String[isolate] { "'" ("\\" _ | !['\\])* "'" | '"' ("\\" _ | !["\\])* '"' }
|
String[isolate] { "'" ("\\" _ | !['\\])* "'" | '"' ("\\" _ | !["\\])* '"' }
|
||||||
|
|
||||||
@ -106,6 +112,9 @@ commaSep<term> { (term ("," term)*)? ","? }
|
|||||||
|
|
||||||
Shebang { "#!" ![\n]* }
|
Shebang { "#!" ![\n]* }
|
||||||
|
|
||||||
|
ImportItemAs { "as" }
|
||||||
|
ImportFrom { "from" }
|
||||||
|
|
||||||
"(" ")"
|
"(" ")"
|
||||||
"{" "}"
|
"{" "}"
|
||||||
"[" "]"
|
"[" "]"
|
||||||
|
@ -501,6 +501,7 @@ export function sketchOnExtrudedFace(
|
|||||||
createIdentifier(extrudeName ? extrudeName : oldSketchName),
|
createIdentifier(extrudeName ? extrudeName : oldSketchName),
|
||||||
_tag,
|
_tag,
|
||||||
]),
|
]),
|
||||||
|
undefined,
|
||||||
'const'
|
'const'
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -682,6 +683,7 @@ export function createPipeExpression(
|
|||||||
export function createVariableDeclaration(
|
export function createVariableDeclaration(
|
||||||
varName: string,
|
varName: string,
|
||||||
init: VariableDeclarator['init'],
|
init: VariableDeclarator['init'],
|
||||||
|
visibility: VariableDeclaration['visibility'] = 'default',
|
||||||
kind: VariableDeclaration['kind'] = 'const'
|
kind: VariableDeclaration['kind'] = 'const'
|
||||||
): VariableDeclaration {
|
): VariableDeclaration {
|
||||||
return {
|
return {
|
||||||
@ -699,6 +701,7 @@ export function createVariableDeclaration(
|
|||||||
init,
|
init,
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
visibility,
|
||||||
kind,
|
kind,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ import {
|
|||||||
getConstraintType,
|
getConstraintType,
|
||||||
} from './std/sketchcombos'
|
} from './std/sketchcombos'
|
||||||
import { err } from 'lib/trap'
|
import { err } from 'lib/trap'
|
||||||
|
import { ImportStatement } from 'wasm-lib/kcl/bindings/ImportStatement'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
* Retrieves a node from a given path within a Program node structure, optionally stopping at a specified node type.
|
||||||
@ -120,7 +121,12 @@ export function getNodeFromPathCurry(
|
|||||||
}
|
}
|
||||||
|
|
||||||
function moreNodePathFromSourceRange(
|
function moreNodePathFromSourceRange(
|
||||||
node: Expr | ExpressionStatement | VariableDeclaration | ReturnStatement,
|
node:
|
||||||
|
| Expr
|
||||||
|
| ImportStatement
|
||||||
|
| ExpressionStatement
|
||||||
|
| VariableDeclaration
|
||||||
|
| ReturnStatement,
|
||||||
sourceRange: Selection['range'],
|
sourceRange: Selection['range'],
|
||||||
previousPath: PathToNode = [['body', '']]
|
previousPath: PathToNode = [['body', '']]
|
||||||
): PathToNode {
|
): PathToNode {
|
||||||
|
@ -426,6 +426,7 @@ export const _executor = async (
|
|||||||
baseUnit,
|
baseUnit,
|
||||||
engineCommandManager,
|
engineCommandManager,
|
||||||
fileSystemManager,
|
fileSystemManager,
|
||||||
|
undefined,
|
||||||
isMock
|
isMock
|
||||||
)
|
)
|
||||||
return execStateFromRaw(execState)
|
return execStateFromRaw(execState)
|
||||||
|
@ -762,7 +762,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
|
|||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -17,7 +17,7 @@ mod test_examples_someFn {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -17,7 +17,7 @@ mod test_examples_someFn {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -17,7 +17,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
@ -51,7 +51,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -17,7 +17,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -18,7 +18,7 @@ mod test_examples_my_func {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
@ -52,7 +52,7 @@ mod test_examples_my_func {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -18,7 +18,7 @@ mod test_examples_line_to {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
@ -52,7 +52,7 @@ mod test_examples_line_to {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -17,7 +17,7 @@ mod test_examples_min {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
@ -51,7 +51,7 @@ mod test_examples_min {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -17,7 +17,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -17,7 +17,7 @@ mod test_examples_import {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -17,7 +17,7 @@ mod test_examples_import {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -17,7 +17,7 @@ mod test_examples_import {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -17,7 +17,7 @@ mod test_examples_show {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -17,7 +17,7 @@ mod test_examples_some_function {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: crate::executor::ContextType::Mock,
|
context_type: crate::executor::ContextType::Mock,
|
||||||
};
|
};
|
||||||
ctx.run(&program, None, id_generator).await.unwrap();
|
ctx.run(&program, None, id_generator, None).await.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
#[tokio::test(flavor = "multi_thread", worker_threads = 5)]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
extern crate alloc;
|
extern crate alloc;
|
||||||
use kcl_lib::ast::types::{
|
use kcl_lib::ast::types::{
|
||||||
BodyItem, Expr, Identifier, Literal, LiteralValue, NonCodeMeta, Program, VariableDeclaration, VariableDeclarator,
|
BodyItem, Expr, Identifier, ItemVisibility, Literal, LiteralValue, NonCodeMeta, Program, VariableDeclaration,
|
||||||
VariableKind,
|
VariableDeclarator, VariableKind,
|
||||||
};
|
};
|
||||||
use kcl_macros::parse;
|
use kcl_macros::parse;
|
||||||
use pretty_assertions::assert_eq;
|
use pretty_assertions::assert_eq;
|
||||||
@ -33,6 +33,7 @@ fn basic() {
|
|||||||
})),
|
})),
|
||||||
digest: None,
|
digest: None,
|
||||||
}],
|
}],
|
||||||
|
visibility: ItemVisibility::Default,
|
||||||
kind: VariableKind::Const,
|
kind: VariableKind::Const,
|
||||||
digest: None,
|
digest: None,
|
||||||
})],
|
})],
|
||||||
|
@ -178,7 +178,7 @@ async fn snapshot_endpoint(body: Bytes, state: ExecutorContext) -> Response<Body
|
|||||||
// Let users know if the test is taking a long time.
|
// Let users know if the test is taking a long time.
|
||||||
let (done_tx, done_rx) = oneshot::channel::<()>();
|
let (done_tx, done_rx) = oneshot::channel::<()>();
|
||||||
let timer = time_until(done_rx);
|
let timer = time_until(done_rx);
|
||||||
let snapshot = match state.execute_and_prepare_snapshot(&program, id_generator).await {
|
let snapshot = match state.execute_and_prepare_snapshot(&program, id_generator, None).await {
|
||||||
Ok(sn) => sn,
|
Ok(sn) => sn,
|
||||||
Err(e) => return kcl_err(e),
|
Err(e) => return kcl_err(e),
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use indexmap::IndexMap;
|
use indexmap::IndexMap;
|
||||||
use kcl_lib::{
|
use kcl_lib::{
|
||||||
|
engine::ExecutionKind,
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
executor::{DefaultPlanes, IdGenerator},
|
executor::{DefaultPlanes, IdGenerator},
|
||||||
};
|
};
|
||||||
@ -26,6 +27,7 @@ pub struct EngineConnection {
|
|||||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::executor::SourceRange)>>>,
|
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, kcl_lib::executor::SourceRange)>>>,
|
||||||
core_test: Arc<Mutex<String>>,
|
core_test: Arc<Mutex<String>>,
|
||||||
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||||
|
execution_kind: Arc<Mutex<ExecutionKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EngineConnection {
|
impl EngineConnection {
|
||||||
@ -39,6 +41,7 @@ impl EngineConnection {
|
|||||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||||
core_test: result,
|
core_test: result,
|
||||||
default_planes: Default::default(),
|
default_planes: Default::default(),
|
||||||
|
execution_kind: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -360,6 +363,18 @@ impl kcl_lib::engine::EngineManager for EngineConnection {
|
|||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execution_kind(&self) -> ExecutionKind {
|
||||||
|
let guard = self.execution_kind.lock().unwrap();
|
||||||
|
*guard
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||||
|
let mut guard = self.execution_kind.lock().unwrap();
|
||||||
|
let original = *guard;
|
||||||
|
*guard = execution_kind;
|
||||||
|
original
|
||||||
|
}
|
||||||
|
|
||||||
async fn default_planes(
|
async fn default_planes(
|
||||||
&self,
|
&self,
|
||||||
id_generator: &mut IdGenerator,
|
id_generator: &mut IdGenerator,
|
||||||
|
@ -23,7 +23,7 @@ pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: kcl_lib::executor::ContextType::MockCustomForwarded,
|
context_type: kcl_lib::executor::ContextType::MockCustomForwarded,
|
||||||
};
|
};
|
||||||
let _memory = ctx.run(&program, None, IdGenerator::default()).await?;
|
let _memory = ctx.run(&program, None, IdGenerator::default(), None).await?;
|
||||||
|
|
||||||
let result = result.lock().expect("mutex lock").clone();
|
let result = result.lock().expect("mutex lock").clone();
|
||||||
Ok(result)
|
Ok(result)
|
||||||
|
@ -48,7 +48,10 @@ pub async fn modify_ast_for_sketch(
|
|||||||
|
|
||||||
// Get the information about the sketch.
|
// Get the information about the sketch.
|
||||||
if let Some(ast_sketch) = program.get_variable(sketch_name) {
|
if let Some(ast_sketch) = program.get_variable(sketch_name) {
|
||||||
let constraint_level = ast_sketch.get_constraint_level();
|
let constraint_level = match ast_sketch {
|
||||||
|
super::types::Definition::Variable(var) => var.get_constraint_level(),
|
||||||
|
super::types::Definition::Import(import) => import.get_constraint_level(),
|
||||||
|
};
|
||||||
match &constraint_level {
|
match &constraint_level {
|
||||||
ConstraintLevel::None { source_ranges: _ } => {}
|
ConstraintLevel::None { source_ranges: _ } => {}
|
||||||
ConstraintLevel::Ignore { source_ranges: _ } => {}
|
ConstraintLevel::Ignore { source_ranges: _ } => {}
|
||||||
|
@ -40,6 +40,11 @@ mod none;
|
|||||||
/// Position-independent digest of the AST node.
|
/// Position-independent digest of the AST node.
|
||||||
pub type Digest = [u8; 32];
|
pub type Digest = [u8; 32];
|
||||||
|
|
||||||
|
pub enum Definition<'a> {
|
||||||
|
Variable(&'a VariableDeclarator),
|
||||||
|
Import(&'a ImportStatement),
|
||||||
|
}
|
||||||
|
|
||||||
/// A KCL program top level, or function body.
|
/// A KCL program top level, or function body.
|
||||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
#[databake(path = kcl_lib::ast::types)]
|
#[databake(path = kcl_lib::ast::types)]
|
||||||
@ -198,6 +203,7 @@ impl Program {
|
|||||||
|
|
||||||
// Recurse over the item.
|
// Recurse over the item.
|
||||||
match item {
|
match item {
|
||||||
|
BodyItem::ImportStatement(_) => None,
|
||||||
BodyItem::ExpressionStatement(expression_statement) => Some(&expression_statement.expression),
|
BodyItem::ExpressionStatement(expression_statement) => Some(&expression_statement.expression),
|
||||||
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.get_expr_for_position(pos),
|
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.get_expr_for_position(pos),
|
||||||
BodyItem::ReturnStatement(return_statement) => Some(&return_statement.argument),
|
BodyItem::ReturnStatement(return_statement) => Some(&return_statement.argument),
|
||||||
@ -214,6 +220,7 @@ impl Program {
|
|||||||
|
|
||||||
// Recurse over the item.
|
// Recurse over the item.
|
||||||
let expr = match item {
|
let expr = match item {
|
||||||
|
BodyItem::ImportStatement(_) => None,
|
||||||
BodyItem::ExpressionStatement(expression_statement) => Some(&expression_statement.expression),
|
BodyItem::ExpressionStatement(expression_statement) => Some(&expression_statement.expression),
|
||||||
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.get_expr_for_position(pos),
|
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.get_expr_for_position(pos),
|
||||||
BodyItem::ReturnStatement(return_statement) => Some(&return_statement.argument),
|
BodyItem::ReturnStatement(return_statement) => Some(&return_statement.argument),
|
||||||
@ -257,6 +264,7 @@ impl Program {
|
|||||||
// We only care about the top level things in the program.
|
// We only care about the top level things in the program.
|
||||||
for item in &self.body {
|
for item in &self.body {
|
||||||
match item {
|
match item {
|
||||||
|
BodyItem::ImportStatement(_) => continue,
|
||||||
BodyItem::ExpressionStatement(expression_statement) => {
|
BodyItem::ExpressionStatement(expression_statement) => {
|
||||||
if let Some(folding_range) = expression_statement.expression.get_lsp_folding_range() {
|
if let Some(folding_range) = expression_statement.expression.get_lsp_folding_range() {
|
||||||
ranges.push(folding_range)
|
ranges.push(folding_range)
|
||||||
@ -280,6 +288,12 @@ impl Program {
|
|||||||
let mut old_name = None;
|
let mut old_name = None;
|
||||||
for item in &mut self.body {
|
for item in &mut self.body {
|
||||||
match item {
|
match item {
|
||||||
|
BodyItem::ImportStatement(stmt) => {
|
||||||
|
if let Some(var_old_name) = stmt.rename_symbol(new_name, pos) {
|
||||||
|
old_name = Some(var_old_name);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
BodyItem::ExpressionStatement(_expression_statement) => {
|
BodyItem::ExpressionStatement(_expression_statement) => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -306,6 +320,7 @@ impl Program {
|
|||||||
|
|
||||||
// Recurse over the item.
|
// Recurse over the item.
|
||||||
let mut value = match item {
|
let mut value = match item {
|
||||||
|
BodyItem::ImportStatement(_) => None, // TODO
|
||||||
BodyItem::ExpressionStatement(ref mut expression_statement) => {
|
BodyItem::ExpressionStatement(ref mut expression_statement) => {
|
||||||
Some(&mut expression_statement.expression)
|
Some(&mut expression_statement.expression)
|
||||||
}
|
}
|
||||||
@ -337,6 +352,9 @@ impl Program {
|
|||||||
fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
|
fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
|
||||||
for item in &mut self.body {
|
for item in &mut self.body {
|
||||||
match item {
|
match item {
|
||||||
|
BodyItem::ImportStatement(ref mut stmt) => {
|
||||||
|
stmt.rename_identifiers(old_name, new_name);
|
||||||
|
}
|
||||||
BodyItem::ExpressionStatement(ref mut expression_statement) => {
|
BodyItem::ExpressionStatement(ref mut expression_statement) => {
|
||||||
expression_statement.expression.rename_identifiers(old_name, new_name);
|
expression_statement.expression.rename_identifiers(old_name, new_name);
|
||||||
}
|
}
|
||||||
@ -354,6 +372,9 @@ impl Program {
|
|||||||
pub fn replace_variable(&mut self, name: &str, declarator: VariableDeclarator) {
|
pub fn replace_variable(&mut self, name: &str, declarator: VariableDeclarator) {
|
||||||
for item in &mut self.body {
|
for item in &mut self.body {
|
||||||
match item {
|
match item {
|
||||||
|
BodyItem::ImportStatement(_) => {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
BodyItem::ExpressionStatement(_expression_statement) => {
|
BodyItem::ExpressionStatement(_expression_statement) => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -374,6 +395,7 @@ impl Program {
|
|||||||
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Expr) {
|
pub fn replace_value(&mut self, source_range: SourceRange, new_value: Expr) {
|
||||||
for item in &mut self.body {
|
for item in &mut self.body {
|
||||||
match item {
|
match item {
|
||||||
|
BodyItem::ImportStatement(_) => {} // TODO
|
||||||
BodyItem::ExpressionStatement(ref mut expression_statement) => expression_statement
|
BodyItem::ExpressionStatement(ref mut expression_statement) => expression_statement
|
||||||
.expression
|
.expression
|
||||||
.replace_value(source_range, new_value.clone()),
|
.replace_value(source_range, new_value.clone()),
|
||||||
@ -388,16 +410,23 @@ impl Program {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Get the variable declaration with the given name.
|
/// Get the variable declaration with the given name.
|
||||||
pub fn get_variable(&self, name: &str) -> Option<&VariableDeclarator> {
|
pub fn get_variable(&self, name: &str) -> Option<Definition<'_>> {
|
||||||
for item in &self.body {
|
for item in &self.body {
|
||||||
match item {
|
match item {
|
||||||
|
BodyItem::ImportStatement(stmt) => {
|
||||||
|
for import_item in &stmt.items {
|
||||||
|
if import_item.identifier() == name {
|
||||||
|
return Some(Definition::Import(stmt.as_ref()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
BodyItem::ExpressionStatement(_expression_statement) => {
|
BodyItem::ExpressionStatement(_expression_statement) => {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
BodyItem::VariableDeclaration(variable_declaration) => {
|
BodyItem::VariableDeclaration(variable_declaration) => {
|
||||||
for declaration in &variable_declaration.declarations {
|
for declaration in &variable_declaration.declarations {
|
||||||
if declaration.id.name == name {
|
if declaration.id.name == name {
|
||||||
return Some(declaration);
|
return Some(Definition::Variable(declaration));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,6 +483,7 @@ pub(crate) use impl_value_meta;
|
|||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
#[serde(tag = "type")]
|
#[serde(tag = "type")]
|
||||||
pub enum BodyItem {
|
pub enum BodyItem {
|
||||||
|
ImportStatement(Box<ImportStatement>),
|
||||||
ExpressionStatement(ExpressionStatement),
|
ExpressionStatement(ExpressionStatement),
|
||||||
VariableDeclaration(VariableDeclaration),
|
VariableDeclaration(VariableDeclaration),
|
||||||
ReturnStatement(ReturnStatement),
|
ReturnStatement(ReturnStatement),
|
||||||
@ -462,6 +492,7 @@ pub enum BodyItem {
|
|||||||
impl BodyItem {
|
impl BodyItem {
|
||||||
pub fn compute_digest(&mut self) -> Digest {
|
pub fn compute_digest(&mut self) -> Digest {
|
||||||
match self {
|
match self {
|
||||||
|
BodyItem::ImportStatement(s) => s.compute_digest(),
|
||||||
BodyItem::ExpressionStatement(es) => es.compute_digest(),
|
BodyItem::ExpressionStatement(es) => es.compute_digest(),
|
||||||
BodyItem::VariableDeclaration(vs) => vs.compute_digest(),
|
BodyItem::VariableDeclaration(vs) => vs.compute_digest(),
|
||||||
BodyItem::ReturnStatement(rs) => rs.compute_digest(),
|
BodyItem::ReturnStatement(rs) => rs.compute_digest(),
|
||||||
@ -470,6 +501,7 @@ impl BodyItem {
|
|||||||
|
|
||||||
pub fn start(&self) -> usize {
|
pub fn start(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
|
BodyItem::ImportStatement(stmt) => stmt.start(),
|
||||||
BodyItem::ExpressionStatement(expression_statement) => expression_statement.start(),
|
BodyItem::ExpressionStatement(expression_statement) => expression_statement.start(),
|
||||||
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.start(),
|
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.start(),
|
||||||
BodyItem::ReturnStatement(return_statement) => return_statement.start(),
|
BodyItem::ReturnStatement(return_statement) => return_statement.start(),
|
||||||
@ -478,6 +510,7 @@ impl BodyItem {
|
|||||||
|
|
||||||
pub fn end(&self) -> usize {
|
pub fn end(&self) -> usize {
|
||||||
match self {
|
match self {
|
||||||
|
BodyItem::ImportStatement(stmt) => stmt.end(),
|
||||||
BodyItem::ExpressionStatement(expression_statement) => expression_statement.end(),
|
BodyItem::ExpressionStatement(expression_statement) => expression_statement.end(),
|
||||||
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.end(),
|
BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.end(),
|
||||||
BodyItem::ReturnStatement(return_statement) => return_statement.end(),
|
BodyItem::ReturnStatement(return_statement) => return_statement.end(),
|
||||||
@ -1123,6 +1156,124 @@ impl NonCodeMeta {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
|
#[databake(path = kcl_lib::ast::types)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub struct ImportItem {
|
||||||
|
/// Name of the item to import.
|
||||||
|
pub name: Identifier,
|
||||||
|
/// Rename the item using an identifier after "as".
|
||||||
|
pub alias: Option<Identifier>,
|
||||||
|
|
||||||
|
pub start: usize,
|
||||||
|
pub end: usize,
|
||||||
|
|
||||||
|
pub digest: Option<Digest>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_value_meta!(ImportItem);
|
||||||
|
|
||||||
|
impl ImportItem {
|
||||||
|
compute_digest!(|slf, hasher| {
|
||||||
|
let name = slf.name.name.as_bytes();
|
||||||
|
hasher.update(name.len().to_ne_bytes());
|
||||||
|
hasher.update(name);
|
||||||
|
if let Some(alias) = &mut slf.alias {
|
||||||
|
hasher.update([1]);
|
||||||
|
hasher.update(alias.compute_digest());
|
||||||
|
} else {
|
||||||
|
hasher.update([0]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn identifier(&self) -> &str {
|
||||||
|
match &self.alias {
|
||||||
|
Some(alias) => &alias.name,
|
||||||
|
None => &self.name.name,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rename_symbol(&mut self, new_name: &str, pos: usize) -> Option<String> {
|
||||||
|
match &mut self.alias {
|
||||||
|
Some(alias) => {
|
||||||
|
let alias_source_range = SourceRange::from(&*alias);
|
||||||
|
if !alias_source_range.contains(pos) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
let old_name = std::mem::replace(&mut alias.name, new_name.to_owned());
|
||||||
|
Some(old_name)
|
||||||
|
}
|
||||||
|
None => {
|
||||||
|
let use_source_range = SourceRange::from(&*self);
|
||||||
|
if use_source_range.contains(pos) {
|
||||||
|
self.alias = Some(Identifier::new(new_name));
|
||||||
|
}
|
||||||
|
// Return implicit name.
|
||||||
|
return Some(self.identifier().to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
|
||||||
|
if let Some(alias) = &mut self.alias {
|
||||||
|
alias.rename(old_name, new_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
|
#[databake(path = kcl_lib::ast::types)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(tag = "type")]
|
||||||
|
pub struct ImportStatement {
|
||||||
|
pub start: usize,
|
||||||
|
pub end: usize,
|
||||||
|
pub items: Vec<ImportItem>,
|
||||||
|
pub path: String,
|
||||||
|
pub raw_path: String,
|
||||||
|
|
||||||
|
pub digest: Option<Digest>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl_value_meta!(ImportStatement);
|
||||||
|
|
||||||
|
impl ImportStatement {
|
||||||
|
compute_digest!(|slf, hasher| {
|
||||||
|
for item in &mut slf.items {
|
||||||
|
hasher.update(item.compute_digest());
|
||||||
|
}
|
||||||
|
let path = slf.path.as_bytes();
|
||||||
|
hasher.update(path.len().to_ne_bytes());
|
||||||
|
hasher.update(path);
|
||||||
|
});
|
||||||
|
|
||||||
|
pub fn get_constraint_level(&self) -> ConstraintLevel {
|
||||||
|
ConstraintLevel::Full {
|
||||||
|
source_ranges: vec![self.into()],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rename_symbol(&mut self, new_name: &str, pos: usize) -> Option<String> {
|
||||||
|
for item in &mut self.items {
|
||||||
|
let source_range = SourceRange::from(&*item);
|
||||||
|
if source_range.contains(pos) {
|
||||||
|
let old_name = item.rename_symbol(new_name, pos);
|
||||||
|
if old_name.is_some() {
|
||||||
|
return old_name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn rename_identifiers(&mut self, old_name: &str, new_name: &str) {
|
||||||
|
for item in &mut self.items {
|
||||||
|
item.rename_identifiers(old_name, new_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
#[databake(path = kcl_lib::ast::types)]
|
#[databake(path = kcl_lib::ast::types)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@ -1284,6 +1435,32 @@ impl PartialEq for Function {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(
|
||||||
|
Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, FromStr, Display, Bake,
|
||||||
|
)]
|
||||||
|
#[databake(path = kcl_lib::ast::types)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[display(style = "snake_case")]
|
||||||
|
pub enum ItemVisibility {
|
||||||
|
#[default]
|
||||||
|
Default,
|
||||||
|
Export,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ItemVisibility {
|
||||||
|
fn digestable_id(&self) -> [u8; 1] {
|
||||||
|
match self {
|
||||||
|
ItemVisibility::Default => [0],
|
||||||
|
ItemVisibility::Export => [1],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_default(&self) -> bool {
|
||||||
|
matches!(self, Self::Default)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
|
||||||
#[databake(path = kcl_lib::ast::types)]
|
#[databake(path = kcl_lib::ast::types)]
|
||||||
#[ts(export)]
|
#[ts(export)]
|
||||||
@ -1292,6 +1469,8 @@ pub struct VariableDeclaration {
|
|||||||
pub start: usize,
|
pub start: usize,
|
||||||
pub end: usize,
|
pub end: usize,
|
||||||
pub declarations: Vec<VariableDeclarator>,
|
pub declarations: Vec<VariableDeclarator>,
|
||||||
|
#[serde(default, skip_serializing_if = "ItemVisibility::is_default")]
|
||||||
|
pub visibility: ItemVisibility,
|
||||||
pub kind: VariableKind, // Change to enum if there are specific values
|
pub kind: VariableKind, // Change to enum if there are specific values
|
||||||
|
|
||||||
pub digest: Option<Digest>,
|
pub digest: Option<Digest>,
|
||||||
@ -1337,14 +1516,16 @@ impl VariableDeclaration {
|
|||||||
for declarator in &mut slf.declarations {
|
for declarator in &mut slf.declarations {
|
||||||
hasher.update(declarator.compute_digest());
|
hasher.update(declarator.compute_digest());
|
||||||
}
|
}
|
||||||
|
hasher.update(slf.visibility.digestable_id());
|
||||||
hasher.update(slf.kind.digestable_id());
|
hasher.update(slf.kind.digestable_id());
|
||||||
});
|
});
|
||||||
|
|
||||||
pub fn new(declarations: Vec<VariableDeclarator>, kind: VariableKind) -> Self {
|
pub fn new(declarations: Vec<VariableDeclarator>, visibility: ItemVisibility, kind: VariableKind) -> Self {
|
||||||
Self {
|
Self {
|
||||||
start: 0,
|
start: 0,
|
||||||
end: 0,
|
end: 0,
|
||||||
declarations,
|
declarations,
|
||||||
|
visibility,
|
||||||
kind,
|
kind,
|
||||||
digest: None,
|
digest: None,
|
||||||
}
|
}
|
||||||
|
@ -24,6 +24,8 @@ use crate::{
|
|||||||
executor::{DefaultPlanes, IdGenerator},
|
executor::{DefaultPlanes, IdGenerator},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::ExecutionKind;
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
#[derive(Debug, PartialEq)]
|
||||||
enum SocketHealth {
|
enum SocketHealth {
|
||||||
Active,
|
Active,
|
||||||
@ -46,6 +48,8 @@ pub struct EngineConnection {
|
|||||||
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
default_planes: Arc<RwLock<Option<DefaultPlanes>>>,
|
||||||
/// If the server sends session data, it'll be copied to here.
|
/// If the server sends session data, it'll be copied to here.
|
||||||
session_data: Arc<Mutex<Option<ModelingSessionData>>>,
|
session_data: Arc<Mutex<Option<ModelingSessionData>>>,
|
||||||
|
|
||||||
|
execution_kind: Arc<Mutex<ExecutionKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TcpRead {
|
pub struct TcpRead {
|
||||||
@ -300,6 +304,7 @@ impl EngineConnection {
|
|||||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||||
default_planes: Default::default(),
|
default_planes: Default::default(),
|
||||||
session_data,
|
session_data,
|
||||||
|
execution_kind: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -314,6 +319,18 @@ impl EngineManager for EngineConnection {
|
|||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execution_kind(&self) -> ExecutionKind {
|
||||||
|
let guard = self.execution_kind.lock().unwrap();
|
||||||
|
*guard
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||||
|
let mut guard = self.execution_kind.lock().unwrap();
|
||||||
|
let original = *guard;
|
||||||
|
*guard = execution_kind;
|
||||||
|
original
|
||||||
|
}
|
||||||
|
|
||||||
async fn default_planes(
|
async fn default_planes(
|
||||||
&self,
|
&self,
|
||||||
id_generator: &mut IdGenerator,
|
id_generator: &mut IdGenerator,
|
||||||
|
@ -22,10 +22,13 @@ use crate::{
|
|||||||
executor::{DefaultPlanes, IdGenerator},
|
executor::{DefaultPlanes, IdGenerator},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use super::ExecutionKind;
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct EngineConnection {
|
pub struct EngineConnection {
|
||||||
batch: Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>>,
|
batch: Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>>,
|
||||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>>,
|
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>>,
|
||||||
|
execution_kind: Arc<Mutex<ExecutionKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EngineConnection {
|
impl EngineConnection {
|
||||||
@ -33,6 +36,7 @@ impl EngineConnection {
|
|||||||
Ok(EngineConnection {
|
Ok(EngineConnection {
|
||||||
batch: Arc::new(Mutex::new(Vec::new())),
|
batch: Arc::new(Mutex::new(Vec::new())),
|
||||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||||
|
execution_kind: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,6 +51,18 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execution_kind(&self) -> ExecutionKind {
|
||||||
|
let guard = self.execution_kind.lock().unwrap();
|
||||||
|
*guard
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||||
|
let mut guard = self.execution_kind.lock().unwrap();
|
||||||
|
let original = *guard;
|
||||||
|
*guard = execution_kind;
|
||||||
|
original
|
||||||
|
}
|
||||||
|
|
||||||
async fn default_planes(
|
async fn default_planes(
|
||||||
&self,
|
&self,
|
||||||
_id_generator: &mut IdGenerator,
|
_id_generator: &mut IdGenerator,
|
||||||
|
@ -9,6 +9,7 @@ use kittycad_modeling_cmds as kcmc;
|
|||||||
use wasm_bindgen::prelude::*;
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
engine::ExecutionKind,
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::{DefaultPlanes, IdGenerator},
|
executor::{DefaultPlanes, IdGenerator},
|
||||||
};
|
};
|
||||||
@ -42,6 +43,7 @@ pub struct EngineConnection {
|
|||||||
manager: Arc<EngineCommandManager>,
|
manager: Arc<EngineCommandManager>,
|
||||||
batch: Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>>,
|
batch: Arc<Mutex<Vec<(WebSocketRequest, crate::executor::SourceRange)>>>,
|
||||||
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>>,
|
batch_end: Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>>,
|
||||||
|
execution_kind: Arc<Mutex<ExecutionKind>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Safety: WebAssembly will only ever run in a single-threaded context.
|
// Safety: WebAssembly will only ever run in a single-threaded context.
|
||||||
@ -54,6 +56,7 @@ impl EngineConnection {
|
|||||||
manager: Arc::new(manager),
|
manager: Arc::new(manager),
|
||||||
batch: Arc::new(Mutex::new(Vec::new())),
|
batch: Arc::new(Mutex::new(Vec::new())),
|
||||||
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
batch_end: Arc::new(Mutex::new(IndexMap::new())),
|
||||||
|
execution_kind: Default::default(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -68,6 +71,18 @@ impl crate::engine::EngineManager for EngineConnection {
|
|||||||
self.batch_end.clone()
|
self.batch_end.clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn execution_kind(&self) -> ExecutionKind {
|
||||||
|
let guard = self.execution_kind.lock().unwrap();
|
||||||
|
*guard
|
||||||
|
}
|
||||||
|
|
||||||
|
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind {
|
||||||
|
let mut guard = self.execution_kind.lock().unwrap();
|
||||||
|
let original = *guard;
|
||||||
|
*guard = execution_kind;
|
||||||
|
original
|
||||||
|
}
|
||||||
|
|
||||||
async fn default_planes(
|
async fn default_planes(
|
||||||
&self,
|
&self,
|
||||||
_id_generator: &mut IdGenerator,
|
_id_generator: &mut IdGenerator,
|
||||||
|
@ -41,6 +41,23 @@ lazy_static::lazy_static! {
|
|||||||
pub static ref GRID_SCALE_TEXT_OBJECT_ID: uuid::Uuid = uuid::Uuid::parse_str("10782f33-f588-4668-8bcd-040502d26590").unwrap();
|
pub static ref GRID_SCALE_TEXT_OBJECT_ID: uuid::Uuid = uuid::Uuid::parse_str("10782f33-f588-4668-8bcd-040502d26590").unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The mode of execution. When isolated, like during an import, attempting to
|
||||||
|
/// send a command results in an error.
|
||||||
|
#[derive(Debug, Default, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, JsonSchema)]
|
||||||
|
#[ts(export)]
|
||||||
|
#[serde(rename_all = "camelCase")]
|
||||||
|
pub enum ExecutionKind {
|
||||||
|
#[default]
|
||||||
|
Normal,
|
||||||
|
Isolated,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ExecutionKind {
|
||||||
|
pub fn is_isolated(&self) -> bool {
|
||||||
|
matches!(self, ExecutionKind::Isolated)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
#[async_trait::async_trait]
|
||||||
pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
||||||
/// Get the batch of commands to be sent to the engine.
|
/// Get the batch of commands to be sent to the engine.
|
||||||
@ -49,6 +66,13 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
/// Get the batch of end commands to be sent to the engine.
|
/// Get the batch of end commands to be sent to the engine.
|
||||||
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>>;
|
fn batch_end(&self) -> Arc<Mutex<IndexMap<uuid::Uuid, (WebSocketRequest, crate::executor::SourceRange)>>>;
|
||||||
|
|
||||||
|
/// Get the current execution kind.
|
||||||
|
fn execution_kind(&self) -> ExecutionKind;
|
||||||
|
|
||||||
|
/// Replace the current execution kind with a new value and return the
|
||||||
|
/// existing value.
|
||||||
|
fn replace_execution_kind(&self, execution_kind: ExecutionKind) -> ExecutionKind;
|
||||||
|
|
||||||
/// Get the default planes.
|
/// Get the default planes.
|
||||||
async fn default_planes(
|
async fn default_planes(
|
||||||
&self,
|
&self,
|
||||||
@ -102,6 +126,10 @@ pub trait EngineManager: std::fmt::Debug + Send + Sync + 'static {
|
|||||||
source_range: crate::executor::SourceRange,
|
source_range: crate::executor::SourceRange,
|
||||||
cmd: &ModelingCmd,
|
cmd: &ModelingCmd,
|
||||||
) -> Result<(), crate::errors::KclError> {
|
) -> Result<(), crate::errors::KclError> {
|
||||||
|
let execution_kind = self.execution_kind();
|
||||||
|
if execution_kind.is_isolated() {
|
||||||
|
return Err(KclError::Semantic(KclErrorDetails { message: "Cannot send modeling commands while importing. Wrap your code in a function if you want to import the file.".to_owned(), source_ranges: vec![source_range] }));
|
||||||
|
}
|
||||||
let req = WebSocketRequest::ModelingCmdReq(ModelingCmdReq {
|
let req = WebSocketRequest::ModelingCmdReq(ModelingCmdReq {
|
||||||
cmd: cmd.clone(),
|
cmd: cmd.clone(),
|
||||||
cmd_id: id.into(),
|
cmd_id: id.into(),
|
||||||
|
@ -14,6 +14,8 @@ pub enum KclError {
|
|||||||
Syntax(KclErrorDetails),
|
Syntax(KclErrorDetails),
|
||||||
#[error("semantic: {0:?}")]
|
#[error("semantic: {0:?}")]
|
||||||
Semantic(KclErrorDetails),
|
Semantic(KclErrorDetails),
|
||||||
|
#[error("import cycle: {0:?}")]
|
||||||
|
ImportCycle(KclErrorDetails),
|
||||||
#[error("type: {0:?}")]
|
#[error("type: {0:?}")]
|
||||||
Type(KclErrorDetails),
|
Type(KclErrorDetails),
|
||||||
#[error("unimplemented: {0:?}")]
|
#[error("unimplemented: {0:?}")]
|
||||||
@ -52,6 +54,7 @@ impl KclError {
|
|||||||
KclError::Lexical(_) => "lexical",
|
KclError::Lexical(_) => "lexical",
|
||||||
KclError::Syntax(_) => "syntax",
|
KclError::Syntax(_) => "syntax",
|
||||||
KclError::Semantic(_) => "semantic",
|
KclError::Semantic(_) => "semantic",
|
||||||
|
KclError::ImportCycle(_) => "import cycle",
|
||||||
KclError::Type(_) => "type",
|
KclError::Type(_) => "type",
|
||||||
KclError::Unimplemented(_) => "unimplemented",
|
KclError::Unimplemented(_) => "unimplemented",
|
||||||
KclError::Unexpected(_) => "unexpected",
|
KclError::Unexpected(_) => "unexpected",
|
||||||
@ -68,6 +71,7 @@ impl KclError {
|
|||||||
KclError::Lexical(e) => e.source_ranges.clone(),
|
KclError::Lexical(e) => e.source_ranges.clone(),
|
||||||
KclError::Syntax(e) => e.source_ranges.clone(),
|
KclError::Syntax(e) => e.source_ranges.clone(),
|
||||||
KclError::Semantic(e) => e.source_ranges.clone(),
|
KclError::Semantic(e) => e.source_ranges.clone(),
|
||||||
|
KclError::ImportCycle(e) => e.source_ranges.clone(),
|
||||||
KclError::Type(e) => e.source_ranges.clone(),
|
KclError::Type(e) => e.source_ranges.clone(),
|
||||||
KclError::Unimplemented(e) => e.source_ranges.clone(),
|
KclError::Unimplemented(e) => e.source_ranges.clone(),
|
||||||
KclError::Unexpected(e) => e.source_ranges.clone(),
|
KclError::Unexpected(e) => e.source_ranges.clone(),
|
||||||
@ -85,6 +89,7 @@ impl KclError {
|
|||||||
KclError::Lexical(e) => &e.message,
|
KclError::Lexical(e) => &e.message,
|
||||||
KclError::Syntax(e) => &e.message,
|
KclError::Syntax(e) => &e.message,
|
||||||
KclError::Semantic(e) => &e.message,
|
KclError::Semantic(e) => &e.message,
|
||||||
|
KclError::ImportCycle(e) => &e.message,
|
||||||
KclError::Type(e) => &e.message,
|
KclError::Type(e) => &e.message,
|
||||||
KclError::Unimplemented(e) => &e.message,
|
KclError::Unimplemented(e) => &e.message,
|
||||||
KclError::Unexpected(e) => &e.message,
|
KclError::Unexpected(e) => &e.message,
|
||||||
@ -102,6 +107,7 @@ impl KclError {
|
|||||||
KclError::Lexical(e) => e.source_ranges = source_ranges,
|
KclError::Lexical(e) => e.source_ranges = source_ranges,
|
||||||
KclError::Syntax(e) => e.source_ranges = source_ranges,
|
KclError::Syntax(e) => e.source_ranges = source_ranges,
|
||||||
KclError::Semantic(e) => e.source_ranges = source_ranges,
|
KclError::Semantic(e) => e.source_ranges = source_ranges,
|
||||||
|
KclError::ImportCycle(e) => e.source_ranges = source_ranges,
|
||||||
KclError::Type(e) => e.source_ranges = source_ranges,
|
KclError::Type(e) => e.source_ranges = source_ranges,
|
||||||
KclError::Unimplemented(e) => e.source_ranges = source_ranges,
|
KclError::Unimplemented(e) => e.source_ranges = source_ranges,
|
||||||
KclError::Unexpected(e) => e.source_ranges = source_ranges,
|
KclError::Unexpected(e) => e.source_ranges = source_ranges,
|
||||||
@ -121,6 +127,7 @@ impl KclError {
|
|||||||
KclError::Lexical(e) => e.source_ranges.extend(source_ranges),
|
KclError::Lexical(e) => e.source_ranges.extend(source_ranges),
|
||||||
KclError::Syntax(e) => e.source_ranges.extend(source_ranges),
|
KclError::Syntax(e) => e.source_ranges.extend(source_ranges),
|
||||||
KclError::Semantic(e) => e.source_ranges.extend(source_ranges),
|
KclError::Semantic(e) => e.source_ranges.extend(source_ranges),
|
||||||
|
KclError::ImportCycle(e) => e.source_ranges.extend(source_ranges),
|
||||||
KclError::Type(e) => e.source_ranges.extend(source_ranges),
|
KclError::Type(e) => e.source_ranges.extend(source_ranges),
|
||||||
KclError::Unimplemented(e) => e.source_ranges.extend(source_ranges),
|
KclError::Unimplemented(e) => e.source_ranges.extend(source_ranges),
|
||||||
KclError::Unexpected(e) => e.source_ranges.extend(source_ranges),
|
KclError::Unexpected(e) => e.source_ranges.extend(source_ranges),
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
//! The executor for the AST.
|
//! The executor for the AST.
|
||||||
|
|
||||||
use std::{collections::HashMap, sync::Arc};
|
use std::{
|
||||||
|
collections::{HashMap, HashSet},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use async_recursion::async_recursion;
|
use async_recursion::async_recursion;
|
||||||
@ -23,12 +26,12 @@ type Point3D = kcmc::shared::Point3d<f64>;
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
human_friendly_type, BodyItem, Expr, ExpressionStatement, FunctionExpression, KclNone, Program,
|
human_friendly_type, BodyItem, Expr, ExpressionStatement, FunctionExpression, ImportStatement, ItemVisibility,
|
||||||
ReturnStatement, TagDeclarator,
|
KclNone, Program, ReturnStatement, TagDeclarator,
|
||||||
},
|
},
|
||||||
engine::EngineManager,
|
engine::{EngineManager, ExecutionKind},
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
fs::FileManager,
|
fs::{FileManager, FileSystem},
|
||||||
settings::types::UnitLength,
|
settings::types::UnitLength,
|
||||||
std::{FnAsArg, StdLib},
|
std::{FnAsArg, StdLib},
|
||||||
};
|
};
|
||||||
@ -47,6 +50,14 @@ pub struct ExecState {
|
|||||||
/// The current value of the pipe operator returned from the previous
|
/// The current value of the pipe operator returned from the previous
|
||||||
/// expression. If we're not currently in a pipeline, this will be None.
|
/// expression. If we're not currently in a pipeline, this will be None.
|
||||||
pub pipe_value: Option<KclValue>,
|
pub pipe_value: Option<KclValue>,
|
||||||
|
/// Identifiers that have been exported from the current module.
|
||||||
|
pub module_exports: HashSet<String>,
|
||||||
|
/// 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>,
|
||||||
|
/// The directory of the current project. This is used for resolving import
|
||||||
|
/// paths. If None is given, the current working directory is used.
|
||||||
|
pub project_directory: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
|
||||||
@ -391,6 +402,20 @@ impl KclValue {
|
|||||||
KclValue::Face(_) => "Face",
|
KclValue::Face(_) => "Face",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn is_function(&self) -> bool {
|
||||||
|
match self {
|
||||||
|
KclValue::UserVal(..)
|
||||||
|
| KclValue::TagIdentifier(..)
|
||||||
|
| KclValue::TagDeclarator(..)
|
||||||
|
| KclValue::Plane(..)
|
||||||
|
| KclValue::Face(..)
|
||||||
|
| KclValue::Solid(..)
|
||||||
|
| KclValue::Solids { .. }
|
||||||
|
| KclValue::ImportedGeometry(..) => false,
|
||||||
|
KclValue::Function { .. } => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<SketchSet> for KclValue {
|
impl From<SketchSet> for KclValue {
|
||||||
@ -1504,6 +1529,14 @@ impl From<SourceRange> for Metadata {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<&ImportStatement> for Metadata {
|
||||||
|
fn from(stmt: &ImportStatement) -> Self {
|
||||||
|
Self {
|
||||||
|
source_range: SourceRange::new(stmt.start, stmt.end),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl From<&ExpressionStatement> for Metadata {
|
impl From<&ExpressionStatement> for Metadata {
|
||||||
fn from(exp_statement: &ExpressionStatement) -> Self {
|
fn from(exp_statement: &ExpressionStatement) -> Self {
|
||||||
Self {
|
Self {
|
||||||
@ -1967,8 +2000,9 @@ impl ExecutorContext {
|
|||||||
program: &crate::ast::types::Program,
|
program: &crate::ast::types::Program,
|
||||||
memory: Option<ProgramMemory>,
|
memory: Option<ProgramMemory>,
|
||||||
id_generator: IdGenerator,
|
id_generator: IdGenerator,
|
||||||
|
project_directory: Option<String>,
|
||||||
) -> Result<ExecState, KclError> {
|
) -> Result<ExecState, KclError> {
|
||||||
self.run_with_session_data(program, memory, id_generator)
|
self.run_with_session_data(program, memory, id_generator, project_directory)
|
||||||
.await
|
.await
|
||||||
.map(|x| x.0)
|
.map(|x| x.0)
|
||||||
}
|
}
|
||||||
@ -1980,6 +2014,7 @@ impl ExecutorContext {
|
|||||||
program: &crate::ast::types::Program,
|
program: &crate::ast::types::Program,
|
||||||
memory: Option<ProgramMemory>,
|
memory: Option<ProgramMemory>,
|
||||||
id_generator: IdGenerator,
|
id_generator: IdGenerator,
|
||||||
|
project_directory: Option<String>,
|
||||||
) -> Result<(ExecState, Option<ModelingSessionData>), KclError> {
|
) -> Result<(ExecState, Option<ModelingSessionData>), KclError> {
|
||||||
let memory = if let Some(memory) = memory {
|
let memory = if let Some(memory) = memory {
|
||||||
memory.clone()
|
memory.clone()
|
||||||
@ -1989,6 +2024,7 @@ impl ExecutorContext {
|
|||||||
let mut exec_state = ExecState {
|
let mut exec_state = ExecState {
|
||||||
memory,
|
memory,
|
||||||
id_generator,
|
id_generator,
|
||||||
|
project_directory,
|
||||||
..Default::default()
|
..Default::default()
|
||||||
};
|
};
|
||||||
// Before we even start executing the program, set the units.
|
// Before we even start executing the program, set the units.
|
||||||
@ -2027,6 +2063,91 @@ impl ExecutorContext {
|
|||||||
// Iterate over the body of the program.
|
// Iterate over the body of the program.
|
||||||
for statement in &program.body {
|
for statement in &program.body {
|
||||||
match statement {
|
match statement {
|
||||||
|
BodyItem::ImportStatement(import_stmt) => {
|
||||||
|
let source_range = SourceRange::from(import_stmt);
|
||||||
|
let path = import_stmt.path.clone();
|
||||||
|
let resolved_path = if let Some(project_dir) = &exec_state.project_directory {
|
||||||
|
std::path::PathBuf::from(project_dir).join(&path)
|
||||||
|
} else {
|
||||||
|
std::path::PathBuf::from(&path)
|
||||||
|
};
|
||||||
|
if exec_state.import_stack.contains(&resolved_path) {
|
||||||
|
return Err(KclError::ImportCycle(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"circular import of modules is not allowed: {} -> {}",
|
||||||
|
exec_state
|
||||||
|
.import_stack
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.as_path().to_string_lossy())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(" -> "),
|
||||||
|
resolved_path.to_string_lossy()
|
||||||
|
),
|
||||||
|
source_ranges: vec![import_stmt.into()],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
let source = self.fs.read_to_string(&resolved_path, source_range).await?;
|
||||||
|
let program = crate::parser::parse(&source)?;
|
||||||
|
let (module_memory, module_exports) = {
|
||||||
|
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);
|
||||||
|
let result = self
|
||||||
|
.inner_execute(&program, exec_state, crate::executor::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| {
|
||||||
|
if let KclError::ImportCycle(_) = err {
|
||||||
|
// It was an import cycle. Keep the original message.
|
||||||
|
err.override_source_ranges(vec![source_range])
|
||||||
|
} else {
|
||||||
|
KclError::Semantic(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Error loading imported file. Open it to view more details. {path}: {}",
|
||||||
|
err.message()
|
||||||
|
),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})?;
|
||||||
|
|
||||||
|
(module_memory, module_exports)
|
||||||
|
};
|
||||||
|
for import_item in &import_stmt.items {
|
||||||
|
// Extract the item from the module.
|
||||||
|
let item = module_memory
|
||||||
|
.get(&import_item.name.name, import_item.into())
|
||||||
|
.map_err(|_err| {
|
||||||
|
KclError::UndefinedValue(KclErrorDetails {
|
||||||
|
message: format!("{} is not defined in module", import_item.name.name),
|
||||||
|
source_ranges: vec![SourceRange::from(&import_item.name)],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
// Check that the item is allowed to be imported.
|
||||||
|
if !module_exports.contains(&import_item.name.name) {
|
||||||
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
|
message: format!(
|
||||||
|
"Cannot import \"{}\" from module because it is not exported. Add \"export\" before the definition to export it.",
|
||||||
|
import_item.name.name
|
||||||
|
),
|
||||||
|
source_ranges: vec![SourceRange::from(&import_item.name)],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the item to the current module.
|
||||||
|
exec_state.memory.add(
|
||||||
|
import_item.identifier(),
|
||||||
|
item.clone(),
|
||||||
|
SourceRange::from(&import_item.name),
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
last_expr = None;
|
||||||
|
}
|
||||||
BodyItem::ExpressionStatement(expression_statement) => {
|
BodyItem::ExpressionStatement(expression_statement) => {
|
||||||
let metadata = Metadata::from(expression_statement);
|
let metadata = Metadata::from(expression_statement);
|
||||||
last_expr = Some(
|
last_expr = Some(
|
||||||
@ -2053,7 +2174,21 @@ impl ExecutorContext {
|
|||||||
StatementKind::Declaration { name: &var_name },
|
StatementKind::Declaration { name: &var_name },
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
let is_function = memory_item.is_function();
|
||||||
exec_state.memory.add(&var_name, memory_item, source_range)?;
|
exec_state.memory.add(&var_name, memory_item, source_range)?;
|
||||||
|
// Track exports.
|
||||||
|
match variable_declaration.visibility {
|
||||||
|
ItemVisibility::Export => {
|
||||||
|
if !is_function {
|
||||||
|
return Err(KclError::Semantic(KclErrorDetails {
|
||||||
|
message: "Only functions can be exported".to_owned(),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
exec_state.module_exports.insert(var_name);
|
||||||
|
}
|
||||||
|
ItemVisibility::Default => {}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
last_expr = None;
|
last_expr = None;
|
||||||
}
|
}
|
||||||
@ -2158,8 +2293,9 @@ impl ExecutorContext {
|
|||||||
&self,
|
&self,
|
||||||
program: &Program,
|
program: &Program,
|
||||||
id_generator: IdGenerator,
|
id_generator: IdGenerator,
|
||||||
|
project_directory: Option<String>,
|
||||||
) -> Result<TakeSnapshot> {
|
) -> Result<TakeSnapshot> {
|
||||||
let _ = self.run(program, None, id_generator).await?;
|
let _ = self.run(program, None, id_generator, project_directory).await?;
|
||||||
|
|
||||||
// Zoom to fit.
|
// Zoom to fit.
|
||||||
self.engine
|
self.engine
|
||||||
@ -2304,7 +2440,7 @@ mod tests {
|
|||||||
settings: Default::default(),
|
settings: Default::default(),
|
||||||
context_type: ContextType::Mock,
|
context_type: ContextType::Mock,
|
||||||
};
|
};
|
||||||
let exec_state = ctx.run(&program, None, IdGenerator::default()).await?;
|
let exec_state = ctx.run(&program, None, IdGenerator::default(), None).await?;
|
||||||
|
|
||||||
Ok(exec_state.memory)
|
Ok(exec_state.memory)
|
||||||
}
|
}
|
||||||
|
@ -37,6 +37,19 @@ impl FileSystem for FileManager {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn read_to_string<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
|
||||||
|
&self,
|
||||||
|
path: P,
|
||||||
|
source_range: crate::executor::SourceRange,
|
||||||
|
) -> Result<String, KclError> {
|
||||||
|
tokio::fs::read_to_string(&path).await.map_err(|e| {
|
||||||
|
KclError::Engine(KclErrorDetails {
|
||||||
|
message: format!("Failed to read file `{}`: {}", path.as_ref().display(), e),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
async fn exists<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
|
async fn exists<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
|
||||||
&self,
|
&self,
|
||||||
path: P,
|
path: P,
|
||||||
|
@ -23,6 +23,13 @@ pub trait FileSystem: Clone {
|
|||||||
source_range: crate::executor::SourceRange,
|
source_range: crate::executor::SourceRange,
|
||||||
) -> Result<Vec<u8>, crate::errors::KclError>;
|
) -> Result<Vec<u8>, crate::errors::KclError>;
|
||||||
|
|
||||||
|
/// Read a file from the local file system.
|
||||||
|
async fn read_to_string<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
|
||||||
|
&self,
|
||||||
|
path: P,
|
||||||
|
source_range: crate::executor::SourceRange,
|
||||||
|
) -> Result<String, crate::errors::KclError>;
|
||||||
|
|
||||||
/// Check if a file exists on the local file system.
|
/// Check if a file exists on the local file system.
|
||||||
async fn exists<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
|
async fn exists<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
|
||||||
&self,
|
&self,
|
||||||
|
@ -78,6 +78,22 @@ impl FileSystem for FileManager {
|
|||||||
Ok(bytes)
|
Ok(bytes)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn read_to_string<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
|
||||||
|
&self,
|
||||||
|
path: P,
|
||||||
|
source_range: crate::executor::SourceRange,
|
||||||
|
) -> Result<String, KclError> {
|
||||||
|
let bytes = self.read(path, source_range).await?;
|
||||||
|
let string = String::from_utf8(bytes).map_err(|e| {
|
||||||
|
KclError::Engine(KclErrorDetails {
|
||||||
|
message: format!("Failed to convert bytes to string: {:?}", e),
|
||||||
|
source_ranges: vec![source_range],
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(string)
|
||||||
|
}
|
||||||
|
|
||||||
async fn exists<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
|
async fn exists<P: AsRef<std::path::Path> + std::marker::Send + std::marker::Sync>(
|
||||||
&self,
|
&self,
|
||||||
path: P,
|
path: P,
|
||||||
|
@ -596,7 +596,7 @@ impl Backend {
|
|||||||
.clear_scene(&mut id_generator, SourceRange::default())
|
.clear_scene(&mut id_generator, SourceRange::default())
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let exec_state = match executor_ctx.run(ast, None, id_generator).await {
|
let exec_state = match executor_ctx.run(ast, None, id_generator, None).await {
|
||||||
Ok(exec_state) => exec_state,
|
Ok(exec_state) => exec_state,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
self.memory_map.remove(params.uri.as_str());
|
self.memory_map.remove(params.uri.as_str());
|
||||||
|
@ -12,6 +12,13 @@ pub(crate) mod parser_impl;
|
|||||||
pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%";
|
pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%";
|
||||||
pub const PIPE_OPERATOR: &str = "|>";
|
pub const PIPE_OPERATOR: &str = "|>";
|
||||||
|
|
||||||
|
/// Parse the given KCL code into an AST.
|
||||||
|
pub fn parse(code: &str) -> Result<Program, KclError> {
|
||||||
|
let tokens = crate::token::lexer(code)?;
|
||||||
|
let parser = Parser::new(tokens);
|
||||||
|
parser.ast()
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Parser {
|
pub struct Parser {
|
||||||
pub tokens: Vec<Token>,
|
pub tokens: Vec<Token>,
|
||||||
pub unknown_tokens: Vec<Token>,
|
pub unknown_tokens: Vec<Token>,
|
||||||
|
@ -12,10 +12,10 @@ use crate::{
|
|||||||
ast::types::{
|
ast::types::{
|
||||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression,
|
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression,
|
||||||
CommentStyle, ElseIf, Expr, ExpressionStatement, FnArgPrimitive, FnArgType, FunctionExpression, Identifier,
|
CommentStyle, ElseIf, Expr, ExpressionStatement, FnArgPrimitive, FnArgType, FunctionExpression, Identifier,
|
||||||
IfExpression, Literal, LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, NonCodeMeta,
|
IfExpression, ImportItem, ImportStatement, ItemVisibility, Literal, LiteralIdentifier, LiteralValue,
|
||||||
NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty, Parameter, PipeExpression, PipeSubstitution,
|
MemberExpression, MemberObject, NonCodeMeta, NonCodeNode, NonCodeValue, ObjectExpression, ObjectProperty,
|
||||||
Program, ReturnStatement, TagDeclarator, UnaryExpression, UnaryOperator, ValueMeta, VariableDeclaration,
|
Parameter, PipeExpression, PipeSubstitution, Program, ReturnStatement, TagDeclarator, UnaryExpression,
|
||||||
VariableDeclarator, VariableKind,
|
UnaryOperator, ValueMeta, VariableDeclaration, VariableDeclarator, VariableKind,
|
||||||
},
|
},
|
||||||
errors::{KclError, KclErrorDetails},
|
errors::{KclError, KclErrorDetails},
|
||||||
executor::SourceRange,
|
executor::SourceRange,
|
||||||
@ -956,8 +956,10 @@ fn body_items_within_function(i: TokenSlice) -> PResult<WithinFunction> {
|
|||||||
// Any of the body item variants, each of which can optionally be followed by a comment.
|
// Any of the body item variants, each of which can optionally be followed by a comment.
|
||||||
// If there is a comment, it may be preceded by whitespace.
|
// If there is a comment, it may be preceded by whitespace.
|
||||||
let item = dispatch! {peek(any);
|
let item = dispatch! {peek(any);
|
||||||
token if token.declaration_keyword().is_some() =>
|
token if token.declaration_keyword().is_some() || token.visibility_keyword().is_some() =>
|
||||||
(declaration.map(BodyItem::VariableDeclaration), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
(declaration.map(BodyItem::VariableDeclaration), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
||||||
|
token if token.value == "import" && matches!(token.token_type, TokenType::Keyword) =>
|
||||||
|
(import_stmt.map(BodyItem::ImportStatement), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
||||||
Token { ref value, .. } if value == "return" =>
|
Token { ref value, .. } if value == "return" =>
|
||||||
(return_stmt.map(BodyItem::ReturnStatement), opt(noncode_just_after_code)).map(WithinFunction::BodyItem),
|
(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() => {
|
||||||
@ -1122,6 +1124,111 @@ pub fn function_body(i: TokenSlice) -> PResult<Program> {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn import_stmt(i: TokenSlice) -> PResult<Box<ImportStatement>> {
|
||||||
|
let import_token = any
|
||||||
|
.try_map(|token: Token| {
|
||||||
|
if matches!(token.token_type, TokenType::Keyword) && token.value == "import" {
|
||||||
|
Ok(token)
|
||||||
|
} else {
|
||||||
|
Err(KclError::Syntax(KclErrorDetails {
|
||||||
|
source_ranges: token.as_source_ranges(),
|
||||||
|
message: format!("{} is not the 'import' keyword", token.value.as_str()),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.context(expected("the 'import' keyword"))
|
||||||
|
.parse_next(i)?;
|
||||||
|
let start = import_token.start;
|
||||||
|
|
||||||
|
require_whitespace(i)?;
|
||||||
|
|
||||||
|
let items = separated(1.., import_item, comma_sep)
|
||||||
|
.parse_next(i)
|
||||||
|
.map_err(|e| e.cut())?;
|
||||||
|
|
||||||
|
require_whitespace(i)?;
|
||||||
|
|
||||||
|
any.try_map(|token: Token| {
|
||||||
|
if matches!(token.token_type, TokenType::Keyword | TokenType::Word) && token.value == "from" {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(KclError::Syntax(KclErrorDetails {
|
||||||
|
source_ranges: token.as_source_ranges(),
|
||||||
|
message: format!("{} is not the 'from' keyword", token.value.as_str()),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.context(expected("the 'from' keyword"))
|
||||||
|
.parse_next(i)
|
||||||
|
.map_err(|e| e.cut())?;
|
||||||
|
|
||||||
|
require_whitespace(i)?;
|
||||||
|
|
||||||
|
let path = string_literal(i)?;
|
||||||
|
let end = path.end();
|
||||||
|
let path_string = match path.value {
|
||||||
|
LiteralValue::String(s) => s,
|
||||||
|
_ => unreachable!(),
|
||||||
|
};
|
||||||
|
if path_string
|
||||||
|
.chars()
|
||||||
|
.any(|c| !c.is_ascii_alphanumeric() && c != '_' && c != '-' && c != '.')
|
||||||
|
{
|
||||||
|
return Err(ErrMode::Cut(
|
||||||
|
KclError::Syntax(KclErrorDetails {
|
||||||
|
source_ranges: vec![SourceRange::new(path.start, path.end)],
|
||||||
|
message: "import path may only contain alphanumeric characters, underscore, hyphen, and period. Files in other directories are not yet supported.".to_owned(),
|
||||||
|
})
|
||||||
|
.into(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
Ok(Box::new(ImportStatement {
|
||||||
|
items,
|
||||||
|
path: path_string,
|
||||||
|
raw_path: path.raw,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
digest: None,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_item(i: TokenSlice) -> PResult<ImportItem> {
|
||||||
|
let name = identifier.context(expected("an identifier to import")).parse_next(i)?;
|
||||||
|
let start = name.start;
|
||||||
|
let alias = opt(preceded(
|
||||||
|
(whitespace, import_as_keyword, whitespace),
|
||||||
|
identifier.context(expected("an identifier to alias the import")),
|
||||||
|
))
|
||||||
|
.parse_next(i)?;
|
||||||
|
let end = if let Some(ref alias) = alias {
|
||||||
|
alias.end()
|
||||||
|
} else {
|
||||||
|
name.end()
|
||||||
|
};
|
||||||
|
Ok(ImportItem {
|
||||||
|
name,
|
||||||
|
alias,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
digest: None,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn import_as_keyword(i: TokenSlice) -> PResult<Token> {
|
||||||
|
any.try_map(|token: Token| {
|
||||||
|
if matches!(token.token_type, TokenType::Keyword | TokenType::Word) && token.value == "as" {
|
||||||
|
Ok(token)
|
||||||
|
} else {
|
||||||
|
Err(KclError::Syntax(KclErrorDetails {
|
||||||
|
source_ranges: token.as_source_ranges(),
|
||||||
|
message: format!("{} is not the 'as' keyword", token.value.as_str()),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.context(expected("the 'as' keyword"))
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a return statement of a user-defined function, e.g. `return x`.
|
/// Parse a return statement of a user-defined function, e.g. `return x`.
|
||||||
pub fn return_stmt(i: TokenSlice) -> PResult<ReturnStatement> {
|
pub fn return_stmt(i: TokenSlice) -> PResult<ReturnStatement> {
|
||||||
let start = any
|
let start = any
|
||||||
@ -1214,6 +1321,19 @@ fn possible_operands(i: TokenSlice) -> PResult<Expr> {
|
|||||||
.parse_next(i)
|
.parse_next(i)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parse an item visibility specifier, e.g. export.
|
||||||
|
fn item_visibility(i: TokenSlice) -> PResult<(ItemVisibility, Token)> {
|
||||||
|
any.verify_map(|token: Token| {
|
||||||
|
if token.token_type == TokenType::Keyword && token.value == "export" {
|
||||||
|
Some((ItemVisibility::Export, token))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.context(expected("item visibility, e.g. 'export'"))
|
||||||
|
.parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
fn declaration_keyword(i: TokenSlice) -> PResult<(VariableKind, Token)> {
|
fn declaration_keyword(i: TokenSlice) -> PResult<(VariableKind, Token)> {
|
||||||
let res = any
|
let res = any
|
||||||
.verify_map(|token: Token| token.declaration_keyword().map(|kw| (kw, token)))
|
.verify_map(|token: Token| token.declaration_keyword().map(|kw| (kw, token)))
|
||||||
@ -1223,6 +1343,9 @@ fn declaration_keyword(i: TokenSlice) -> PResult<(VariableKind, Token)> {
|
|||||||
|
|
||||||
/// Parse a variable/constant declaration.
|
/// Parse a variable/constant declaration.
|
||||||
fn declaration(i: TokenSlice) -> PResult<VariableDeclaration> {
|
fn declaration(i: TokenSlice) -> PResult<VariableDeclaration> {
|
||||||
|
let (visibility, visibility_token) = opt(terminated(item_visibility, whitespace))
|
||||||
|
.parse_next(i)?
|
||||||
|
.map_or((ItemVisibility::Default, None), |pair| (pair.0, Some(pair.1)));
|
||||||
let decl_token = opt(declaration_keyword).parse_next(i)?;
|
let decl_token = opt(declaration_keyword).parse_next(i)?;
|
||||||
if decl_token.is_some() {
|
if decl_token.is_some() {
|
||||||
// If there was a declaration keyword like `fn`, then it must be followed by some spaces.
|
// If there was a declaration keyword like `fn`, then it must be followed by some spaces.
|
||||||
@ -1235,11 +1358,14 @@ fn declaration(i: TokenSlice) -> PResult<VariableDeclaration> {
|
|||||||
"an identifier, which becomes name you're binding the value to",
|
"an identifier, which becomes name you're binding the value to",
|
||||||
))
|
))
|
||||||
.parse_next(i)?;
|
.parse_next(i)?;
|
||||||
let (kind, start, dec_end) = if let Some((kind, token)) = &decl_token {
|
let (kind, mut start, dec_end) = if let Some((kind, token)) = &decl_token {
|
||||||
(*kind, token.start, token.end)
|
(*kind, token.start, token.end)
|
||||||
} else {
|
} else {
|
||||||
(VariableKind::Const, id.start(), id.end())
|
(VariableKind::Const, id.start(), id.end())
|
||||||
};
|
};
|
||||||
|
if let Some(token) = visibility_token {
|
||||||
|
start = token.start;
|
||||||
|
}
|
||||||
|
|
||||||
ignore_whitespace(i);
|
ignore_whitespace(i);
|
||||||
equals(i)?;
|
equals(i)?;
|
||||||
@ -1288,6 +1414,7 @@ fn declaration(i: TokenSlice) -> PResult<VariableDeclaration> {
|
|||||||
init: val,
|
init: val,
|
||||||
digest: None,
|
digest: None,
|
||||||
}],
|
}],
|
||||||
|
visibility,
|
||||||
kind,
|
kind,
|
||||||
digest: None,
|
digest: None,
|
||||||
})
|
})
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parser/parser_impl.rs
|
||||||
assertion_line: 3423
|
|
||||||
expression: actual
|
expression: actual
|
||||||
---
|
---
|
||||||
{
|
{
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
---
|
---
|
||||||
source: kcl/src/parser/parser_impl.rs
|
source: kcl/src/parser/parser_impl.rs
|
||||||
assertion_line: 3470
|
|
||||||
expression: actual
|
expression: actual
|
||||||
---
|
---
|
||||||
{
|
{
|
||||||
|
@ -30,7 +30,7 @@ async fn do_execute_and_snapshot(ctx: &ExecutorContext, code: &str) -> anyhow::R
|
|||||||
let program = parser.ast()?;
|
let program = parser.ast()?;
|
||||||
|
|
||||||
let snapshot = ctx
|
let snapshot = ctx
|
||||||
.execute_and_prepare_snapshot(&program, IdGenerator::default())
|
.execute_and_prepare_snapshot(&program, IdGenerator::default(), None)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
// Create a temporary file to write the output to.
|
// Create a temporary file to write the output to.
|
||||||
|
@ -7,7 +7,11 @@ use serde::{Deserialize, Serialize};
|
|||||||
use tower_lsp::lsp_types::SemanticTokenType;
|
use tower_lsp::lsp_types::SemanticTokenType;
|
||||||
use winnow::stream::ContainsToken;
|
use winnow::stream::ContainsToken;
|
||||||
|
|
||||||
use crate::{ast::types::VariableKind, errors::KclError, executor::SourceRange};
|
use crate::{
|
||||||
|
ast::types::{ItemVisibility, VariableKind},
|
||||||
|
errors::KclError,
|
||||||
|
executor::SourceRange,
|
||||||
|
};
|
||||||
|
|
||||||
mod tokeniser;
|
mod tokeniser;
|
||||||
|
|
||||||
@ -196,6 +200,16 @@ impl Token {
|
|||||||
vec![self.as_source_range()]
|
vec![self.as_source_range()]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn visibility_keyword(&self) -> Option<ItemVisibility> {
|
||||||
|
if !matches!(self.token_type, TokenType::Keyword) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
match self.value.as_str() {
|
||||||
|
"export" => Some(ItemVisibility::Export),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Is this token the beginning of a variable/function declaration?
|
/// Is this token the beginning of a variable/function declaration?
|
||||||
/// If so, what kind?
|
/// If so, what kind?
|
||||||
/// If not, returns None.
|
/// If not, returns None.
|
||||||
|
@ -169,11 +169,17 @@ fn string(i: &mut Located<&str>) -> PResult<Token> {
|
|||||||
Ok(Token::from_range(range, TokenType::String, value.to_string()))
|
Ok(Token::from_range(range, TokenType::String, value.to_string()))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn keyword(i: &mut Located<&str>) -> PResult<Token> {
|
fn import_keyword(i: &mut Located<&str>) -> PResult<Token> {
|
||||||
|
let (value, range) = "import".with_span().parse_next(i)?;
|
||||||
|
let token_type = peek(alt((' '.map(|_| TokenType::Keyword), '('.map(|_| TokenType::Word)))).parse_next(i)?;
|
||||||
|
Ok(Token::from_range(range, token_type, value.to_owned()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unambiguous_keywords(i: &mut Located<&str>) -> PResult<Token> {
|
||||||
// These are the keywords themselves.
|
// These are the keywords themselves.
|
||||||
let keyword_candidates = alt((
|
let keyword_candidates = alt((
|
||||||
"if", "else", "for", "while", "return", "break", "continue", "fn", "let", "mut", "loop", "true", "false",
|
"if", "else", "for", "while", "return", "break", "continue", "fn", "let", "mut", "loop", "true", "false",
|
||||||
"nil", "and", "or", "not", "var", "const",
|
"nil", "and", "or", "not", "var", "const", "export",
|
||||||
));
|
));
|
||||||
// Look ahead. If any of these characters follow the keyword, then it's not a keyword, it's just
|
// Look ahead. If any of these characters follow the keyword, then it's not a keyword, it's just
|
||||||
// the start of a normal word.
|
// the start of a normal word.
|
||||||
@ -185,6 +191,10 @@ fn keyword(i: &mut Located<&str>) -> PResult<Token> {
|
|||||||
Ok(Token::from_range(range, TokenType::Keyword, value.to_owned()))
|
Ok(Token::from_range(range, TokenType::Keyword, value.to_owned()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn keyword(i: &mut Located<&str>) -> PResult<Token> {
|
||||||
|
alt((import_keyword, unambiguous_keywords)).parse_next(i)
|
||||||
|
}
|
||||||
|
|
||||||
fn type_(i: &mut Located<&str>) -> PResult<Token> {
|
fn type_(i: &mut Located<&str>) -> PResult<Token> {
|
||||||
// These are the types themselves.
|
// These are the types themselves.
|
||||||
let type_candidates = alt(("string", "number", "bool", "sketch", "sketch_surface", "solid"));
|
let type_candidates = alt(("string", "number", "bool", "sketch", "sketch_surface", "solid"));
|
||||||
@ -1572,4 +1582,28 @@ const things = "things"
|
|||||||
|
|
||||||
assert_tokens(expected, actual);
|
assert_tokens(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_keyword() {
|
||||||
|
let actual = lexer("import foo").unwrap();
|
||||||
|
let expected = Token {
|
||||||
|
token_type: TokenType::Keyword,
|
||||||
|
value: "import".to_owned(),
|
||||||
|
start: 0,
|
||||||
|
end: 6,
|
||||||
|
};
|
||||||
|
assert_eq!(actual[0], expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn import_function() {
|
||||||
|
let actual = lexer("import(3)").unwrap();
|
||||||
|
let expected = Token {
|
||||||
|
token_type: TokenType::Word,
|
||||||
|
value: "import".to_owned(),
|
||||||
|
start: 0,
|
||||||
|
end: 6,
|
||||||
|
};
|
||||||
|
assert_eq!(actual[0], expected);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ use std::fmt::Write;
|
|||||||
use crate::{
|
use crate::{
|
||||||
ast::types::{
|
ast::types::{
|
||||||
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression,
|
ArrayExpression, ArrayRangeExpression, BinaryExpression, BinaryOperator, BinaryPart, BodyItem, CallExpression,
|
||||||
Expr, FormatOptions, FunctionExpression, IfExpression, Literal, LiteralIdentifier, LiteralValue,
|
Expr, FormatOptions, FunctionExpression, IfExpression, ImportStatement, ItemVisibility, Literal,
|
||||||
MemberExpression, MemberObject, NonCodeValue, ObjectExpression, PipeExpression, Program, TagDeclarator,
|
LiteralIdentifier, LiteralValue, MemberExpression, MemberObject, NonCodeValue, ObjectExpression,
|
||||||
UnaryExpression, VariableDeclaration, VariableKind,
|
PipeExpression, Program, TagDeclarator, UnaryExpression, VariableDeclaration, VariableKind,
|
||||||
},
|
},
|
||||||
parser::PIPE_OPERATOR,
|
parser::PIPE_OPERATOR,
|
||||||
};
|
};
|
||||||
@ -17,6 +17,7 @@ impl Program {
|
|||||||
.body
|
.body
|
||||||
.iter()
|
.iter()
|
||||||
.map(|statement| match statement.clone() {
|
.map(|statement| match statement.clone() {
|
||||||
|
BodyItem::ImportStatement(stmt) => stmt.recast(options, indentation_level),
|
||||||
BodyItem::ExpressionStatement(expression_statement) => {
|
BodyItem::ExpressionStatement(expression_statement) => {
|
||||||
expression_statement
|
expression_statement
|
||||||
.expression
|
.expression
|
||||||
@ -108,6 +109,27 @@ impl NonCodeValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl ImportStatement {
|
||||||
|
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||||
|
let indentation = options.get_indentation(indentation_level);
|
||||||
|
let mut string = format!("{}import ", indentation);
|
||||||
|
for (i, item) in self.items.iter().enumerate() {
|
||||||
|
if i > 0 {
|
||||||
|
string.push_str(", ");
|
||||||
|
}
|
||||||
|
string.push_str(&item.name.name);
|
||||||
|
if let Some(alias) = &item.alias {
|
||||||
|
// If the alias is the same, don't output it.
|
||||||
|
if item.name.name != alias.name {
|
||||||
|
string.push_str(&format!(" as {}", alias.name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
string.push_str(&format!(" from {}", self.raw_path));
|
||||||
|
string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Expr {
|
impl Expr {
|
||||||
pub(crate) fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
|
pub(crate) fn recast(&self, options: &FormatOptions, indentation_level: usize, is_in_pipe: bool) -> String {
|
||||||
match &self {
|
match &self {
|
||||||
@ -168,7 +190,11 @@ impl CallExpression {
|
|||||||
impl VariableDeclaration {
|
impl VariableDeclaration {
|
||||||
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
pub fn recast(&self, options: &FormatOptions, indentation_level: usize) -> String {
|
||||||
let indentation = options.get_indentation(indentation_level);
|
let indentation = options.get_indentation(indentation_level);
|
||||||
self.declarations.iter().fold(String::new(), |mut output, declaration| {
|
let output = match self.visibility {
|
||||||
|
ItemVisibility::Default => String::new(),
|
||||||
|
ItemVisibility::Export => "export ".to_owned(),
|
||||||
|
};
|
||||||
|
self.declarations.iter().fold(output, |mut output, declaration| {
|
||||||
let keyword = match self.kind {
|
let keyword = match self.kind {
|
||||||
VariableKind::Fn => "fn ",
|
VariableKind::Fn => "fn ",
|
||||||
VariableKind::Const => "",
|
VariableKind::Const => "",
|
||||||
@ -581,6 +607,46 @@ mod tests {
|
|||||||
assert_eq!(output, input);
|
assert_eq!(output, input);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recast_import() {
|
||||||
|
let input = r#"import a from "a.kcl"
|
||||||
|
import a as aaa from "a.kcl"
|
||||||
|
import a, b from "a.kcl"
|
||||||
|
import a as aaa, b from "a.kcl"
|
||||||
|
import a, b as bbb from "a.kcl"
|
||||||
|
import a as aaa, b as bbb from "a.kcl"
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(input).unwrap();
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast().unwrap();
|
||||||
|
let output = program.recast(&Default::default(), 0);
|
||||||
|
assert_eq!(output, input);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recast_import_as_same_name() {
|
||||||
|
let input = r#"import a as a from "a.kcl"
|
||||||
|
"#;
|
||||||
|
let program = crate::parser::parse(input).unwrap();
|
||||||
|
let output = program.recast(&Default::default(), 0);
|
||||||
|
let expected = r#"import a from "a.kcl"
|
||||||
|
"#;
|
||||||
|
assert_eq!(output, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_recast_export_fn() {
|
||||||
|
let input = r#"export fn a = () => {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
"#;
|
||||||
|
let tokens = crate::token::lexer(input).unwrap();
|
||||||
|
let parser = crate::parser::Parser::new(tokens);
|
||||||
|
let program = parser.ast().unwrap();
|
||||||
|
let output = program.recast(&Default::default(), 0);
|
||||||
|
assert_eq!(output, input);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_recast_bug_fn_in_fn() {
|
fn test_recast_bug_fn_in_fn() {
|
||||||
let some_program_string = r#"// Start point (top left)
|
let some_program_string = r#"// Start point (top left)
|
||||||
|
@ -9,6 +9,7 @@ use crate::{
|
|||||||
pub enum Node<'a> {
|
pub enum Node<'a> {
|
||||||
Program(&'a types::Program),
|
Program(&'a types::Program),
|
||||||
|
|
||||||
|
ImportStatement(&'a types::ImportStatement),
|
||||||
ExpressionStatement(&'a types::ExpressionStatement),
|
ExpressionStatement(&'a types::ExpressionStatement),
|
||||||
VariableDeclaration(&'a types::VariableDeclaration),
|
VariableDeclaration(&'a types::VariableDeclaration),
|
||||||
ReturnStatement(&'a types::ReturnStatement),
|
ReturnStatement(&'a types::ReturnStatement),
|
||||||
@ -42,6 +43,7 @@ impl From<&Node<'_>> for SourceRange {
|
|||||||
fn from(node: &Node) -> Self {
|
fn from(node: &Node) -> Self {
|
||||||
match node {
|
match node {
|
||||||
Node::Program(p) => SourceRange([p.start, p.end]),
|
Node::Program(p) => SourceRange([p.start, p.end]),
|
||||||
|
Node::ImportStatement(e) => SourceRange([e.start(), e.end()]),
|
||||||
Node::ExpressionStatement(e) => SourceRange([e.start(), e.end()]),
|
Node::ExpressionStatement(e) => SourceRange([e.start(), e.end()]),
|
||||||
Node::VariableDeclaration(v) => SourceRange([v.start(), v.end()]),
|
Node::VariableDeclaration(v) => SourceRange([v.start(), v.end()]),
|
||||||
Node::ReturnStatement(r) => SourceRange([r.start(), r.end()]),
|
Node::ReturnStatement(r) => SourceRange([r.start(), r.end()]),
|
||||||
@ -79,6 +81,7 @@ macro_rules! impl_from {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl_from!(Node, Program);
|
impl_from!(Node, Program);
|
||||||
|
impl_from!(Node, ImportStatement);
|
||||||
impl_from!(Node, ExpressionStatement);
|
impl_from!(Node, ExpressionStatement);
|
||||||
impl_from!(Node, VariableDeclaration);
|
impl_from!(Node, VariableDeclaration);
|
||||||
impl_from!(Node, ReturnStatement);
|
impl_from!(Node, ReturnStatement);
|
||||||
|
@ -277,6 +277,12 @@ where
|
|||||||
// We don't walk a BodyItem since it's an enum itself.
|
// We don't walk a BodyItem since it's an enum itself.
|
||||||
|
|
||||||
match node {
|
match node {
|
||||||
|
BodyItem::ImportStatement(xs) => {
|
||||||
|
if !f.walk(xs.as_ref().into())? {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
BodyItem::ExpressionStatement(xs) => {
|
BodyItem::ExpressionStatement(xs) => {
|
||||||
if !f.walk(xs.into())? {
|
if !f.walk(xs.into())? {
|
||||||
return Ok(false);
|
return Ok(false);
|
||||||
|
@ -20,6 +20,7 @@ pub async fn execute_wasm(
|
|||||||
units: &str,
|
units: &str,
|
||||||
engine_manager: kcl_lib::engine::conn_wasm::EngineCommandManager,
|
engine_manager: kcl_lib::engine::conn_wasm::EngineCommandManager,
|
||||||
fs_manager: kcl_lib::fs::wasm::FileSystemManager,
|
fs_manager: kcl_lib::fs::wasm::FileSystemManager,
|
||||||
|
project_directory: Option<String>,
|
||||||
is_mock: bool,
|
is_mock: bool,
|
||||||
) -> Result<JsValue, String> {
|
) -> Result<JsValue, String> {
|
||||||
console_error_panic_hook::set_once();
|
console_error_panic_hook::set_once();
|
||||||
@ -62,7 +63,7 @@ pub async fn execute_wasm(
|
|||||||
};
|
};
|
||||||
|
|
||||||
let exec_state = ctx
|
let exec_state = ctx
|
||||||
.run(&program, Some(memory), id_generator)
|
.run(&program, Some(memory), id_generator, project_directory)
|
||||||
.await
|
.await
|
||||||
.map_err(String::from)?;
|
.map_err(String::from)?;
|
||||||
|
|
||||||
|
@ -0,0 +1 @@
|
|||||||
|
export three = 3
|
@ -0,0 +1,5 @@
|
|||||||
|
export fn foo = () => { return 0 }
|
||||||
|
|
||||||
|
// This interacts with the engine.
|
||||||
|
part001 = startSketchOn('XY')
|
||||||
|
|> startProfileAt([0, 0], %)
|
@ -0,0 +1,3 @@
|
|||||||
|
export fn identity = (x) => {
|
||||||
|
return x
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
import three from "export_constant.kcl"
|
@ -0,0 +1,3 @@
|
|||||||
|
import two from "import_cycle2.kcl"
|
||||||
|
|
||||||
|
export fn one = () => { return two() - 1 }
|
@ -0,0 +1,3 @@
|
|||||||
|
import three from "import_cycle3.kcl"
|
||||||
|
|
||||||
|
export fn two = () => { return three() - 1 }
|
@ -0,0 +1,3 @@
|
|||||||
|
import one from "import_cycle1.kcl"
|
||||||
|
|
||||||
|
export fn three = () => { return one() + one() + one() }
|
@ -0,0 +1 @@
|
|||||||
|
import cube from "../cube.kcl"
|
@ -0,0 +1,4 @@
|
|||||||
|
fn foo = () => {
|
||||||
|
import identity from "identity.kcl"
|
||||||
|
return 1
|
||||||
|
}
|
@ -0,0 +1,6 @@
|
|||||||
|
if true {
|
||||||
|
import identity from "identity.kcl"
|
||||||
|
1
|
||||||
|
} else {
|
||||||
|
2
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
import foo from "export_side_effect.kcl"
|
@ -0,0 +1,25 @@
|
|||||||
|
import identity from "identity.kcl"
|
||||||
|
|
||||||
|
answer = identity(42)
|
||||||
|
assertEqual(answer, 42, 0.0001, "identity")
|
||||||
|
|
||||||
|
import identity as id from "identity.kcl"
|
||||||
|
|
||||||
|
answer43 = id(43)
|
||||||
|
assertEqual(answer43, 43, 0.0001, "identity")
|
||||||
|
|
||||||
|
import increment, decrement from "numbers.kcl"
|
||||||
|
|
||||||
|
answer3 = increment(2)
|
||||||
|
assertEqual(answer3, 3, 0.0001, "increment")
|
||||||
|
|
||||||
|
answer5 = decrement(6)
|
||||||
|
assertEqual(answer5, 5, 0.0001, "decrement")
|
||||||
|
|
||||||
|
import increment as inc, decrement as dec from "numbers.kcl"
|
||||||
|
|
||||||
|
answer4 = inc(3)
|
||||||
|
assertEqual(answer4, 4, 0.0001, "inc")
|
||||||
|
|
||||||
|
answer6 = dec(7)
|
||||||
|
assertEqual(answer6, 6, 0.0001, "dec")
|
@ -0,0 +1,7 @@
|
|||||||
|
export fn increment = (x) => {
|
||||||
|
return x + 1
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn decrement = (x) => {
|
||||||
|
return x - 1
|
||||||
|
}
|
@ -2,6 +2,7 @@ use kcl_lib::{
|
|||||||
ast::types::Program,
|
ast::types::Program,
|
||||||
errors::KclError,
|
errors::KclError,
|
||||||
executor::{ExecutorContext, IdGenerator},
|
executor::{ExecutorContext, IdGenerator},
|
||||||
|
parser,
|
||||||
};
|
};
|
||||||
|
|
||||||
macro_rules! gen_test {
|
macro_rules! gen_test {
|
||||||
@ -25,10 +26,28 @@ macro_rules! gen_test_fail {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! gen_test_parse_fail {
|
||||||
|
($file:ident, $expected:literal) => {
|
||||||
|
#[tokio::test]
|
||||||
|
async fn $file() {
|
||||||
|
let code = include_str!(concat!("inputs/no_visuals/", stringify!($file), ".kcl"));
|
||||||
|
let actual = run_parse_fail(&code).await;
|
||||||
|
assert_eq!(actual.get_message(), $expected);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async fn run(code: &str) {
|
async fn run(code: &str) {
|
||||||
let (ctx, program, id_generator) = setup(code).await;
|
let (ctx, program, id_generator) = setup(code).await;
|
||||||
|
|
||||||
let res = ctx.run(&program, None, id_generator).await;
|
let res = ctx
|
||||||
|
.run(
|
||||||
|
&program,
|
||||||
|
None,
|
||||||
|
id_generator,
|
||||||
|
Some("tests/executor/inputs/no_visuals/".to_owned()),
|
||||||
|
)
|
||||||
|
.await;
|
||||||
match res {
|
match res {
|
||||||
Ok(state) => {
|
Ok(state) => {
|
||||||
println!("{:#?}", state.memory);
|
println!("{:#?}", state.memory);
|
||||||
@ -57,12 +76,27 @@ async fn setup(program: &str) -> (ExecutorContext, Program, IdGenerator) {
|
|||||||
|
|
||||||
async fn run_fail(code: &str) -> KclError {
|
async fn run_fail(code: &str) -> KclError {
|
||||||
let (ctx, program, id_generator) = setup(code).await;
|
let (ctx, program, id_generator) = setup(code).await;
|
||||||
let Err(e) = ctx.run(&program, None, id_generator).await else {
|
let Err(e) = ctx
|
||||||
|
.run(
|
||||||
|
&program,
|
||||||
|
None,
|
||||||
|
id_generator,
|
||||||
|
Some("tests/executor/inputs/no_visuals/".to_owned()),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
else {
|
||||||
panic!("Expected this KCL program to fail, but it (incorrectly) never threw an error.");
|
panic!("Expected this KCL program to fail, but it (incorrectly) never threw an error.");
|
||||||
};
|
};
|
||||||
e
|
e
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn run_parse_fail(code: &str) -> KclError {
|
||||||
|
let Err(e) = parser::parse(code) else {
|
||||||
|
panic!("Expected this KCL program to fail to parse, but it (incorrectly) never threw an error.");
|
||||||
|
};
|
||||||
|
e
|
||||||
|
}
|
||||||
|
|
||||||
gen_test!(property_of_object);
|
gen_test!(property_of_object);
|
||||||
gen_test!(index_of_array);
|
gen_test!(index_of_array);
|
||||||
gen_test!(comparisons);
|
gen_test!(comparisons);
|
||||||
@ -111,5 +145,31 @@ gen_test!(if_else);
|
|||||||
// "syntax: blocks inside an if/else expression must end in an expression"
|
// "syntax: blocks inside an if/else expression must end in an expression"
|
||||||
// );
|
// );
|
||||||
gen_test_fail!(comparisons_multiple, "syntax: Invalid number: true");
|
gen_test_fail!(comparisons_multiple, "syntax: Invalid number: true");
|
||||||
|
gen_test!(import_simple);
|
||||||
|
gen_test_fail!(
|
||||||
|
import_cycle1,
|
||||||
|
"import cycle: circular import of modules is not allowed: tests/executor/inputs/no_visuals/import_cycle2.kcl -> tests/executor/inputs/no_visuals/import_cycle3.kcl -> tests/executor/inputs/no_visuals/import_cycle1.kcl -> tests/executor/inputs/no_visuals/import_cycle2.kcl"
|
||||||
|
);
|
||||||
|
gen_test_fail!(
|
||||||
|
import_constant,
|
||||||
|
"semantic: Error loading imported file. Open it to view more details. export_constant.kcl: Only functions can be exported"
|
||||||
|
);
|
||||||
|
gen_test_fail!(
|
||||||
|
import_side_effect,
|
||||||
|
"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."
|
||||||
|
);
|
||||||
|
gen_test_parse_fail!(
|
||||||
|
import_from_other_directory,
|
||||||
|
"syntax: import path may only contain alphanumeric characters, underscore, hyphen, and period. Files in other directories are not yet supported."
|
||||||
|
);
|
||||||
|
// TODO: We'd like these tests.
|
||||||
|
// gen_test_fail!(
|
||||||
|
// import_in_if,
|
||||||
|
// "syntax: Can import only import at the top level"
|
||||||
|
// );
|
||||||
|
// gen_test_fail!(
|
||||||
|
// import_in_function,
|
||||||
|
// "syntax: Can import only import at the top level"
|
||||||
|
// );
|
||||||
gen_test!(add_lots);
|
gen_test!(add_lots);
|
||||||
gen_test!(double_map);
|
gen_test!(double_map);
|
||||||
|
@ -35,7 +35,7 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Program, uuid
|
|||||||
let parser = kcl_lib::parser::Parser::new(tokens);
|
let parser = kcl_lib::parser::Parser::new(tokens);
|
||||||
let program = parser.ast()?;
|
let program = parser.ast()?;
|
||||||
let ctx = kcl_lib::executor::ExecutorContext::new(&client, Default::default()).await?;
|
let ctx = kcl_lib::executor::ExecutorContext::new(&client, Default::default()).await?;
|
||||||
let exec_state = ctx.run(&program, None, IdGenerator::default()).await?;
|
let exec_state = ctx.run(&program, None, IdGenerator::default(), None).await?;
|
||||||
|
|
||||||
// We need to get the sketch ID.
|
// We need to get the sketch ID.
|
||||||
// Get the sketch ID from memory.
|
// Get the sketch ID from memory.
|
||||||
|
Reference in New Issue
Block a user