2025-01-08 10:58:41 -05:00
|
|
|
import {
|
|
|
|
init,
|
2023-09-29 11:11:01 -07:00
|
|
|
parse_wasm,
|
|
|
|
recast_wasm,
|
2024-12-10 18:50:22 -08:00
|
|
|
execute,
|
2024-06-26 00:04:52 -04:00
|
|
|
kcl_lint,
|
2023-09-29 11:11:01 -07:00
|
|
|
modify_ast_for_sketch_wasm,
|
2024-02-11 12:59:00 +11:00
|
|
|
is_points_ccw,
|
|
|
|
get_tangential_arc_to_info,
|
2024-02-11 18:26:09 -08:00
|
|
|
program_memory_init,
|
2024-04-15 17:18:32 -07:00
|
|
|
make_default_planes,
|
2024-04-09 18:05:36 -07:00
|
|
|
coredump,
|
2024-04-16 21:36:19 -07:00
|
|
|
toml_stringify,
|
2024-04-25 00:13:09 -07:00
|
|
|
default_app_settings,
|
|
|
|
parse_app_settings,
|
|
|
|
parse_project_settings,
|
|
|
|
default_project_settings,
|
2024-08-14 14:26:44 -04:00
|
|
|
base64_decode,
|
2024-12-06 14:56:53 -08:00
|
|
|
clear_scene_and_bust_cache,
|
2025-01-08 10:58:41 -05:00
|
|
|
reloadModule,
|
|
|
|
} from 'lib/wasm_lib_wrapper'
|
|
|
|
|
2023-09-29 11:11:01 -07:00
|
|
|
import { KCLError } from './errors'
|
|
|
|
import { KclError as RustKclError } from '../wasm-lib/kcl/bindings/KclError'
|
2023-10-14 03:47:46 +11:00
|
|
|
import { EngineCommandManager } from './std/engineConnection'
|
2024-06-26 00:04:52 -04:00
|
|
|
import { Discovered } from '../wasm-lib/kcl/bindings/Discovered'
|
2024-08-12 16:53:24 -05:00
|
|
|
import { KclValue } from '../wasm-lib/kcl/bindings/KclValue'
|
2023-09-29 11:11:01 -07:00
|
|
|
import type { Program } from '../wasm-lib/kcl/bindings/Program'
|
2024-02-11 12:59:00 +11:00
|
|
|
import { Coords2d } from './std/sketch'
|
2024-02-12 12:18:37 -08:00
|
|
|
import { fileSystemManager } from 'lang/std/fileSystemManager'
|
2024-06-20 16:36:28 -07:00
|
|
|
import { CoreDumpInfo } from 'wasm-lib/kcl/bindings/CoreDumpInfo'
|
2024-04-09 18:05:36 -07:00
|
|
|
import { CoreDumpManager } from 'lib/coredump'
|
|
|
|
import openWindow from 'lib/openWindow'
|
2024-04-15 17:18:32 -07:00
|
|
|
import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes'
|
2024-04-19 14:24:40 -07:00
|
|
|
import { TEST } from 'env'
|
2024-11-19 17:34:54 -05:00
|
|
|
import { err, Reason } from 'lib/trap'
|
2024-08-20 22:16:44 -04:00
|
|
|
import { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
|
|
|
import { DeepPartial } from 'lib/types'
|
|
|
|
import { ProjectConfiguration } from 'wasm-lib/kcl/bindings/ProjectConfiguration'
|
2024-09-27 15:44:44 -07:00
|
|
|
import { Sketch } from '../wasm-lib/kcl/bindings/Sketch'
|
2025-01-06 16:55:59 -05:00
|
|
|
import { ExecOutcome as RustExecOutcome } from 'wasm-lib/kcl/bindings/ExecOutcome'
|
2024-10-09 19:38:40 -04:00
|
|
|
import { ProgramMemory as RawProgramMemory } from '../wasm-lib/kcl/bindings/ProgramMemory'
|
|
|
|
import { EnvironmentRef } from '../wasm-lib/kcl/bindings/EnvironmentRef'
|
|
|
|
import { Environment } from '../wasm-lib/kcl/bindings/Environment'
|
2024-10-30 16:52:17 -04:00
|
|
|
import { Node } from 'wasm-lib/kcl/bindings/Node'
|
2024-12-06 13:57:31 +13:00
|
|
|
import { CompilationError } from 'wasm-lib/kcl/bindings/CompilationError'
|
2025-01-17 14:34:36 -05:00
|
|
|
import { SourceRange } from 'wasm-lib/kcl/bindings/SourceRange'
|
2024-12-10 18:50:22 -08:00
|
|
|
import { getAllCurrentSettings } from 'lib/settings/settingsUtils'
|
2024-12-20 16:19:59 -05:00
|
|
|
import { Operation } from 'wasm-lib/kcl/bindings/Operation'
|
2024-12-16 13:10:31 -05:00
|
|
|
import { KclErrorWithOutputs } from 'wasm-lib/kcl/bindings/KclErrorWithOutputs'
|
2025-01-17 14:34:36 -05:00
|
|
|
import { Artifact as RustArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
import { ArtifactId } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
import { ArtifactCommand } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
import { ArtifactGraph as RustArtifactGraph } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
import { Artifact } from './std/artifactGraph'
|
2025-01-27 14:24:28 +01:00
|
|
|
import { getNodePathFromSourceRange } from 'lang/queryAstNodePathUtils'
|
2023-09-29 11:11:01 -07:00
|
|
|
|
2025-01-08 20:02:30 -05:00
|
|
|
export type { Artifact } from 'wasm-lib/kcl/bindings/Artifact'
|
2025-01-17 14:34:36 -05:00
|
|
|
export type { ArtifactCommand } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
export type { ArtifactId } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
export type { Cap as CapArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
export type { CodeRef } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
export type { EdgeCut } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
export type { Path as PathArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
export type { Plane as PlaneArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
export type { Segment as SegmentArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
export type { Solid2d as Solid2dArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
export type { Sweep as SweepArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
export type { SweepEdge } from 'wasm-lib/kcl/bindings/Artifact'
|
|
|
|
export type { Wall as WallArtifact } from 'wasm-lib/kcl/bindings/Artifact'
|
2024-12-10 18:50:22 -08:00
|
|
|
export type { Configuration } from 'wasm-lib/kcl/bindings/Configuration'
|
2023-09-29 11:11:01 -07:00
|
|
|
export type { Program } from '../wasm-lib/kcl/bindings/Program'
|
2024-08-12 15:38:42 -05:00
|
|
|
export type { Expr } from '../wasm-lib/kcl/bindings/Expr'
|
2023-09-29 11:11:01 -07:00
|
|
|
export type { ObjectExpression } from '../wasm-lib/kcl/bindings/ObjectExpression'
|
2024-09-26 18:25:05 +10:00
|
|
|
export type { ObjectProperty } from '../wasm-lib/kcl/bindings/ObjectProperty'
|
2023-09-29 11:11:01 -07:00
|
|
|
export type { MemberExpression } from '../wasm-lib/kcl/bindings/MemberExpression'
|
|
|
|
export type { PipeExpression } from '../wasm-lib/kcl/bindings/PipeExpression'
|
|
|
|
export type { VariableDeclaration } from '../wasm-lib/kcl/bindings/VariableDeclaration'
|
2023-11-20 11:19:08 -06:00
|
|
|
export type { Parameter } from '../wasm-lib/kcl/bindings/Parameter'
|
2023-09-29 11:11:01 -07:00
|
|
|
export type { PipeSubstitution } from '../wasm-lib/kcl/bindings/PipeSubstitution'
|
|
|
|
export type { Identifier } from '../wasm-lib/kcl/bindings/Identifier'
|
|
|
|
export type { UnaryExpression } from '../wasm-lib/kcl/bindings/UnaryExpression'
|
|
|
|
export type { BinaryExpression } from '../wasm-lib/kcl/bindings/BinaryExpression'
|
|
|
|
export type { ReturnStatement } from '../wasm-lib/kcl/bindings/ReturnStatement'
|
|
|
|
export type { ExpressionStatement } from '../wasm-lib/kcl/bindings/ExpressionStatement'
|
|
|
|
export type { CallExpression } from '../wasm-lib/kcl/bindings/CallExpression'
|
|
|
|
export type { VariableDeclarator } from '../wasm-lib/kcl/bindings/VariableDeclarator'
|
|
|
|
export type { BinaryPart } from '../wasm-lib/kcl/bindings/BinaryPart'
|
|
|
|
export type { Literal } from '../wasm-lib/kcl/bindings/Literal'
|
2024-11-18 10:04:09 -05:00
|
|
|
export type { LiteralValue } from '../wasm-lib/kcl/bindings/LiteralValue'
|
2023-09-29 11:11:01 -07:00
|
|
|
export type { ArrayExpression } from '../wasm-lib/kcl/bindings/ArrayExpression'
|
2025-01-17 14:34:36 -05:00
|
|
|
export type { SourceRange } from 'wasm-lib/kcl/bindings/SourceRange'
|
2023-09-29 11:11:01 -07:00
|
|
|
|
|
|
|
export type SyntaxType =
|
|
|
|
| 'Program'
|
|
|
|
| 'ExpressionStatement'
|
|
|
|
| 'BinaryExpression'
|
|
|
|
| 'CallExpression'
|
|
|
|
| 'Identifier'
|
|
|
|
| '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'
|
|
|
|
|
|
|
|
export type { Path } from '../wasm-lib/kcl/bindings/Path'
|
2024-09-27 15:44:44 -07:00
|
|
|
export type { Sketch } from '../wasm-lib/kcl/bindings/Sketch'
|
|
|
|
export type { Solid } from '../wasm-lib/kcl/bindings/Solid'
|
2024-08-12 16:53:24 -05:00
|
|
|
export type { KclValue } from '../wasm-lib/kcl/bindings/KclValue'
|
2023-09-29 11:11:01 -07:00
|
|
|
export type { ExtrudeSurface } from '../wasm-lib/kcl/bindings/ExtrudeSurface'
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2024-12-13 14:03:24 -05:00
|
|
|
/**
|
2025-01-17 14:34:36 -05:00
|
|
|
* Create a SourceRange for the top-level module.
|
2024-12-13 14:03:24 -05:00
|
|
|
*/
|
2025-01-17 14:34:36 -05:00
|
|
|
export function topLevelRange(start: number, end: number): SourceRange {
|
|
|
|
return [start, end, 0]
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns true if this source range is from the file being executed. Returns
|
|
|
|
* false if it's from a file that was imported.
|
|
|
|
*/
|
|
|
|
export function isTopLevelModule(range: SourceRange): boolean {
|
|
|
|
return range[2] === 0
|
2024-12-13 14:03:24 -05:00
|
|
|
}
|
|
|
|
|
2024-04-16 21:36:19 -07:00
|
|
|
export const wasmUrl = () => {
|
2024-08-16 07:15:42 -04:00
|
|
|
// For when we're in electron (file based) or web server (network based)
|
|
|
|
// For some reason relative paths don't work as expected. Otherwise we would
|
|
|
|
// just do /wasm_lib_bg.wasm. In particular, the issue arises when the path
|
|
|
|
// is used from within worker.ts.
|
|
|
|
const fullUrl = document.location.protocol.includes('http')
|
|
|
|
? document.location.origin + '/wasm_lib_bg.wasm'
|
|
|
|
: document.location.protocol +
|
|
|
|
document.location.pathname.split('/').slice(0, -1).join('/') +
|
|
|
|
'/wasm_lib_bg.wasm'
|
|
|
|
|
2024-04-16 21:36:19 -07:00
|
|
|
return fullUrl
|
|
|
|
}
|
|
|
|
|
|
|
|
// Initialise the wasm module.
|
|
|
|
const initialise = async () => {
|
2024-04-19 14:24:40 -07:00
|
|
|
try {
|
2025-01-08 10:58:41 -05:00
|
|
|
await reloadModule()
|
2024-04-19 14:24:40 -07:00
|
|
|
const fullUrl = wasmUrl()
|
|
|
|
const input = await fetch(fullUrl)
|
|
|
|
const buffer = await input.arrayBuffer()
|
2024-12-11 04:54:20 -05:00
|
|
|
return await init({ module_or_path: buffer })
|
2024-04-19 14:24:40 -07:00
|
|
|
} catch (e) {
|
|
|
|
console.log('Error initialising WASM', e)
|
2024-06-24 11:45:40 -04:00
|
|
|
return Promise.reject(e)
|
2024-04-19 14:24:40 -07:00
|
|
|
}
|
2023-09-29 11:11:01 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
export const initPromise = initialise()
|
|
|
|
|
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
|
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,
|
2024-12-16 13:10:31 -05:00
|
|
|
sourceRangeFromRust(parsed.sourceRanges[0]),
|
2025-01-08 20:02:30 -05:00
|
|
|
[],
|
2025-01-17 14:34:36 -05:00
|
|
|
[],
|
|
|
|
defaultArtifactGraph()
|
2023-09-29 11:11:01 -07:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-12-06 13:57:31 +13:00
|
|
|
// Parse and throw an exception if there are any errors (probably not suitable for use outside of testing).
|
|
|
|
export const assertParse = (code: string): Node<Program> => {
|
|
|
|
const result = parse(code)
|
|
|
|
// eslint-disable-next-line suggest-no-throw/suggest-no-throw
|
|
|
|
if (err(result) || !resultIsOk(result)) throw result
|
|
|
|
return result.program
|
|
|
|
}
|
|
|
|
|
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'
|
|
|
|
}
|
|
|
|
|
2024-10-09 19:38:40 -04:00
|
|
|
export interface ExecState {
|
|
|
|
memory: ProgramMemory
|
2024-12-20 16:19:59 -05:00
|
|
|
operations: Operation[]
|
2025-01-17 14:34:36 -05:00
|
|
|
artifacts: { [key in ArtifactId]?: RustArtifact }
|
2025-01-08 20:02:30 -05:00
|
|
|
artifactCommands: ArtifactCommand[]
|
2025-01-17 14:34:36 -05:00
|
|
|
artifactGraph: ArtifactGraph
|
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 {
|
|
|
|
memory: ProgramMemory.empty(),
|
2024-12-20 16:19:59 -05:00
|
|
|
operations: [],
|
2025-01-08 20:02:30 -05:00
|
|
|
artifacts: {},
|
|
|
|
artifactCommands: [],
|
2025-01-17 14:34:36 -05:00
|
|
|
artifactGraph: defaultArtifactGraph(),
|
2024-10-09 19:38:40 -04:00
|
|
|
}
|
|
|
|
}
|
2024-07-22 19:43:40 -04:00
|
|
|
|
2025-01-17 14:34:36 -05:00
|
|
|
function execStateFromRust(
|
|
|
|
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.
|
|
|
|
for (const [id, artifact] of artifactGraph) {
|
|
|
|
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-01-06 16:55:59 -05:00
|
|
|
memory: ProgramMemory.fromRaw(execOutcome.memory),
|
|
|
|
operations: execOutcome.operations,
|
2025-01-08 20:02:30 -05:00
|
|
|
artifacts: execOutcome.artifacts,
|
|
|
|
artifactCommands: execOutcome.artifactCommands,
|
2025-01-17 14:34:36 -05:00
|
|
|
artifactGraph,
|
2024-10-09 19:38:40 -04:00
|
|
|
}
|
2024-07-22 19:43:40 -04: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
|
|
|
|
}
|
|
|
|
|
|
|
|
export function defaultArtifactGraph(): ArtifactGraph {
|
|
|
|
return new Map()
|
|
|
|
}
|
|
|
|
|
2024-10-09 19:38:40 -04:00
|
|
|
interface Memory {
|
|
|
|
[key: string]: KclValue | undefined
|
2024-07-22 19:43:40 -04:00
|
|
|
}
|
|
|
|
|
2024-10-09 19:38:40 -04:00
|
|
|
const ROOT_ENVIRONMENT_REF: EnvironmentRef = 0
|
|
|
|
|
|
|
|
function emptyEnvironment(): Environment {
|
|
|
|
return { bindings: {}, parent: null }
|
2023-09-29 11:11:01 -07:00
|
|
|
}
|
|
|
|
|
2024-12-05 19:51:06 -08:00
|
|
|
function emptyRootEnvironment(): Environment {
|
|
|
|
return {
|
|
|
|
// This is dumb this is copied from rust.
|
|
|
|
bindings: {
|
|
|
|
ZERO: { type: 'Number', value: 0.0, __meta: [] },
|
|
|
|
QUARTER_TURN: { type: 'Number', value: 90.0, __meta: [] },
|
|
|
|
HALF_TURN: { type: 'Number', value: 180.0, __meta: [] },
|
|
|
|
THREE_QUARTER_TURN: { type: 'Number', value: 270.0, __meta: [] },
|
|
|
|
},
|
|
|
|
parent: null,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-07-22 19:43:40 -04:00
|
|
|
/**
|
|
|
|
* This duplicates logic in Rust. The hope is to keep ProgramMemory internals
|
|
|
|
* isolated from the rest of the TypeScript code so that we can move it to Rust
|
|
|
|
* in the future.
|
|
|
|
*/
|
|
|
|
export class ProgramMemory {
|
|
|
|
private environments: Environment[]
|
|
|
|
private currentEnv: EnvironmentRef
|
2024-08-12 17:55:05 -05:00
|
|
|
private return: KclValue | null
|
2024-07-22 19:43:40 -04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Empty memory doesn't include prelude definitions.
|
|
|
|
*/
|
|
|
|
static empty(): ProgramMemory {
|
|
|
|
return new ProgramMemory()
|
|
|
|
}
|
|
|
|
|
|
|
|
static fromRaw(raw: RawProgramMemory): ProgramMemory {
|
|
|
|
return new ProgramMemory(raw.environments, raw.currentEnv, raw.return)
|
|
|
|
}
|
|
|
|
|
|
|
|
constructor(
|
2024-12-05 19:51:06 -08:00
|
|
|
environments: Environment[] = [emptyRootEnvironment()],
|
2024-07-22 19:43:40 -04:00
|
|
|
currentEnv: EnvironmentRef = ROOT_ENVIRONMENT_REF,
|
2024-08-12 17:55:05 -05:00
|
|
|
returnVal: KclValue | null = null
|
2024-07-22 19:43:40 -04:00
|
|
|
) {
|
|
|
|
this.environments = environments
|
|
|
|
this.currentEnv = currentEnv
|
|
|
|
this.return = returnVal
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns a deep copy.
|
|
|
|
*/
|
|
|
|
clone(): ProgramMemory {
|
2024-07-25 20:11:46 -04:00
|
|
|
return ProgramMemory.fromRaw(structuredClone(this.toRaw()))
|
2024-07-22 19:43:40 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
has(name: string): boolean {
|
|
|
|
let envRef = this.currentEnv
|
|
|
|
while (true) {
|
|
|
|
const env = this.environments[envRef]
|
|
|
|
if (env.bindings.hasOwnProperty(name)) {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
if (!env.parent) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
envRef = env.parent
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2024-08-12 16:53:24 -05:00
|
|
|
get(name: string): KclValue | null {
|
2024-07-22 19:43:40 -04:00
|
|
|
let envRef = this.currentEnv
|
|
|
|
while (true) {
|
|
|
|
const env = this.environments[envRef]
|
|
|
|
if (env.bindings.hasOwnProperty(name)) {
|
2024-10-09 19:38:40 -04:00
|
|
|
return env.bindings[name] ?? null
|
2024-07-22 19:43:40 -04:00
|
|
|
}
|
|
|
|
if (!env.parent) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
envRef = env.parent
|
|
|
|
}
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
2024-08-12 16:53:24 -05:00
|
|
|
set(name: string, value: KclValue): Error | null {
|
2024-07-22 19:43:40 -04:00
|
|
|
if (this.environments.length === 0) {
|
|
|
|
return new Error('No environment to set memory in')
|
|
|
|
}
|
|
|
|
const env = this.environments[this.currentEnv]
|
|
|
|
env.bindings[name] = value
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-08-12 16:53:24 -05:00
|
|
|
* Returns a new ProgramMemory with only `KclValue`s that pass the
|
2024-07-22 19:43:40 -04:00
|
|
|
* predicate. Values are deep copied.
|
|
|
|
*
|
|
|
|
* Note: Return value of the returned ProgramMemory is always null.
|
|
|
|
*/
|
|
|
|
filterVariables(
|
|
|
|
keepPrelude: boolean,
|
2024-08-12 16:53:24 -05:00
|
|
|
predicate: (value: KclValue) => boolean
|
2024-07-22 19:43:40 -04:00
|
|
|
): ProgramMemory | Error {
|
|
|
|
const environments: Environment[] = []
|
|
|
|
for (const [i, env] of this.environments.entries()) {
|
|
|
|
let bindings: Memory
|
|
|
|
if (i === ROOT_ENVIRONMENT_REF && keepPrelude) {
|
|
|
|
// Get prelude definitions. Create these first so that they're always
|
|
|
|
// first in iteration order.
|
|
|
|
const memoryOrError = programMemoryInit()
|
|
|
|
if (err(memoryOrError)) return memoryOrError
|
|
|
|
bindings = memoryOrError.environments[0].bindings
|
|
|
|
} else {
|
|
|
|
bindings = emptyEnvironment().bindings
|
|
|
|
}
|
|
|
|
|
|
|
|
for (const [name, value] of Object.entries(env.bindings)) {
|
2024-10-09 19:38:40 -04:00
|
|
|
if (value === undefined) continue
|
2024-07-22 19:43:40 -04:00
|
|
|
// Check the predicate.
|
|
|
|
if (!predicate(value)) {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
// Deep copy.
|
2024-07-25 20:11:46 -04:00
|
|
|
bindings[name] = structuredClone(value)
|
2024-07-22 19:43:40 -04:00
|
|
|
}
|
|
|
|
environments.push({ bindings, parent: env.parent })
|
|
|
|
}
|
|
|
|
return new ProgramMemory(environments, this.currentEnv, null)
|
|
|
|
}
|
|
|
|
|
|
|
|
numEnvironments(): number {
|
|
|
|
return this.environments.length
|
|
|
|
}
|
|
|
|
|
|
|
|
numVariables(envRef: EnvironmentRef): number {
|
|
|
|
return Object.keys(this.environments[envRef]).length
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns all variable entries in memory that are visible, in a flat
|
|
|
|
* structure. If variables are shadowed, they're not visible, and therefore,
|
|
|
|
* not included.
|
|
|
|
*
|
|
|
|
* This should only be used to display in the MemoryPane UI.
|
|
|
|
*/
|
2024-08-12 16:53:24 -05:00
|
|
|
visibleEntries(): Map<string, KclValue> {
|
|
|
|
const map = new Map<string, KclValue>()
|
2024-07-22 19:43:40 -04:00
|
|
|
let envRef = this.currentEnv
|
|
|
|
while (true) {
|
|
|
|
const env = this.environments[envRef]
|
|
|
|
for (const [name, value] of Object.entries(env.bindings)) {
|
2024-10-09 19:38:40 -04:00
|
|
|
if (value === undefined) continue
|
2024-07-22 19:43:40 -04:00
|
|
|
// Don't include shadowed variables.
|
|
|
|
if (!map.has(name)) {
|
|
|
|
map.set(name, value)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!env.parent) {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
envRef = env.parent
|
|
|
|
}
|
|
|
|
return map
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2024-09-27 15:44:44 -07:00
|
|
|
* Returns true if any visible variables are a Sketch or Solid.
|
2024-07-22 19:43:40 -04:00
|
|
|
*/
|
2024-09-27 15:44:44 -07:00
|
|
|
hasSketchOrSolid(): boolean {
|
2024-07-22 19:43:40 -04:00
|
|
|
for (const node of this.visibleEntries().values()) {
|
2024-11-14 17:27:19 -06:00
|
|
|
if (node.type === 'Solid' || node.type === 'Sketch') {
|
2024-07-22 19:43:40 -04:00
|
|
|
return true
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return the representation that can be serialized to JSON. This should only
|
|
|
|
* be used within this module.
|
|
|
|
*/
|
|
|
|
toRaw(): RawProgramMemory {
|
|
|
|
return {
|
|
|
|
environments: this.environments,
|
|
|
|
currentEnv: this.currentEnv,
|
|
|
|
return: this.return,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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
|
|
|
}
|
|
|
|
|
2023-09-29 11:11:01 -07:00
|
|
|
export const executor = async (
|
2024-10-30 16:52:17 -04:00
|
|
|
node: Node<Program>,
|
2024-03-26 19:32:31 -07:00
|
|
|
engineCommandManager: EngineCommandManager,
|
2024-12-05 19:51:06 -08:00
|
|
|
programMemoryOverride: ProgramMemory | Error | null = null
|
2024-10-09 19:38:40 -04:00
|
|
|
): Promise<ExecState> => {
|
2024-12-05 19:51:06 -08:00
|
|
|
if (programMemoryOverride !== null && err(programMemoryOverride))
|
|
|
|
return Promise.reject(programMemoryOverride)
|
2024-06-24 11:45:40 -04:00
|
|
|
|
2024-12-16 10:34:11 -05:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-floating-promises
|
|
|
|
if (programMemoryOverride !== null && err(programMemoryOverride))
|
|
|
|
return Promise.reject(programMemoryOverride)
|
|
|
|
|
2023-09-29 11:11:01 -07:00
|
|
|
try {
|
2024-12-10 18:50:22 -08:00
|
|
|
let jsAppSettings = default_app_settings()
|
2024-04-19 14:24:40 -07:00
|
|
|
if (!TEST) {
|
2024-12-10 18:50:22 -08:00
|
|
|
const lastSettingsSnapshot = await import(
|
|
|
|
'components/SettingsAuthProvider'
|
|
|
|
).then((module) => module.lastSettingsContextSnapshot)
|
|
|
|
if (lastSettingsSnapshot) {
|
|
|
|
jsAppSettings = getAllCurrentSettings(lastSettingsSnapshot)
|
|
|
|
}
|
2024-04-19 14:24:40 -07:00
|
|
|
}
|
2025-01-06 16:55:59 -05:00
|
|
|
const execOutcome: RustExecOutcome = await execute(
|
2023-09-29 11:11:01 -07:00
|
|
|
JSON.stringify(node),
|
2024-12-05 19:51:06 -08:00
|
|
|
JSON.stringify(programMemoryOverride?.toRaw() || null),
|
2024-12-10 18:50:22 -08:00
|
|
|
JSON.stringify({ settings: jsAppSettings }),
|
2024-02-12 12:18:37 -08:00
|
|
|
engineCommandManager,
|
2024-12-05 19:51:06 -08:00
|
|
|
fileSystemManager
|
2023-09-29 11:11:01 -07:00
|
|
|
)
|
2025-01-17 14:34:36 -05:00
|
|
|
return execStateFromRust(execOutcome, node)
|
2023-09-29 11:11:01 -07:00
|
|
|
} catch (e: any) {
|
2024-02-11 15:08:54 -08:00
|
|
|
console.log(e)
|
2024-12-16 13:10:31 -05:00
|
|
|
const parsed: KclErrorWithOutputs = JSON.parse(e.toString())
|
2023-09-29 11:11:01 -07:00
|
|
|
const kclError = new KCLError(
|
2024-12-16 13:10:31 -05:00
|
|
|
parsed.error.kind,
|
|
|
|
parsed.error.msg,
|
|
|
|
sourceRangeFromRust(parsed.error.sourceRanges[0]),
|
2025-01-08 20:02:30 -05:00
|
|
|
parsed.operations,
|
2025-01-17 14:34:36 -05:00
|
|
|
parsed.artifactCommands,
|
|
|
|
rustArtifactGraphToMap(parsed.artifactGraph)
|
2023-09-29 11:11:01 -07:00
|
|
|
)
|
|
|
|
|
2024-06-24 11:45:40 -04:00
|
|
|
return Promise.reject(kclError)
|
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
|
|
|
}
|
|
|
|
|
2024-04-15 17:18:32 -07:00
|
|
|
export const makeDefaultPlanes = async (
|
|
|
|
engineCommandManager: EngineCommandManager
|
|
|
|
): Promise<DefaultPlanes> => {
|
|
|
|
try {
|
|
|
|
const planes: DefaultPlanes = await make_default_planes(
|
|
|
|
engineCommandManager
|
|
|
|
)
|
|
|
|
return planes
|
|
|
|
} catch (e) {
|
|
|
|
// TODO: do something real with the error.
|
|
|
|
console.log('make default planes error', e)
|
2024-06-24 11:45:40 -04:00
|
|
|
return Promise.reject(e)
|
2024-04-15 17:18:32 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-29 11:11:01 -07:00
|
|
|
export const modifyAstForSketch = async (
|
|
|
|
engineCommandManager: EngineCommandManager,
|
2024-10-30 16:52:17 -04:00
|
|
|
ast: Node<Program>,
|
2023-09-29 11:11:01 -07:00
|
|
|
variableName: string,
|
2023-10-05 14:27:48 -07:00
|
|
|
currentPlane: string,
|
2023-09-29 11:11:01 -07:00
|
|
|
engineId: string
|
2024-10-30 16:52:17 -04:00
|
|
|
): Promise<Node<Program>> => {
|
2023-09-29 11:11:01 -07:00
|
|
|
try {
|
2024-10-30 16:52:17 -04:00
|
|
|
const updatedAst: Node<Program> = await modify_ast_for_sketch_wasm(
|
2023-09-29 11:11:01 -07:00
|
|
|
engineCommandManager,
|
|
|
|
JSON.stringify(ast),
|
|
|
|
variableName,
|
2023-10-05 14:27:48 -07:00
|
|
|
JSON.stringify(currentPlane),
|
2023-09-29 11:11:01 -07:00
|
|
|
engineId
|
|
|
|
)
|
|
|
|
|
|
|
|
return updatedAst
|
|
|
|
} catch (e: any) {
|
|
|
|
const parsed: RustKclError = JSON.parse(e.toString())
|
|
|
|
const kclError = new KCLError(
|
|
|
|
parsed.kind,
|
|
|
|
parsed.msg,
|
2024-12-16 13:10:31 -05:00
|
|
|
sourceRangeFromRust(parsed.sourceRanges[0]),
|
2025-01-08 20:02:30 -05:00
|
|
|
[],
|
2025-01-17 14:34:36 -05:00
|
|
|
[],
|
|
|
|
defaultArtifactGraph()
|
2023-09-29 11:11:01 -07:00
|
|
|
)
|
|
|
|
|
|
|
|
console.log(kclError)
|
2024-06-24 11:45:40 -04:00
|
|
|
return Promise.reject(kclError)
|
2023-09-29 11:11:01 -07:00
|
|
|
}
|
|
|
|
}
|
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-07-22 19:43:40 -04:00
|
|
|
/**
|
|
|
|
* Returns new ProgramMemory with prelude definitions.
|
|
|
|
*/
|
2024-06-24 11:45:40 -04:00
|
|
|
export function programMemoryInit(): ProgramMemory | Error {
|
2024-02-11 18:26:09 -08:00
|
|
|
try {
|
2024-07-22 19:43:40 -04:00
|
|
|
const memory: RawProgramMemory = program_memory_init()
|
|
|
|
return new ProgramMemory(
|
|
|
|
memory.environments,
|
|
|
|
memory.currentEnv,
|
|
|
|
memory.return
|
|
|
|
)
|
2024-02-11 18:26:09 -08:00
|
|
|
} catch (e: any) {
|
|
|
|
console.log(e)
|
|
|
|
const parsed: RustKclError = JSON.parse(e.toString())
|
2024-06-24 11:45:40 -04:00
|
|
|
return new KCLError(
|
2024-02-11 18:26:09 -08:00
|
|
|
parsed.kind,
|
|
|
|
parsed.msg,
|
2024-12-16 13:10:31 -05:00
|
|
|
sourceRangeFromRust(parsed.sourceRanges[0]),
|
2025-01-08 20:02:30 -05:00
|
|
|
[],
|
2025-01-17 14:34:36 -05:00
|
|
|
[],
|
|
|
|
defaultArtifactGraph()
|
2024-02-11 18:26:09 -08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
2024-03-11 17:50:31 -07: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-06-24 11:45:40 -04:00
|
|
|
export function tomlStringify(toml: any): string | Error {
|
|
|
|
return toml_stringify(JSON.stringify(toml))
|
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-12-06 14:56:53 -08:00
|
|
|
export async function clearSceneAndBustCache(
|
|
|
|
engineCommandManager: EngineCommandManager
|
|
|
|
): Promise<null | Error> {
|
|
|
|
try {
|
|
|
|
await clear_scene_and_bust_cache(engineCommandManager)
|
|
|
|
} catch (e: any) {
|
|
|
|
console.error('clear_scene_and_bust_cache: error', e)
|
|
|
|
return Promise.reject(
|
|
|
|
new Error(`Error on clear_scene_and_bust_cache: ${e}`)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
return null
|
|
|
|
}
|
|
|
|
|
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) {
|
|
|
|
console.error('Caught error decoding base64 string: ' + e)
|
|
|
|
return new Error('Caught error decoding base64 string: ' + e)
|
|
|
|
}
|
|
|
|
}
|