2025-04-01 23:54:26 -07:00
|
|
|
import type {
|
|
|
|
|
ArtifactCommand,
|
|
|
|
|
ArtifactId,
|
|
|
|
|
ArtifactGraph as RustArtifactGraph,
|
|
|
|
|
} from '@rust/kcl-lib/bindings/Artifact'
|
|
|
|
|
import type { CompilationError } from '@rust/kcl-lib/bindings/CompilationError'
|
|
|
|
|
import type { Configuration } from '@rust/kcl-lib/bindings/Configuration'
|
|
|
|
|
import type { CoreDumpInfo } from '@rust/kcl-lib/bindings/CoreDumpInfo'
|
|
|
|
|
import type { DefaultPlanes } from '@rust/kcl-lib/bindings/DefaultPlanes'
|
|
|
|
|
import type { Discovered } from '@rust/kcl-lib/bindings/Discovered'
|
|
|
|
|
import type { ExecOutcome as RustExecOutcome } from '@rust/kcl-lib/bindings/ExecOutcome'
|
|
|
|
|
import type { KclError as RustKclError } from '@rust/kcl-lib/bindings/KclError'
|
|
|
|
|
import type { KclErrorWithOutputs } from '@rust/kcl-lib/bindings/KclErrorWithOutputs'
|
|
|
|
|
import type { KclValue } from '@rust/kcl-lib/bindings/KclValue'
|
|
|
|
|
import type { MetaSettings } from '@rust/kcl-lib/bindings/MetaSettings'
|
|
|
|
|
import type { UnitAngle, UnitLength } from '@rust/kcl-lib/bindings/ModelingCmd'
|
|
|
|
|
import type { ModulePath } from '@rust/kcl-lib/bindings/ModulePath'
|
|
|
|
|
import type { Node } from '@rust/kcl-lib/bindings/Node'
|
|
|
|
|
import type { NumericSuffix } from '@rust/kcl-lib/bindings/NumericSuffix'
|
|
|
|
|
import type { Operation } from '@rust/kcl-lib/bindings/Operation'
|
|
|
|
|
import type { Program } from '@rust/kcl-lib/bindings/Program'
|
|
|
|
|
import type { ProjectConfiguration } from '@rust/kcl-lib/bindings/ProjectConfiguration'
|
|
|
|
|
import type { Sketch } from '@rust/kcl-lib/bindings/Sketch'
|
|
|
|
|
import type { SourceRange } from '@rust/kcl-lib/bindings/SourceRange'
|
|
|
|
|
import type { UnitAngle as UnitAng } from '@rust/kcl-lib/bindings/UnitAngle'
|
|
|
|
|
import type { UnitLen } from '@rust/kcl-lib/bindings/UnitLen'
|
|
|
|
|
|
|
|
|
|
import { KCLError } from '@src/lang/errors'
|
|
|
|
|
import { getNodePathFromSourceRange } from '@src/lang/queryAstNodePathUtils'
|
2025-04-01 14:20:42 -07:00
|
|
|
import {
|
2025-04-01 23:54:26 -07:00
|
|
|
type Artifact,
|
2025-04-16 00:11:25 +10:00
|
|
|
defaultArtifactGraph,
|
2025-04-01 23:54:26 -07:00
|
|
|
} from '@src/lang/std/artifactGraph'
|
|
|
|
|
import type { Coords2d } from '@src/lang/std/sketch'
|
|
|
|
|
import {
|
|
|
|
|
DEFAULT_DEFAULT_ANGLE_UNIT,
|
|
|
|
|
DEFAULT_DEFAULT_LENGTH_UNIT,
|
|
|
|
|
} from '@src/lib/constants'
|
|
|
|
|
import type { CoreDumpManager } from '@src/lib/coredump'
|
|
|
|
|
import openWindow from '@src/lib/openWindow'
|
|
|
|
|
import { Reason, err } from '@src/lib/trap'
|
|
|
|
|
import type { DeepPartial } from '@src/lib/types'
|
|
|
|
|
import { isArray } from '@src/lib/utils'
|
|
|
|
|
import {
|
|
|
|
|
base64_decode,
|
|
|
|
|
change_kcl_settings,
|
2025-04-01 15:31:19 -07:00
|
|
|
coredump,
|
|
|
|
|
default_app_settings,
|
|
|
|
|
default_project_settings,
|
2025-04-01 23:54:26 -07:00
|
|
|
format_number,
|
|
|
|
|
get_kcl_version,
|
|
|
|
|
get_tangential_arc_to_info,
|
|
|
|
|
is_kcl_empty_or_only_settings,
|
|
|
|
|
is_points_ccw,
|
|
|
|
|
kcl_lint,
|
2025-04-01 15:31:19 -07:00
|
|
|
kcl_settings,
|
2025-04-01 23:54:26 -07:00
|
|
|
parse_app_settings,
|
|
|
|
|
parse_project_settings,
|
|
|
|
|
parse_wasm,
|
|
|
|
|
recast_wasm,
|
|
|
|
|
serialize_configuration,
|
|
|
|
|
serialize_project_configuration,
|
|
|
|
|
} from '@src/lib/wasm_lib_wrapper'
|
2025-01-08 10:58:41 -05:00
|
|
|
|
2025-04-01 23:54:26 -07:00
|
|
|
export type { ArrayExpression } from '@rust/kcl-lib/bindings/ArrayExpression'
|
|
|
|
|
export type {
|
|
|
|
|
Artifact,
|
|
|
|
|
ArtifactCommand,
|
|
|
|
|
ArtifactId,
|
|
|
|
|
Cap as CapArtifact,
|
|
|
|
|
CodeRef,
|
2025-04-11 01:16:29 -04:00
|
|
|
CompositeSolid as CompositeSolidArtifact,
|
2025-04-01 23:54:26 -07:00
|
|
|
EdgeCut,
|
|
|
|
|
Path as PathArtifact,
|
|
|
|
|
Plane as PlaneArtifact,
|
|
|
|
|
Segment as SegmentArtifact,
|
|
|
|
|
Solid2d as Solid2dArtifact,
|
|
|
|
|
Sweep as SweepArtifact,
|
|
|
|
|
SweepEdge,
|
|
|
|
|
Wall as WallArtifact,
|
|
|
|
|
} from '@rust/kcl-lib/bindings/Artifact'
|
|
|
|
|
export type { BinaryExpression } from '@rust/kcl-lib/bindings/BinaryExpression'
|
|
|
|
|
export type { BinaryPart } from '@rust/kcl-lib/bindings/BinaryPart'
|
|
|
|
|
export type { CallExpression } from '@rust/kcl-lib/bindings/CallExpression'
|
|
|
|
|
export type { CallExpressionKw } from '@rust/kcl-lib/bindings/CallExpressionKw'
|
2025-03-01 13:59:01 -08:00
|
|
|
export type { Configuration } from '@rust/kcl-lib/bindings/Configuration'
|
|
|
|
|
export type { Expr } from '@rust/kcl-lib/bindings/Expr'
|
2025-04-01 23:54:26 -07:00
|
|
|
export type { ExpressionStatement } from '@rust/kcl-lib/bindings/ExpressionStatement'
|
|
|
|
|
export type { Identifier } from '@rust/kcl-lib/bindings/Identifier'
|
|
|
|
|
export type { LabeledArg } from '@rust/kcl-lib/bindings/LabeledArg'
|
|
|
|
|
export type { Literal } from '@rust/kcl-lib/bindings/Literal'
|
|
|
|
|
export type { LiteralValue } from '@rust/kcl-lib/bindings/LiteralValue'
|
|
|
|
|
export type { MemberExpression } from '@rust/kcl-lib/bindings/MemberExpression'
|
|
|
|
|
export type { Name } from '@rust/kcl-lib/bindings/Name'
|
|
|
|
|
export type { NumericSuffix } from '@rust/kcl-lib/bindings/NumericSuffix'
|
2025-03-01 13:59:01 -08:00
|
|
|
export type { ObjectExpression } from '@rust/kcl-lib/bindings/ObjectExpression'
|
|
|
|
|
export type { ObjectProperty } from '@rust/kcl-lib/bindings/ObjectProperty'
|
|
|
|
|
export type { Parameter } from '@rust/kcl-lib/bindings/Parameter'
|
2025-04-01 23:54:26 -07:00
|
|
|
export type { PipeExpression } from '@rust/kcl-lib/bindings/PipeExpression'
|
2025-03-01 13:59:01 -08:00
|
|
|
export type { PipeSubstitution } from '@rust/kcl-lib/bindings/PipeSubstitution'
|
2025-04-01 23:54:26 -07:00
|
|
|
export type { Program } from '@rust/kcl-lib/bindings/Program'
|
2025-03-01 13:59:01 -08:00
|
|
|
export type { ReturnStatement } from '@rust/kcl-lib/bindings/ReturnStatement'
|
|
|
|
|
export type { SourceRange } from '@rust/kcl-lib/bindings/SourceRange'
|
2025-04-01 23:54:26 -07:00
|
|
|
export type { UnaryExpression } from '@rust/kcl-lib/bindings/UnaryExpression'
|
|
|
|
|
export type { VariableDeclaration } from '@rust/kcl-lib/bindings/VariableDeclaration'
|
|
|
|
|
export type { VariableDeclarator } from '@rust/kcl-lib/bindings/VariableDeclarator'
|
2023-09-29 11:11:01 -07:00
|
|
|
|
|
|
|
|
export type SyntaxType =
|
|
|
|
|
| 'Program'
|
|
|
|
|
| 'ExpressionStatement'
|
|
|
|
|
| 'BinaryExpression'
|
|
|
|
|
| 'CallExpression'
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
| 'CallExpressionKw'
|
2025-03-24 20:58:55 +13:00
|
|
|
| 'Name'
|
2023-09-29 11:11:01 -07:00
|
|
|
| 'ReturnStatement'
|
|
|
|
|
| 'VariableDeclaration'
|
|
|
|
|
| 'VariableDeclarator'
|
|
|
|
|
| 'MemberExpression'
|
|
|
|
|
| 'ArrayExpression'
|
|
|
|
|
| 'ObjectExpression'
|
|
|
|
|
| 'ObjectProperty'
|
|
|
|
|
| 'FunctionExpression'
|
|
|
|
|
| 'PipeExpression'
|
|
|
|
|
| 'PipeSubstitution'
|
|
|
|
|
| 'Literal'
|
2024-11-18 10:04:09 -05:00
|
|
|
| 'LiteralValue'
|
2023-09-29 11:11:01 -07:00
|
|
|
| 'NonCodeNode'
|
|
|
|
|
| 'UnaryExpression'
|
2025-04-30 13:07:39 -04:00
|
|
|
| 'ImportStatement'
|
2023-09-29 11:11:01 -07:00
|
|
|
|
2025-04-01 23:54:26 -07:00
|
|
|
export type { ExtrudeSurface } from '@rust/kcl-lib/bindings/ExtrudeSurface'
|
|
|
|
|
export type { KclValue } from '@rust/kcl-lib/bindings/KclValue'
|
2025-03-01 13:59:01 -08:00
|
|
|
export type { Path } from '@rust/kcl-lib/bindings/Path'
|
|
|
|
|
export type { Sketch } from '@rust/kcl-lib/bindings/Sketch'
|
|
|
|
|
export type { Solid } from '@rust/kcl-lib/bindings/Solid'
|
2023-09-29 11:11:01 -07:00
|
|
|
|
2024-12-11 21:37:03 +13:00
|
|
|
/**
|
|
|
|
|
* Convert a SourceRange as used inside the KCL interpreter into the above one for use in the
|
|
|
|
|
* frontend (essentially we're eagerly checking whether the frontend should care about the SourceRange
|
|
|
|
|
* so as not to expose details of the interpreter's current representation of module ids throughout
|
|
|
|
|
* the frontend).
|
|
|
|
|
*/
|
2025-01-17 14:34:36 -05:00
|
|
|
export function sourceRangeFromRust(s: SourceRange): SourceRange {
|
|
|
|
|
return [s[0], s[1], s[2]]
|
2024-12-06 13:57:31 +13:00
|
|
|
}
|
|
|
|
|
|
2024-12-11 21:37:03 +13:00
|
|
|
/**
|
|
|
|
|
* Create a default SourceRange for testing or as a placeholder.
|
|
|
|
|
*/
|
2024-12-06 13:57:31 +13:00
|
|
|
export function defaultSourceRange(): SourceRange {
|
2025-01-17 14:34:36 -05:00
|
|
|
return [0, 0, 0]
|
2024-12-06 13:57:31 +13:00
|
|
|
}
|
|
|
|
|
|
2025-02-04 20:34:56 -05:00
|
|
|
function firstSourceRange(error: RustKclError): SourceRange {
|
|
|
|
|
return error.sourceRanges.length > 0
|
|
|
|
|
? sourceRangeFromRust(error.sourceRanges[0])
|
|
|
|
|
: defaultSourceRange()
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-06 13:57:31 +13:00
|
|
|
const splitErrors = (
|
|
|
|
|
input: CompilationError[]
|
|
|
|
|
): { errors: CompilationError[]; warnings: CompilationError[] } => {
|
|
|
|
|
let errors = []
|
|
|
|
|
let warnings = []
|
|
|
|
|
for (const i of input) {
|
|
|
|
|
if (i.severity === 'Warning') {
|
|
|
|
|
warnings.push(i)
|
|
|
|
|
} else {
|
|
|
|
|
errors.push(i)
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-09-29 11:11:01 -07:00
|
|
|
|
2024-12-06 13:57:31 +13:00
|
|
|
return { errors, warnings }
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export class ParseResult {
|
|
|
|
|
program: Node<Program> | null
|
|
|
|
|
errors: CompilationError[]
|
|
|
|
|
warnings: CompilationError[]
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
program: Node<Program> | null,
|
|
|
|
|
errors: CompilationError[],
|
|
|
|
|
warnings: CompilationError[]
|
|
|
|
|
) {
|
|
|
|
|
this.program = program
|
|
|
|
|
this.errors = errors
|
|
|
|
|
this.warnings = warnings
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-11 21:37:03 +13:00
|
|
|
/**
|
|
|
|
|
* Parsing was successful. There is guaranteed to be an AST and no fatal errors. There may or may
|
|
|
|
|
* not be warnings or non-fatal errors.
|
|
|
|
|
*/
|
2024-12-06 13:57:31 +13:00
|
|
|
class SuccessParseResult extends ParseResult {
|
|
|
|
|
program: Node<Program>
|
|
|
|
|
|
|
|
|
|
constructor(
|
|
|
|
|
program: Node<Program>,
|
|
|
|
|
errors: CompilationError[],
|
|
|
|
|
warnings: CompilationError[]
|
|
|
|
|
) {
|
|
|
|
|
super(program, errors, warnings)
|
|
|
|
|
this.program = program
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function resultIsOk(result: ParseResult): result is SuccessParseResult {
|
|
|
|
|
return !!result.program && result.errors.length === 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export const parse = (code: string | Error): ParseResult | Error => {
|
2024-06-24 11:45:40 -04:00
|
|
|
if (err(code)) return code
|
|
|
|
|
|
2023-09-29 11:11:01 -07:00
|
|
|
try {
|
2024-12-06 13:57:31 +13:00
|
|
|
const parsed: [Node<Program>, CompilationError[]] = parse_wasm(code)
|
|
|
|
|
let errs = splitErrors(parsed[1])
|
|
|
|
|
return new ParseResult(parsed[0], errs.errors, errs.warnings)
|
2023-09-29 11:11:01 -07:00
|
|
|
} catch (e: any) {
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
// throw e
|
KCL: Use keyword arguments for line, lineTo, extrude and close (#5249)
Part of #4600.
PR: https://github.com/KittyCAD/modeling-app/pull/4826
# Changes to KCL stdlib
- `line(point, sketch, tag)` and `lineTo(point, sketch, tag)` are combined into `line(@sketch, end?, endAbsolute?, tag?)`
- `close(sketch, tag?)` is now `close(@sketch, tag?)`
- `extrude(length, sketch)` is now `extrude(@sketch, length)`
Note that if a parameter starts with `@` like `@sketch`, it doesn't have any label when called, so you call it like this:
```
sketch = startSketchAt([0, 0])
line(sketch, end = [3, 3], tag = $hi)
```
Note also that if you're using a `|>` pipeline, you can omit the `@` argument and it will be assumed to be the LHS of the `|>`. So the above could be written as
```
sketch = startSketchAt([0, 0])
|> line(end = [3, 3], tag = $hi)
```
Also changes frontend tests to use KittyCAD/kcl-samples#139 instead of its main
The regex find-and-replace I use for migrating code (note these don't work with multi-line expressions) are:
```
line\(([^=]*), %\)
line(end = $1)
line\((.*), %, (.*)\)
line(end = $1, tag = $2)
lineTo\((.*), %\)
line(endAbsolute = $1)
lineTo\((.*), %, (.*)\)
line(endAbsolute = $1, tag = $2)
extrude\((.*), %\)
extrude(length = $1)
extrude\(([^=]*), ([a-zA-Z0-9]+)\)
extrude($2, length = $1)
close\(%, (.*)\)
close(tag = $1)
```
# Selected notes from commits before I squash them all
* Fix test 'yRelative to horizontal distance'
Fixes:
- Make a lineTo helper
- Fix pathToNode to go through the labeled arg .arg property
* Fix test by changing lookups into transformMap
Parts of the code assumed that `line` is always a relative call. But
actually now it might be absolute, if it's got an `endAbsolute` parameter.
So, change whether to look up `line` or `lineTo` and the relevant absolute
or relative line types based on that parameter.
* Stop asserting on exact source ranges
When I changed line to kwargs, all the source ranges we assert on became
slightly different. I find these assertions to be very very low value.
So I'm removing them.
* Fix more tests: getConstraintType calls weren't checking if the
'line' fn was absolute or relative.
* Fixed another queryAst test
There were 2 problems:
- Test was looking for the old style of `line` call to choose an offset
for pathToNode
- Test assumed that the `tag` param was always the third one, but in
a kwarg call, you have to look it up by label
* Fix test: traverse was not handling CallExpressionKw
* Fix another test, addTagKw
addTag helper was not aware of kw args.
* Convert close from positional to kwargs
If the close() call has 0 args, or a single unlabeled arg, the parser
interprets it as a CallExpression (positional) not a CallExpressionKw.
But then if a codemod wants to add a tag to it, it tries adding a kwarg
called 'tag', which fails because the CallExpression doesn't need
kwargs inserted into it.
The fix is: change the node from CallExpression to CallExpressionKw, and
update getNodeFromPath to take a 'replacement' arg, so we can replace
the old node with the new node in the AST.
* Fix the last test
Test was looking for `lineTo` as a substring of the input KCL program.
But there's no more lineTo function, so I changed it to look for
line() with an endAbsolute arg, which is the new equivalent.
Also changed the getConstraintInfo code to look up the lineTo if using
line with endAbsolute.
* Fix many bad regex find-replaces
I wrote a regex find-and-replace which converted `line` calls from
positional to keyword calls. But it was accidentally applied to more
places than it should be, for example, angledLine, xLine and yLine calls.
Fixes this.
* Fixes test 'Basic sketch › code pane closed at start'
Problem was, the getNodeFromPath call might not actually find a callExpressionKw,
it might find a callExpression. So the `giveSketchFnCallTag` thought
it was modifying a kwargs call, but it was actually modifying a positional
call.
This meant it tried to push a labeled argument in, rather than a normal
arg, and a lot of other problems. Fixed by doing runtime typechecking.
* Fix: Optional args given with wrong type were silently ignored
Optional args don't have to be given. But if the user gives them, they
should be the right type.
Bug: if the KCL interpreter found an optional arg, which was given, but
was the wrong type, it would ignore it and pretend the arg was never
given at all. This was confusing for users.
Fix: Now if you give an optional arg, but it's the wrong type, KCL will
emit a type error just like it would for a mandatory argument.
---------
Signed-off-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: Nick Cameron <nrc@ncameron.org>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Frank Noirot <frank@kittycad.io>
Co-authored-by: Kevin Nadro <kevin@zoo.dev>
Co-authored-by: Jonathan Tran <jonnytran@gmail.com>
2025-02-04 08:31:43 -06:00
|
|
|
console.error(e.toString())
|
2023-09-29 11:11:01 -07:00
|
|
|
const parsed: RustKclError = JSON.parse(e.toString())
|
2024-06-24 11:45:40 -04:00
|
|
|
return new KCLError(
|
2023-09-29 11:11:01 -07:00
|
|
|
parsed.kind,
|
|
|
|
|
parsed.msg,
|
2025-02-04 20:34:56 -05:00
|
|
|
firstSourceRange(parsed),
|
2025-01-08 20:02:30 -05:00
|
|
|
[],
|
2025-01-17 14:34:36 -05:00
|
|
|
[],
|
2025-02-25 11:51:54 -06:00
|
|
|
defaultArtifactGraph(),
|
2025-03-15 10:08:39 -07:00
|
|
|
{},
|
|
|
|
|
null
|
2023-09-29 11:11:01 -07:00
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-04-01 13:21:31 -04:00
|
|
|
/**
|
|
|
|
|
* Parse and throw an exception if there are any errors (probably not suitable for use outside of testing).
|
|
|
|
|
*/
|
|
|
|
|
export function assertParse(code: string): Node<Program> {
|
2024-12-06 13:57:31 +13:00
|
|
|
const result = parse(code)
|
|
|
|
|
// eslint-disable-next-line suggest-no-throw/suggest-no-throw
|
2025-04-01 13:21:31 -04:00
|
|
|
if (err(result)) throw result
|
|
|
|
|
if (!resultIsOk(result)) {
|
|
|
|
|
// eslint-disable-next-line suggest-no-throw/suggest-no-throw
|
KCL: Angled line should use keyword args (#5803)
We continue migrating KCL stdlib functions to use keyword arguments. Next up is the `angledLine` family of functions (except `angledLineThatIntersects, which will be a quick follow-up).
Before vs. after:
`angledLine({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, length = 3, tag = $edge)`
`angledLineOfXLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthX = 3, tag = $edge)`
`angledLineOfYLength({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, lengthY = 3, tag = $edge)`
`angledLineToX({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteX = 3, tag = $edge)`
`angledLineToY({angle = 90, length = 3}, %, $edge)`
=> `angledLine(angle = 90, endAbsoluteY = 3, tag = $edge)`
2025-04-09 14:55:15 -05:00
|
|
|
throw new Error(
|
|
|
|
|
`parse result contains errors: ${result.errors.map((err) => err.message).join('\n')}`,
|
|
|
|
|
{ cause: result }
|
|
|
|
|
)
|
2025-04-01 13:21:31 -04:00
|
|
|
}
|
2024-12-06 13:57:31 +13:00
|
|
|
return result.program
|
|
|
|
|
}
|
|
|
|
|
|
2025-02-12 10:22:56 +13:00
|
|
|
export type VariableMap = { [key in string]?: KclValue }
|
|
|
|
|
|
2023-09-29 11:11:01 -07:00
|
|
|
export type PathToNode = [string | number, string][]
|
|
|
|
|
|
2024-11-18 16:25:25 -05:00
|
|
|
export const isPathToNodeNumber = (
|
|
|
|
|
pathToNode: string | number
|
|
|
|
|
): pathToNode is number => {
|
|
|
|
|
return typeof pathToNode === 'number'
|
|
|
|
|
}
|
|
|
|
|
|
2025-03-20 16:41:09 -04:00
|
|
|
export const isPathToNode = (input: unknown): input is PathToNode =>
|
|
|
|
|
isArray(input) &&
|
|
|
|
|
isArray(input[0]) &&
|
|
|
|
|
input[0].length == 2 &&
|
|
|
|
|
(typeof input[0][0] === 'number' || typeof input[0][0] === 'string') &&
|
|
|
|
|
typeof input[0][1] === 'string'
|
|
|
|
|
|
2024-10-09 19:38:40 -04:00
|
|
|
export interface ExecState {
|
2025-02-12 10:22:56 +13:00
|
|
|
variables: { [key in string]?: KclValue }
|
2024-12-20 16:19:59 -05:00
|
|
|
operations: Operation[]
|
2025-01-08 20:02:30 -05:00
|
|
|
artifactCommands: ArtifactCommand[]
|
2025-01-17 14:34:36 -05:00
|
|
|
artifactGraph: ArtifactGraph
|
2025-02-21 09:30:44 +13:00
|
|
|
errors: CompilationError[]
|
2025-02-25 11:51:54 -06:00
|
|
|
filenames: { [x: number]: ModulePath | undefined }
|
2025-03-15 10:08:39 -07:00
|
|
|
defaultPlanes: DefaultPlanes | null
|
2023-09-29 11:11:01 -07:00
|
|
|
}
|
|
|
|
|
|
2024-10-09 19:38:40 -04:00
|
|
|
/**
|
|
|
|
|
* Create an empty ExecState. This is useful on init to prevent needing an
|
|
|
|
|
* Option.
|
|
|
|
|
*/
|
|
|
|
|
export function emptyExecState(): ExecState {
|
|
|
|
|
return {
|
2025-02-12 10:22:56 +13:00
|
|
|
variables: {},
|
2024-12-20 16:19:59 -05:00
|
|
|
operations: [],
|
2025-01-08 20:02:30 -05:00
|
|
|
artifactCommands: [],
|
2025-01-17 14:34:36 -05:00
|
|
|
artifactGraph: defaultArtifactGraph(),
|
2025-02-21 09:30:44 +13:00
|
|
|
errors: [],
|
2025-02-25 11:51:54 -06:00
|
|
|
filenames: [],
|
2025-03-15 10:08:39 -07:00
|
|
|
defaultPlanes: null,
|
2024-10-09 19:38:40 -04:00
|
|
|
}
|
|
|
|
|
}
|
2024-07-22 19:43:40 -04:00
|
|
|
|
2025-03-15 10:08:39 -07:00
|
|
|
export function execStateFromRust(
|
2025-01-17 14:34:36 -05:00
|
|
|
execOutcome: RustExecOutcome,
|
|
|
|
|
program: Node<Program>
|
|
|
|
|
): ExecState {
|
|
|
|
|
const artifactGraph = rustArtifactGraphToMap(execOutcome.artifactGraph)
|
|
|
|
|
// We haven't ported pathToNode logic to Rust yet, so we need to fill it in.
|
2025-03-19 03:52:10 +11:00
|
|
|
for (const [_id, artifact] of artifactGraph) {
|
2025-01-17 14:34:36 -05:00
|
|
|
if (!artifact) continue
|
|
|
|
|
if (!('codeRef' in artifact)) continue
|
|
|
|
|
const pathToNode = getNodePathFromSourceRange(
|
|
|
|
|
program,
|
|
|
|
|
sourceRangeFromRust(artifact.codeRef.range)
|
|
|
|
|
)
|
|
|
|
|
artifact.codeRef.pathToNode = pathToNode
|
|
|
|
|
}
|
|
|
|
|
|
2024-10-09 19:38:40 -04:00
|
|
|
return {
|
2025-02-12 10:22:56 +13:00
|
|
|
variables: execOutcome.variables,
|
2025-01-06 16:55:59 -05:00
|
|
|
operations: execOutcome.operations,
|
2025-01-08 20:02:30 -05:00
|
|
|
artifactCommands: execOutcome.artifactCommands,
|
2025-01-17 14:34:36 -05:00
|
|
|
artifactGraph,
|
2025-02-21 09:30:44 +13:00
|
|
|
errors: execOutcome.errors,
|
2025-02-25 11:51:54 -06:00
|
|
|
filenames: execOutcome.filenames,
|
2025-03-15 10:08:39 -07:00
|
|
|
defaultPlanes: execOutcome.defaultPlanes,
|
2024-10-09 19:38:40 -04:00
|
|
|
}
|
2024-07-22 19:43:40 -04:00
|
|
|
}
|
|
|
|
|
|
2025-03-17 18:26:11 -07:00
|
|
|
export function mockExecStateFromRust(execOutcome: RustExecOutcome): ExecState {
|
2025-02-12 10:22:56 +13:00
|
|
|
return {
|
|
|
|
|
variables: execOutcome.variables,
|
|
|
|
|
operations: execOutcome.operations,
|
|
|
|
|
artifactCommands: execOutcome.artifactCommands,
|
|
|
|
|
artifactGraph: new Map<ArtifactId, Artifact>(),
|
2025-02-21 09:30:44 +13:00
|
|
|
errors: execOutcome.errors,
|
2025-02-25 11:51:54 -06:00
|
|
|
filenames: execOutcome.filenames,
|
2025-03-15 10:08:39 -07:00
|
|
|
defaultPlanes: execOutcome.defaultPlanes,
|
2025-02-12 10:22:56 +13:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-01-17 14:34:36 -05:00
|
|
|
export type ArtifactGraph = Map<ArtifactId, Artifact>
|
|
|
|
|
|
|
|
|
|
function rustArtifactGraphToMap(
|
|
|
|
|
rustArtifactGraph: RustArtifactGraph
|
|
|
|
|
): ArtifactGraph {
|
|
|
|
|
const map = new Map<ArtifactId, Artifact>()
|
|
|
|
|
for (const [id, artifact] of Object.entries(rustArtifactGraph.map)) {
|
|
|
|
|
if (!artifact) continue
|
|
|
|
|
map.set(id, artifact)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return map
|
|
|
|
|
}
|
|
|
|
|
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
// TODO: In the future, make the parameter be a KclValue.
|
2024-11-19 17:34:54 -05:00
|
|
|
export function sketchFromKclValueOptional(
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
obj: any,
|
|
|
|
|
varName: string | null
|
2024-11-19 17:34:54 -05:00
|
|
|
): Sketch | Reason {
|
2024-09-27 15:44:44 -07:00
|
|
|
if (obj?.value?.type === 'Sketch') return obj.value
|
|
|
|
|
if (obj?.value?.type === 'Solid') return obj.value.sketch
|
2025-01-22 09:42:09 +13:00
|
|
|
if (obj?.type === 'Sketch') return obj.value
|
|
|
|
|
if (obj?.type === 'Solid') return obj.value.sketch
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
if (!varName) {
|
|
|
|
|
varName = 'a KCL value'
|
|
|
|
|
}
|
|
|
|
|
const actualType = obj?.value?.type ?? obj?.type
|
|
|
|
|
if (actualType) {
|
2024-11-19 17:34:54 -05:00
|
|
|
return new Reason(
|
2024-09-27 15:44:44 -07:00
|
|
|
`Expected ${varName} to be a sketch or solid, but it was ${actualType} instead.`
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
)
|
|
|
|
|
} else {
|
2024-11-19 17:34:54 -05:00
|
|
|
return new Reason(`Expected ${varName} to be a sketch, but it wasn't.`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO: In the future, make the parameter be a KclValue.
|
|
|
|
|
export function sketchFromKclValue(
|
|
|
|
|
obj: any,
|
|
|
|
|
varName: string | null
|
|
|
|
|
): Sketch | Error {
|
|
|
|
|
const result = sketchFromKclValueOptional(obj, varName)
|
|
|
|
|
if (result instanceof Reason) {
|
|
|
|
|
return result.toError()
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
}
|
2024-11-19 17:34:54 -05:00
|
|
|
return result
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
}
|
|
|
|
|
|
2025-03-15 10:08:39 -07:00
|
|
|
export const errFromErrWithOutputs = (e: any): KCLError => {
|
2025-02-12 10:22:56 +13:00
|
|
|
const parsed: KclErrorWithOutputs = JSON.parse(e.toString())
|
|
|
|
|
return new KCLError(
|
|
|
|
|
parsed.error.kind,
|
|
|
|
|
parsed.error.msg,
|
|
|
|
|
firstSourceRange(parsed.error),
|
|
|
|
|
parsed.operations,
|
|
|
|
|
parsed.artifactCommands,
|
2025-02-25 11:51:54 -06:00
|
|
|
rustArtifactGraphToMap(parsed.artifactGraph),
|
2025-03-15 10:08:39 -07:00
|
|
|
parsed.filenames,
|
|
|
|
|
parsed.defaultPlanes
|
2025-02-12 10:22:56 +13:00
|
|
|
)
|
2023-09-29 11:11:01 -07:00
|
|
|
}
|
|
|
|
|
|
2024-06-26 00:04:52 -04:00
|
|
|
export const kclLint = async (ast: Program): Promise<Array<Discovered>> => {
|
|
|
|
|
try {
|
|
|
|
|
const discovered_findings: Array<Discovered> = await kcl_lint(
|
|
|
|
|
JSON.stringify(ast)
|
|
|
|
|
)
|
|
|
|
|
return discovered_findings
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
return Promise.reject(e)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-24 11:45:40 -04:00
|
|
|
export const recast = (ast: Program): string | Error => {
|
|
|
|
|
return recast_wasm(JSON.stringify(ast))
|
2023-09-29 11:11:01 -07:00
|
|
|
}
|
|
|
|
|
|
2025-01-31 10:45:39 -05:00
|
|
|
/**
|
|
|
|
|
* Format a number with suffix as KCL.
|
|
|
|
|
*/
|
|
|
|
|
export function formatNumber(value: number, suffix: NumericSuffix): string {
|
|
|
|
|
return format_number(value, JSON.stringify(suffix))
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-11 12:59:00 +11:00
|
|
|
export function isPointsCCW(points: Coords2d[]): number {
|
|
|
|
|
return is_points_ccw(new Float64Array(points.flat()))
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function getTangentialArcToInfo({
|
|
|
|
|
arcStartPoint,
|
|
|
|
|
arcEndPoint,
|
|
|
|
|
tanPreviousPoint,
|
|
|
|
|
obtuse = true,
|
|
|
|
|
}: {
|
|
|
|
|
arcStartPoint: Coords2d
|
|
|
|
|
arcEndPoint: Coords2d
|
|
|
|
|
tanPreviousPoint: Coords2d
|
|
|
|
|
obtuse?: boolean
|
|
|
|
|
}): {
|
|
|
|
|
center: Coords2d
|
|
|
|
|
arcMidPoint: Coords2d
|
|
|
|
|
radius: number
|
|
|
|
|
startAngle: number
|
|
|
|
|
endAngle: number
|
|
|
|
|
ccw: boolean
|
2024-04-03 13:22:56 +11:00
|
|
|
arcLength: number
|
2024-02-11 12:59:00 +11:00
|
|
|
} {
|
|
|
|
|
const result = get_tangential_arc_to_info(
|
|
|
|
|
arcStartPoint[0],
|
|
|
|
|
arcStartPoint[1],
|
|
|
|
|
arcEndPoint[0],
|
|
|
|
|
arcEndPoint[1],
|
|
|
|
|
tanPreviousPoint[0],
|
|
|
|
|
tanPreviousPoint[1],
|
|
|
|
|
obtuse
|
|
|
|
|
)
|
|
|
|
|
return {
|
|
|
|
|
center: [result.center_x, result.center_y],
|
|
|
|
|
arcMidPoint: [result.arc_mid_point_x, result.arc_mid_point_y],
|
|
|
|
|
radius: result.radius,
|
|
|
|
|
startAngle: result.start_angle,
|
|
|
|
|
endAngle: result.end_angle,
|
|
|
|
|
ccw: result.ccw > 0,
|
2024-04-03 13:22:56 +11:00
|
|
|
arcLength: result.arc_length,
|
2024-02-11 12:59:00 +11:00
|
|
|
}
|
|
|
|
|
}
|
2024-02-11 18:26:09 -08:00
|
|
|
|
2024-04-09 18:05:36 -07:00
|
|
|
export async function coreDump(
|
|
|
|
|
coreDumpManager: CoreDumpManager,
|
|
|
|
|
openGithubIssue: boolean = false
|
2024-06-20 16:36:28 -07:00
|
|
|
): Promise<CoreDumpInfo> {
|
2024-04-09 18:05:36 -07:00
|
|
|
try {
|
2024-06-28 18:06:40 -07:00
|
|
|
console.warn('CoreDump: Initializing core dump')
|
2024-06-20 16:36:28 -07:00
|
|
|
const dump: CoreDumpInfo = await coredump(coreDumpManager)
|
|
|
|
|
/* NOTE: this console output of the coredump should include the field
|
|
|
|
|
`github_issue_url` which is not in the uploaded coredump file.
|
|
|
|
|
`github_issue_url` is added after the file is uploaded
|
|
|
|
|
and is only needed for the openWindow operation which creates
|
|
|
|
|
a new GitHub issue for the user.
|
|
|
|
|
*/
|
2024-04-09 18:05:36 -07:00
|
|
|
if (openGithubIssue && dump.github_issue_url) {
|
2024-09-09 18:17:45 -04:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
2024-04-09 18:05:36 -07:00
|
|
|
openWindow(dump.github_issue_url)
|
2024-06-20 16:36:28 -07:00
|
|
|
} else {
|
|
|
|
|
console.error(
|
|
|
|
|
'github_issue_url undefined. Unable to create GitHub issue for coredump.'
|
|
|
|
|
)
|
2024-04-09 18:05:36 -07:00
|
|
|
}
|
2024-06-20 16:36:28 -07:00
|
|
|
console.log('CoreDump: final coredump', dump)
|
|
|
|
|
console.log('CoreDump: final coredump JSON', JSON.stringify(dump))
|
2024-04-09 18:05:36 -07:00
|
|
|
return dump
|
|
|
|
|
} catch (e: any) {
|
2024-06-20 16:36:28 -07:00
|
|
|
console.error('CoreDump: error', e)
|
2024-06-24 11:45:40 -04:00
|
|
|
return Promise.reject(new Error(`Error getting core dump: ${e}`))
|
2024-04-09 18:05:36 -07:00
|
|
|
}
|
|
|
|
|
}
|
2024-04-16 21:36:19 -07:00
|
|
|
|
2024-08-20 22:16:44 -04:00
|
|
|
export function defaultAppSettings(): DeepPartial<Configuration> | Error {
|
|
|
|
|
return default_app_settings()
|
2024-04-25 00:13:09 -07:00
|
|
|
}
|
|
|
|
|
|
2024-08-20 22:16:44 -04:00
|
|
|
export function parseAppSettings(
|
|
|
|
|
toml: string
|
|
|
|
|
): DeepPartial<Configuration> | Error {
|
|
|
|
|
return parse_app_settings(toml)
|
2024-04-25 00:13:09 -07:00
|
|
|
}
|
|
|
|
|
|
2024-08-20 22:16:44 -04:00
|
|
|
export function defaultProjectSettings():
|
|
|
|
|
| DeepPartial<ProjectConfiguration>
|
|
|
|
|
| Error {
|
|
|
|
|
return default_project_settings()
|
2024-04-25 00:13:09 -07:00
|
|
|
}
|
|
|
|
|
|
2024-06-24 11:45:40 -04:00
|
|
|
export function parseProjectSettings(
|
|
|
|
|
toml: string
|
2024-08-20 22:16:44 -04:00
|
|
|
): DeepPartial<ProjectConfiguration> | Error {
|
|
|
|
|
return parse_project_settings(toml)
|
2024-04-16 21:36:19 -07:00
|
|
|
}
|
2024-04-25 11:55:11 -07:00
|
|
|
|
2024-08-14 14:26:44 -04:00
|
|
|
export function base64Decode(base64: string): ArrayBuffer | Error {
|
|
|
|
|
try {
|
|
|
|
|
const decoded = base64_decode(base64)
|
|
|
|
|
return new Uint8Array(decoded).buffer
|
|
|
|
|
} catch (e) {
|
2025-04-01 13:21:31 -04:00
|
|
|
console.error('Caught error decoding base64 string', e)
|
|
|
|
|
return new Error('Caught error decoding base64 string', { cause: e })
|
2024-08-14 14:26:44 -04:00
|
|
|
}
|
|
|
|
|
}
|
2025-01-31 13:11:15 -08:00
|
|
|
|
2025-02-06 12:37:13 -05:00
|
|
|
/**
|
|
|
|
|
* Get the meta settings for the KCL. If no settings were set in the file,
|
|
|
|
|
* returns null.
|
|
|
|
|
*/
|
|
|
|
|
export function kclSettings(
|
|
|
|
|
kcl: string | Node<Program>
|
|
|
|
|
): MetaSettings | null | Error {
|
|
|
|
|
let program: Node<Program>
|
|
|
|
|
if (typeof kcl === 'string') {
|
|
|
|
|
const parseResult = parse(kcl)
|
|
|
|
|
if (err(parseResult)) return parseResult
|
|
|
|
|
if (!resultIsOk(parseResult)) {
|
|
|
|
|
return new Error(`parse result had errors`, { cause: parseResult })
|
|
|
|
|
}
|
|
|
|
|
program = parseResult.program
|
|
|
|
|
} else {
|
|
|
|
|
program = kcl
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
return kcl_settings(JSON.stringify(program))
|
|
|
|
|
} catch (e) {
|
|
|
|
|
return new Error('Caught error getting kcl settings', { cause: e })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Change the meta settings for the kcl file.
|
|
|
|
|
* @returns the new kcl string with the updated settings.
|
|
|
|
|
*/
|
2025-01-31 13:11:15 -08:00
|
|
|
export function changeKclSettings(
|
|
|
|
|
kcl: string,
|
|
|
|
|
settings: MetaSettings
|
|
|
|
|
): string | Error {
|
|
|
|
|
try {
|
|
|
|
|
return change_kcl_settings(kcl, JSON.stringify(settings))
|
|
|
|
|
} catch (e) {
|
2025-02-06 12:37:13 -05:00
|
|
|
console.error('Caught error changing kcl settings', e)
|
|
|
|
|
return new Error('Caught error changing kcl settings', { cause: e })
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-03-31 10:56:03 -04:00
|
|
|
* Returns true if the given KCL is empty or only contains settings that would
|
|
|
|
|
* be auto-generated.
|
|
|
|
|
*/
|
|
|
|
|
export function isKclEmptyOrOnlySettings(kcl: string): boolean {
|
|
|
|
|
if (kcl === '') {
|
|
|
|
|
// Fast path.
|
|
|
|
|
return true
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
return is_kcl_empty_or_only_settings(kcl)
|
|
|
|
|
} catch (e) {
|
|
|
|
|
console.debug('Caught error checking if KCL is empty', e)
|
|
|
|
|
// If there's a parse error, it can't be empty or auto-generated.
|
|
|
|
|
return false
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert a `UnitLength` (used in settings and modeling commands) to a
|
|
|
|
|
* `UnitLen` (used in execution).
|
2025-02-06 12:37:13 -05:00
|
|
|
*/
|
|
|
|
|
export function unitLengthToUnitLen(input: UnitLength): UnitLen {
|
|
|
|
|
switch (input) {
|
|
|
|
|
case 'm':
|
|
|
|
|
return { type: 'M' }
|
|
|
|
|
case 'cm':
|
|
|
|
|
return { type: 'Cm' }
|
|
|
|
|
case 'yd':
|
|
|
|
|
return { type: 'Yards' }
|
|
|
|
|
case 'ft':
|
|
|
|
|
return { type: 'Feet' }
|
|
|
|
|
case 'in':
|
|
|
|
|
return { type: 'Inches' }
|
|
|
|
|
default:
|
|
|
|
|
return { type: 'Mm' }
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-03-31 10:56:03 -04:00
|
|
|
* Convert `UnitLen` (used in execution) to `UnitLength` (used in settings
|
|
|
|
|
* and modeling commands).
|
2025-02-06 12:37:13 -05:00
|
|
|
*/
|
|
|
|
|
export function unitLenToUnitLength(input: UnitLen): UnitLength {
|
|
|
|
|
switch (input.type) {
|
|
|
|
|
case 'M':
|
|
|
|
|
return 'm'
|
|
|
|
|
case 'Cm':
|
|
|
|
|
return 'cm'
|
|
|
|
|
case 'Yards':
|
|
|
|
|
return 'yd'
|
|
|
|
|
case 'Feet':
|
|
|
|
|
return 'ft'
|
|
|
|
|
case 'Inches':
|
|
|
|
|
return 'in'
|
|
|
|
|
default:
|
2025-03-31 10:56:03 -04:00
|
|
|
return DEFAULT_DEFAULT_LENGTH_UNIT
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Convert a `UnitAngle` (used in modeling commands) to a `UnitAng` (used in
|
|
|
|
|
* execution).
|
|
|
|
|
*/
|
|
|
|
|
export function unitAngleToUnitAng(input: UnitAngle): UnitAng {
|
|
|
|
|
switch (input) {
|
|
|
|
|
case 'radians':
|
|
|
|
|
return { type: 'Radians' }
|
|
|
|
|
default:
|
|
|
|
|
return { type: 'Degrees' }
|
2025-02-06 12:37:13 -05:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2025-03-31 10:56:03 -04:00
|
|
|
* Convert `UnitAng` (used in execution) to `UnitAngle` (used in modeling
|
|
|
|
|
* commands).
|
2025-02-06 12:37:13 -05:00
|
|
|
*/
|
|
|
|
|
export function unitAngToUnitAngle(input: UnitAng): UnitAngle {
|
|
|
|
|
switch (input.type) {
|
|
|
|
|
case 'Radians':
|
|
|
|
|
return 'radians'
|
|
|
|
|
default:
|
2025-03-31 10:56:03 -04:00
|
|
|
return DEFAULT_DEFAULT_ANGLE_UNIT
|
2025-01-31 13:11:15 -08:00
|
|
|
}
|
|
|
|
|
}
|
2025-02-12 15:55:34 -08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get the KCL version currently being used.
|
|
|
|
|
*/
|
|
|
|
|
export function getKclVersion(): string {
|
|
|
|
|
return get_kcl_version()
|
|
|
|
|
}
|
2025-02-27 16:35:39 -08:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Serialize a project configuration to a TOML string.
|
|
|
|
|
*/
|
2025-04-30 12:00:37 -07:00
|
|
|
export function serializeConfiguration(
|
|
|
|
|
configuration: DeepPartial<Configuration>
|
|
|
|
|
): string | Error {
|
2025-02-27 16:35:39 -08:00
|
|
|
try {
|
|
|
|
|
return serialize_configuration(configuration)
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
return new Error(`Error serializing configuration: ${e}`)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Serialize a project configuration to a TOML string.
|
|
|
|
|
*/
|
|
|
|
|
export function serializeProjectConfiguration(
|
2025-04-30 12:00:37 -07:00
|
|
|
configuration: DeepPartial<ProjectConfiguration>
|
2025-02-27 16:35:39 -08:00
|
|
|
): string | Error {
|
|
|
|
|
try {
|
|
|
|
|
return serialize_project_configuration(configuration)
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
return new Error(`Error serializing project configuration: ${e}`)
|
|
|
|
|
}
|
|
|
|
|
}
|