Fix KCL source ranges to know which source file they point to (#4418)
* Add ts_rs feature to work with indexmap * Add feature for schemars to work with indexmap * Add module ID to intern module paths * Update code to use new source range with three fields * Update generated files * Update docs * Fix wasm * Fix TS code to use new SourceRange * Fix TS tests to use new SourceRange and moduleId * Fix formatting * Fix to filter errors and source ranges to only show the top-level module * Fix to reuse module IDs * Fix to disallow empty path for import * Revert unneeded Self change * Rename field to be clearer * Fix parser tests * Update snapshots * Change to not serialize module_id of 0 * Update snapshots after adding default module_id * Move module_id functions to separate module * Fix tests for console errors * Proposal: module ID = 0 gets skipped when serializing tokens too (#4422) Just like in AST nodes. Also I think "is_top_level" communicates intention better than is_default --------- Co-authored-by: Adam Chalmers <adam.chalmers@zoo.dev>
This commit is contained in:
		
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -18,7 +18,7 @@ export const isErrorWhitelisted = (exception: Error) => {
 | 
			
		||||
    {
 | 
			
		||||
      name: '"{"kind"',
 | 
			
		||||
      message:
 | 
			
		||||
        '"engine","sourceRanges":[[0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}"',
 | 
			
		||||
        '"engine","sourceRanges":[[0,0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}"',
 | 
			
		||||
      stack: '',
 | 
			
		||||
      foundInSpec: 'e2e/playwright/testing-settings.spec.ts',
 | 
			
		||||
      project: 'Google Chrome',
 | 
			
		||||
@ -156,8 +156,8 @@ export const isErrorWhitelisted = (exception: Error) => {
 | 
			
		||||
    {
 | 
			
		||||
      name: 'Unhandled Promise Rejection',
 | 
			
		||||
      message:
 | 
			
		||||
        '{"kind":"engine","sourceRanges":[[0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}',
 | 
			
		||||
      stack: `Unhandled Promise Rejection: {"kind":"engine","sourceRanges":[[0,0]],"msg":"Failed to get string from response from engine: \`JsValue(undefined)\`"}
 | 
			
		||||
        '{"kind":"engine","sourceRanges":[[0,0,0]],"msg":"Failed to get string from response from engine: `JsValue(undefined)`"}',
 | 
			
		||||
      stack: `Unhandled Promise Rejection: {"kind":"engine","sourceRanges":[[0,0,0]],"msg":"Failed to get string from response from engine: \`JsValue(undefined)\`"}
 | 
			
		||||
    at unknown (http://localhost:3000/src/lang/std/engineConnection.ts:1245:26)`,
 | 
			
		||||
      foundInSpec:
 | 
			
		||||
        'e2e/playwright/onboarding-tests.spec.ts Click through each onboarding step',
 | 
			
		||||
@ -253,7 +253,7 @@ export const isErrorWhitelisted = (exception: Error) => {
 | 
			
		||||
    {
 | 
			
		||||
      name: '{"kind"',
 | 
			
		||||
      stack: ``,
 | 
			
		||||
      message: `engine","sourceRanges":[[0,0]],"msg":"Failed to wait for promise from engine: JsValue(\\"Force interrupt, executionIsStale, new AST requested\\")"}`,
 | 
			
		||||
      message: `engine","sourceRanges":[[0,0,0]],"msg":"Failed to wait for promise from engine: JsValue(\\"Force interrupt, executionIsStale, new AST requested\\")"}`,
 | 
			
		||||
      project: 'Google Chrome',
 | 
			
		||||
      foundInSpec: 'e2e/playwright/testing-settings.spec.ts',
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@ -43,14 +43,14 @@ describe('processMemory', () => {
 | 
			
		||||
          tag: null,
 | 
			
		||||
          id: expect.any(String),
 | 
			
		||||
          faceId: expect.any(String),
 | 
			
		||||
          sourceRange: [170, 194],
 | 
			
		||||
          sourceRange: [170, 194, 0],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: 'extrudePlane',
 | 
			
		||||
          tag: null,
 | 
			
		||||
          id: expect.any(String),
 | 
			
		||||
          faceId: expect.any(String),
 | 
			
		||||
          sourceRange: [202, 230],
 | 
			
		||||
          sourceRange: [202, 230, 0],
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      theSketch: [
 | 
			
		||||
 | 
			
		||||
@ -38,6 +38,7 @@ export class KclManager {
 | 
			
		||||
    body: [],
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
    nonCodeMeta: {
 | 
			
		||||
      nonCodeNodes: {},
 | 
			
		||||
      startNodes: [],
 | 
			
		||||
@ -204,6 +205,7 @@ export class KclManager {
 | 
			
		||||
      body: [],
 | 
			
		||||
      start: 0,
 | 
			
		||||
      end: 0,
 | 
			
		||||
      moduleId: 0,
 | 
			
		||||
      nonCodeMeta: {
 | 
			
		||||
        nonCodeNodes: {},
 | 
			
		||||
        startNodes: [],
 | 
			
		||||
 | 
			
		||||
@ -1903,6 +1903,6 @@ describe('parsing errors', () => {
 | 
			
		||||
    const error = result as KCLError
 | 
			
		||||
    expect(error.kind).toBe('syntax')
 | 
			
		||||
    expect(error.msg).toBe('Unexpected token: (')
 | 
			
		||||
    expect(error.sourceRanges).toEqual([[27, 28]])
 | 
			
		||||
    expect(error.sourceRanges).toEqual([[27, 28, 0]])
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
@ -19,7 +19,7 @@ const mySketch001 = startSketchOn('XY')
 | 
			
		||||
    const sketch001 = execState.memory.get('mySketch001')
 | 
			
		||||
    expect(sketch001).toEqual({
 | 
			
		||||
      type: 'UserVal',
 | 
			
		||||
      __meta: [{ sourceRange: [46, 71] }],
 | 
			
		||||
      __meta: [{ sourceRange: [46, 71, 0] }],
 | 
			
		||||
      value: {
 | 
			
		||||
        type: 'Sketch',
 | 
			
		||||
        on: expect.any(Object),
 | 
			
		||||
@ -29,7 +29,7 @@ const mySketch001 = startSketchOn('XY')
 | 
			
		||||
          tag: null,
 | 
			
		||||
          __geoMeta: {
 | 
			
		||||
            id: expect.any(String),
 | 
			
		||||
            sourceRange: [46, 71],
 | 
			
		||||
            sourceRange: [46, 71, 0],
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        paths: [
 | 
			
		||||
@ -39,7 +39,7 @@ const mySketch001 = startSketchOn('XY')
 | 
			
		||||
            to: [-1.59, -1.54],
 | 
			
		||||
            from: [0, 0],
 | 
			
		||||
            __geoMeta: {
 | 
			
		||||
              sourceRange: [77, 102],
 | 
			
		||||
              sourceRange: [77, 102, 0],
 | 
			
		||||
              id: expect.any(String),
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
@ -49,13 +49,13 @@ const mySketch001 = startSketchOn('XY')
 | 
			
		||||
            from: [-1.59, -1.54],
 | 
			
		||||
            tag: null,
 | 
			
		||||
            __geoMeta: {
 | 
			
		||||
              sourceRange: [108, 132],
 | 
			
		||||
              sourceRange: [108, 132, 0],
 | 
			
		||||
              id: expect.any(String),
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        id: expect.any(String),
 | 
			
		||||
        __meta: [{ sourceRange: [46, 71] }],
 | 
			
		||||
        __meta: [{ sourceRange: [46, 71, 0] }],
 | 
			
		||||
      },
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
@ -80,14 +80,14 @@ const mySketch001 = startSketchOn('XY')
 | 
			
		||||
          faceId: expect.any(String),
 | 
			
		||||
          tag: null,
 | 
			
		||||
          id: expect.any(String),
 | 
			
		||||
          sourceRange: [77, 102],
 | 
			
		||||
          sourceRange: [77, 102, 0],
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          type: 'extrudePlane',
 | 
			
		||||
          faceId: expect.any(String),
 | 
			
		||||
          tag: null,
 | 
			
		||||
          id: expect.any(String),
 | 
			
		||||
          sourceRange: [108, 132],
 | 
			
		||||
          sourceRange: [108, 132, 0],
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
      sketch: {
 | 
			
		||||
@ -104,7 +104,7 @@ const mySketch001 = startSketchOn('XY')
 | 
			
		||||
            tag: null,
 | 
			
		||||
            __geoMeta: {
 | 
			
		||||
              id: expect.any(String),
 | 
			
		||||
              sourceRange: [77, 102],
 | 
			
		||||
              sourceRange: [77, 102, 0],
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
@ -114,7 +114,7 @@ const mySketch001 = startSketchOn('XY')
 | 
			
		||||
            tag: null,
 | 
			
		||||
            __geoMeta: {
 | 
			
		||||
              id: expect.any(String),
 | 
			
		||||
              sourceRange: [108, 132],
 | 
			
		||||
              sourceRange: [108, 132, 0],
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
@ -122,7 +122,7 @@ const mySketch001 = startSketchOn('XY')
 | 
			
		||||
      height: 2,
 | 
			
		||||
      startCapId: expect.any(String),
 | 
			
		||||
      endCapId: expect.any(String),
 | 
			
		||||
      __meta: [{ sourceRange: [46, 71] }],
 | 
			
		||||
      __meta: [{ sourceRange: [46, 71, 0] }],
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
  test('sketch extrude and sketch on one of the faces', async () => {
 | 
			
		||||
@ -162,7 +162,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
            faceId: expect.any(String),
 | 
			
		||||
            tag: null,
 | 
			
		||||
            id: expect.any(String),
 | 
			
		||||
            sourceRange: [69, 89],
 | 
			
		||||
            sourceRange: [69, 89, 0],
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            type: 'extrudePlane',
 | 
			
		||||
@ -174,14 +174,14 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
              value: 'p',
 | 
			
		||||
            },
 | 
			
		||||
            id: expect.any(String),
 | 
			
		||||
            sourceRange: [95, 117],
 | 
			
		||||
            sourceRange: [95, 117, 0],
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            type: 'extrudePlane',
 | 
			
		||||
            faceId: expect.any(String),
 | 
			
		||||
            tag: null,
 | 
			
		||||
            id: expect.any(String),
 | 
			
		||||
            sourceRange: [123, 142],
 | 
			
		||||
            sourceRange: [123, 142, 0],
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        sketch: {
 | 
			
		||||
@ -194,7 +194,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
            p: {
 | 
			
		||||
              __meta: [
 | 
			
		||||
                {
 | 
			
		||||
                  sourceRange: [114, 116],
 | 
			
		||||
                  sourceRange: [114, 116, 0],
 | 
			
		||||
                },
 | 
			
		||||
              ],
 | 
			
		||||
              type: 'TagIdentifier',
 | 
			
		||||
@ -210,7 +210,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
              tag: null,
 | 
			
		||||
              __geoMeta: {
 | 
			
		||||
                id: expect.any(String),
 | 
			
		||||
                sourceRange: [69, 89],
 | 
			
		||||
                sourceRange: [69, 89, 0],
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
@ -225,7 +225,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
              },
 | 
			
		||||
              __geoMeta: {
 | 
			
		||||
                id: expect.any(String),
 | 
			
		||||
                sourceRange: [95, 117],
 | 
			
		||||
                sourceRange: [95, 117, 0],
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
@ -235,7 +235,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
              tag: null,
 | 
			
		||||
              __geoMeta: {
 | 
			
		||||
                id: expect.any(String),
 | 
			
		||||
                sourceRange: [123, 142],
 | 
			
		||||
                sourceRange: [123, 142, 0],
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
@ -243,7 +243,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
        height: 2,
 | 
			
		||||
        startCapId: expect.any(String),
 | 
			
		||||
        endCapId: expect.any(String),
 | 
			
		||||
        __meta: [{ sourceRange: [38, 63] }],
 | 
			
		||||
        __meta: [{ sourceRange: [38, 63, 0] }],
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
        type: 'Solid',
 | 
			
		||||
@ -254,7 +254,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
            faceId: expect.any(String),
 | 
			
		||||
            tag: null,
 | 
			
		||||
            id: expect.any(String),
 | 
			
		||||
            sourceRange: [373, 393],
 | 
			
		||||
            sourceRange: [373, 393, 0],
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            type: 'extrudePlane',
 | 
			
		||||
@ -266,14 +266,14 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
              value: 'o',
 | 
			
		||||
            },
 | 
			
		||||
            id: expect.any(String),
 | 
			
		||||
            sourceRange: [399, 420],
 | 
			
		||||
            sourceRange: [399, 420, 0],
 | 
			
		||||
          },
 | 
			
		||||
          {
 | 
			
		||||
            type: 'extrudePlane',
 | 
			
		||||
            faceId: expect.any(String),
 | 
			
		||||
            tag: null,
 | 
			
		||||
            id: expect.any(String),
 | 
			
		||||
            sourceRange: [426, 445],
 | 
			
		||||
            sourceRange: [426, 445, 0],
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        sketch: {
 | 
			
		||||
@ -286,7 +286,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
            o: {
 | 
			
		||||
              __meta: [
 | 
			
		||||
                {
 | 
			
		||||
                  sourceRange: [417, 419],
 | 
			
		||||
                  sourceRange: [417, 419, 0],
 | 
			
		||||
                },
 | 
			
		||||
              ],
 | 
			
		||||
              type: 'TagIdentifier',
 | 
			
		||||
@ -302,7 +302,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
              tag: null,
 | 
			
		||||
              __geoMeta: {
 | 
			
		||||
                id: expect.any(String),
 | 
			
		||||
                sourceRange: [373, 393],
 | 
			
		||||
                sourceRange: [373, 393, 0],
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
@ -317,7 +317,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
              },
 | 
			
		||||
              __geoMeta: {
 | 
			
		||||
                id: expect.any(String),
 | 
			
		||||
                sourceRange: [399, 420],
 | 
			
		||||
                sourceRange: [399, 420, 0],
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
            {
 | 
			
		||||
@ -327,7 +327,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
              tag: null,
 | 
			
		||||
              __geoMeta: {
 | 
			
		||||
                id: expect.any(String),
 | 
			
		||||
                sourceRange: [426, 445],
 | 
			
		||||
                sourceRange: [426, 445, 0],
 | 
			
		||||
              },
 | 
			
		||||
            },
 | 
			
		||||
          ],
 | 
			
		||||
@ -335,7 +335,7 @@ const sk2 = startSketchOn('XY')
 | 
			
		||||
        height: 2,
 | 
			
		||||
        startCapId: expect.any(String),
 | 
			
		||||
        endCapId: expect.any(String),
 | 
			
		||||
        __meta: [{ sourceRange: [342, 367] }],
 | 
			
		||||
        __meta: [{ sourceRange: [342, 367, 0] }],
 | 
			
		||||
      },
 | 
			
		||||
    ])
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@ -9,8 +9,8 @@ describe('test kclErrToDiagnostic', () => {
 | 
			
		||||
        kind: 'semantic',
 | 
			
		||||
        msg: 'Semantic error',
 | 
			
		||||
        sourceRanges: [
 | 
			
		||||
          [0, 1],
 | 
			
		||||
          [2, 3],
 | 
			
		||||
          [0, 1, 0],
 | 
			
		||||
          [2, 3, 0],
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
      {
 | 
			
		||||
@ -19,8 +19,8 @@ describe('test kclErrToDiagnostic', () => {
 | 
			
		||||
        kind: 'type',
 | 
			
		||||
        msg: 'Type error',
 | 
			
		||||
        sourceRanges: [
 | 
			
		||||
          [4, 5],
 | 
			
		||||
          [6, 7],
 | 
			
		||||
          [4, 5, 0],
 | 
			
		||||
          [6, 7, 0],
 | 
			
		||||
        ],
 | 
			
		||||
      },
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@ -4,15 +4,17 @@ import { posToOffset } from '@kittycad/codemirror-lsp-client'
 | 
			
		||||
import { Diagnostic as LspDiagnostic } from 'vscode-languageserver-protocol'
 | 
			
		||||
import { Text } from '@codemirror/state'
 | 
			
		||||
 | 
			
		||||
const TOP_LEVEL_MODULE_ID = 0
 | 
			
		||||
 | 
			
		||||
type ExtractKind<T> = T extends { kind: infer K } ? K : never
 | 
			
		||||
export class KCLError extends Error {
 | 
			
		||||
  kind: ExtractKind<RustKclError> | 'name'
 | 
			
		||||
  sourceRanges: [number, number][]
 | 
			
		||||
  sourceRanges: [number, number, number][]
 | 
			
		||||
  msg: string
 | 
			
		||||
  constructor(
 | 
			
		||||
    kind: ExtractKind<RustKclError> | 'name',
 | 
			
		||||
    msg: string,
 | 
			
		||||
    sourceRanges: [number, number][]
 | 
			
		||||
    sourceRanges: [number, number, number][]
 | 
			
		||||
  ) {
 | 
			
		||||
    super()
 | 
			
		||||
    this.kind = kind
 | 
			
		||||
@ -23,63 +25,63 @@ export class KCLError extends Error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class KCLLexicalError extends KCLError {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number][]) {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number, number][]) {
 | 
			
		||||
    super('lexical', msg, sourceRanges)
 | 
			
		||||
    Object.setPrototypeOf(this, KCLSyntaxError.prototype)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class KCLInternalError extends KCLError {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number][]) {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number, number][]) {
 | 
			
		||||
    super('internal', msg, sourceRanges)
 | 
			
		||||
    Object.setPrototypeOf(this, KCLSyntaxError.prototype)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class KCLSyntaxError extends KCLError {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number][]) {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number, number][]) {
 | 
			
		||||
    super('syntax', msg, sourceRanges)
 | 
			
		||||
    Object.setPrototypeOf(this, KCLSyntaxError.prototype)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class KCLSemanticError extends KCLError {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number][]) {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number, number][]) {
 | 
			
		||||
    super('semantic', msg, sourceRanges)
 | 
			
		||||
    Object.setPrototypeOf(this, KCLSemanticError.prototype)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class KCLTypeError extends KCLError {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number][]) {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number, number][]) {
 | 
			
		||||
    super('type', msg, sourceRanges)
 | 
			
		||||
    Object.setPrototypeOf(this, KCLTypeError.prototype)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class KCLUnimplementedError extends KCLError {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number][]) {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number, number][]) {
 | 
			
		||||
    super('unimplemented', msg, sourceRanges)
 | 
			
		||||
    Object.setPrototypeOf(this, KCLUnimplementedError.prototype)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class KCLUnexpectedError extends KCLError {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number][]) {
 | 
			
		||||
  constructor(msg: string, sourceRanges: [number, number, number][]) {
 | 
			
		||||
    super('unexpected', msg, sourceRanges)
 | 
			
		||||
    Object.setPrototypeOf(this, KCLUnexpectedError.prototype)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class KCLValueAlreadyDefined extends KCLError {
 | 
			
		||||
  constructor(key: string, sourceRanges: [number, number][]) {
 | 
			
		||||
  constructor(key: string, sourceRanges: [number, number, number][]) {
 | 
			
		||||
    super('name', `Key ${key} was already defined elsewhere`, sourceRanges)
 | 
			
		||||
    Object.setPrototypeOf(this, KCLValueAlreadyDefined.prototype)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export class KCLUndefinedValueError extends KCLError {
 | 
			
		||||
  constructor(key: string, sourceRanges: [number, number][]) {
 | 
			
		||||
  constructor(key: string, sourceRanges: [number, number, number][]) {
 | 
			
		||||
    super('name', `Key ${key} has not been defined`, sourceRanges)
 | 
			
		||||
    Object.setPrototypeOf(this, KCLUndefinedValueError.prototype)
 | 
			
		||||
  }
 | 
			
		||||
@ -97,13 +99,22 @@ export function lspDiagnosticsToKclErrors(
 | 
			
		||||
    .flatMap(
 | 
			
		||||
      ({ range, message }) =>
 | 
			
		||||
        new KCLError('unexpected', message, [
 | 
			
		||||
          [posToOffset(doc, range.start)!, posToOffset(doc, range.end)!],
 | 
			
		||||
          [
 | 
			
		||||
            posToOffset(doc, range.start)!,
 | 
			
		||||
            posToOffset(doc, range.end)!,
 | 
			
		||||
            TOP_LEVEL_MODULE_ID,
 | 
			
		||||
          ],
 | 
			
		||||
        ])
 | 
			
		||||
    )
 | 
			
		||||
    .filter(({ sourceRanges }) => {
 | 
			
		||||
      const [from, to] = sourceRanges[0]
 | 
			
		||||
      const [from, to, moduleId] = sourceRanges[0]
 | 
			
		||||
      return (
 | 
			
		||||
        from !== null && to !== null && from !== undefined && to !== undefined
 | 
			
		||||
        from !== null &&
 | 
			
		||||
        to !== null &&
 | 
			
		||||
        from !== undefined &&
 | 
			
		||||
        to !== undefined &&
 | 
			
		||||
        // Filter out errors that are not from the top-level module.
 | 
			
		||||
        moduleId === TOP_LEVEL_MODULE_ID
 | 
			
		||||
      )
 | 
			
		||||
    })
 | 
			
		||||
    .sort((a, b) => {
 | 
			
		||||
@ -127,8 +138,16 @@ export function kclErrorsToDiagnostics(
 | 
			
		||||
  errors: KCLError[]
 | 
			
		||||
): CodeMirrorDiagnostic[] {
 | 
			
		||||
  return errors?.flatMap((err) => {
 | 
			
		||||
    return err.sourceRanges.map(([from, to]) => {
 | 
			
		||||
      return { from, to, message: err.msg, severity: 'error' }
 | 
			
		||||
    })
 | 
			
		||||
    const sourceRanges: CodeMirrorDiagnostic[] = err.sourceRanges
 | 
			
		||||
      // Filter out errors that are not from the top-level module.
 | 
			
		||||
      .filter(([_start, _end, moduleId]) => moduleId === TOP_LEVEL_MODULE_ID)
 | 
			
		||||
      .map(([from, to]) => {
 | 
			
		||||
        return { from, to, message: err.msg, severity: 'error' }
 | 
			
		||||
      })
 | 
			
		||||
    // Make sure we didn't filter out all the source ranges.
 | 
			
		||||
    if (sourceRanges.length === 0) {
 | 
			
		||||
      sourceRanges.push({ from: 0, to: 0, message: err.msg, severity: 'error' })
 | 
			
		||||
    }
 | 
			
		||||
    return sourceRanges
 | 
			
		||||
  })
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -65,7 +65,7 @@ const newVar = myVar + 1`
 | 
			
		||||
        to: [0, 2],
 | 
			
		||||
        from: [0, 0],
 | 
			
		||||
        __geoMeta: {
 | 
			
		||||
          sourceRange: [72, 97],
 | 
			
		||||
          sourceRange: [72, 97, 0],
 | 
			
		||||
          id: expect.any(String),
 | 
			
		||||
        },
 | 
			
		||||
        tag: {
 | 
			
		||||
@ -81,7 +81,7 @@ const newVar = myVar + 1`
 | 
			
		||||
        from: [0, 2],
 | 
			
		||||
        tag: null,
 | 
			
		||||
        __geoMeta: {
 | 
			
		||||
          sourceRange: [103, 119],
 | 
			
		||||
          sourceRange: [103, 119, 0],
 | 
			
		||||
          id: expect.any(String),
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -90,7 +90,7 @@ const newVar = myVar + 1`
 | 
			
		||||
        to: [5, -1],
 | 
			
		||||
        from: [2, 3],
 | 
			
		||||
        __geoMeta: {
 | 
			
		||||
          sourceRange: [125, 154],
 | 
			
		||||
          sourceRange: [125, 154, 0],
 | 
			
		||||
          id: expect.any(String),
 | 
			
		||||
        },
 | 
			
		||||
        tag: {
 | 
			
		||||
@ -160,14 +160,14 @@ const newVar = myVar + 1`
 | 
			
		||||
          tag: null,
 | 
			
		||||
          __geoMeta: {
 | 
			
		||||
            id: expect.any(String),
 | 
			
		||||
            sourceRange: [39, 63],
 | 
			
		||||
            sourceRange: [39, 63, 0],
 | 
			
		||||
          },
 | 
			
		||||
        },
 | 
			
		||||
        tags: {
 | 
			
		||||
          myPath: {
 | 
			
		||||
            __meta: [
 | 
			
		||||
              {
 | 
			
		||||
                sourceRange: [109, 116],
 | 
			
		||||
                sourceRange: [109, 116, 0],
 | 
			
		||||
              },
 | 
			
		||||
            ],
 | 
			
		||||
            type: 'TagIdentifier',
 | 
			
		||||
@ -182,7 +182,7 @@ const newVar = myVar + 1`
 | 
			
		||||
            from: [0, 0],
 | 
			
		||||
            tag: null,
 | 
			
		||||
            __geoMeta: {
 | 
			
		||||
              sourceRange: [69, 85],
 | 
			
		||||
              sourceRange: [69, 85, 0],
 | 
			
		||||
              id: expect.any(String),
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
@ -191,7 +191,7 @@ const newVar = myVar + 1`
 | 
			
		||||
            to: [0, 1],
 | 
			
		||||
            from: [1, 1],
 | 
			
		||||
            __geoMeta: {
 | 
			
		||||
              sourceRange: [91, 117],
 | 
			
		||||
              sourceRange: [91, 117, 0],
 | 
			
		||||
              id: expect.any(String),
 | 
			
		||||
            },
 | 
			
		||||
            tag: {
 | 
			
		||||
@ -207,15 +207,15 @@ const newVar = myVar + 1`
 | 
			
		||||
            from: [0, 1],
 | 
			
		||||
            tag: null,
 | 
			
		||||
            __geoMeta: {
 | 
			
		||||
              sourceRange: [123, 139],
 | 
			
		||||
              sourceRange: [123, 139, 0],
 | 
			
		||||
              id: expect.any(String),
 | 
			
		||||
            },
 | 
			
		||||
          },
 | 
			
		||||
        ],
 | 
			
		||||
        id: expect.any(String),
 | 
			
		||||
        __meta: [{ sourceRange: [39, 63] }],
 | 
			
		||||
        __meta: [{ sourceRange: [39, 63, 0] }],
 | 
			
		||||
      },
 | 
			
		||||
      __meta: [{ sourceRange: [39, 63] }],
 | 
			
		||||
      __meta: [{ sourceRange: [39, 63, 0] }],
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
  it('execute array expression', async () => {
 | 
			
		||||
@ -229,7 +229,7 @@ const newVar = myVar + 1`
 | 
			
		||||
      value: 3,
 | 
			
		||||
      __meta: [
 | 
			
		||||
        {
 | 
			
		||||
          sourceRange: [14, 15],
 | 
			
		||||
          sourceRange: [14, 15, 0],
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    })
 | 
			
		||||
@ -238,7 +238,7 @@ const newVar = myVar + 1`
 | 
			
		||||
      value: [1, '2', 3, 9],
 | 
			
		||||
      __meta: [
 | 
			
		||||
        {
 | 
			
		||||
          sourceRange: [27, 49],
 | 
			
		||||
          sourceRange: [27, 49, 0],
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    })
 | 
			
		||||
@ -257,7 +257,7 @@ const newVar = myVar + 1`
 | 
			
		||||
      value: { aStr: 'str', anum: 2, identifier: 3, binExp: 9 },
 | 
			
		||||
      __meta: [
 | 
			
		||||
        {
 | 
			
		||||
          sourceRange: [27, 83],
 | 
			
		||||
          sourceRange: [27, 83, 0],
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    })
 | 
			
		||||
@ -272,7 +272,7 @@ const newVar = myVar + 1`
 | 
			
		||||
      value: '123',
 | 
			
		||||
      __meta: [
 | 
			
		||||
        {
 | 
			
		||||
          sourceRange: [41, 50],
 | 
			
		||||
          sourceRange: [41, 50, 0],
 | 
			
		||||
        },
 | 
			
		||||
      ],
 | 
			
		||||
    })
 | 
			
		||||
@ -426,7 +426,7 @@ const theExtrude = startSketchOn('XY')
 | 
			
		||||
      new KCLError(
 | 
			
		||||
        'undefined_value',
 | 
			
		||||
        'memory item key `myVarZ` is not defined',
 | 
			
		||||
        [[129, 135]]
 | 
			
		||||
        [[129, 135, 0]]
 | 
			
		||||
      )
 | 
			
		||||
    )
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
@ -101,15 +101,15 @@ describe('Testing findUniqueName', () => {
 | 
			
		||||
  it('should find a unique name', () => {
 | 
			
		||||
    const result = findUniqueName(
 | 
			
		||||
      JSON.stringify([
 | 
			
		||||
        { type: 'Identifier', name: 'yo01', start: 0, end: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo02', start: 0, end: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo03', start: 0, end: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo04', start: 0, end: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo05', start: 0, end: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo06', start: 0, end: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo07', start: 0, end: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo08', start: 0, end: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo09', start: 0, end: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo01', start: 0, end: 0, moduleId: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo02', start: 0, end: 0, moduleId: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo03', start: 0, end: 0, moduleId: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo04', start: 0, end: 0, moduleId: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo05', start: 0, end: 0, moduleId: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo06', start: 0, end: 0, moduleId: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo07', start: 0, end: 0, moduleId: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo08', start: 0, end: 0, moduleId: 0 },
 | 
			
		||||
        { type: 'Identifier', name: 'yo09', start: 0, end: 0, moduleId: 0 },
 | 
			
		||||
      ] satisfies Node<Identifier>[]),
 | 
			
		||||
      'yo',
 | 
			
		||||
      2
 | 
			
		||||
@ -124,6 +124,7 @@ describe('Testing addSketchTo', () => {
 | 
			
		||||
        body: [],
 | 
			
		||||
        start: 0,
 | 
			
		||||
        end: 0,
 | 
			
		||||
        moduleId: 0,
 | 
			
		||||
        nonCodeMeta: { nonCodeNodes: {}, startNodes: [] },
 | 
			
		||||
      },
 | 
			
		||||
      'yz'
 | 
			
		||||
 | 
			
		||||
@ -242,6 +242,7 @@ export function mutateObjExpProp(
 | 
			
		||||
        value: updateWith,
 | 
			
		||||
        start: 0,
 | 
			
		||||
        end: 0,
 | 
			
		||||
        moduleId: 0,
 | 
			
		||||
      })
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
@ -577,6 +578,7 @@ export function createLiteral(value: string | number): Node<Literal> {
 | 
			
		||||
    type: 'Literal',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
    value,
 | 
			
		||||
    raw: `${value}`,
 | 
			
		||||
  }
 | 
			
		||||
@ -587,6 +589,7 @@ export function createTagDeclarator(value: string): Node<TagDeclarator> {
 | 
			
		||||
    type: 'TagDeclarator',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
 | 
			
		||||
    value,
 | 
			
		||||
  }
 | 
			
		||||
@ -597,6 +600,7 @@ export function createIdentifier(name: string): Node<Identifier> {
 | 
			
		||||
    type: 'Identifier',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
 | 
			
		||||
    name,
 | 
			
		||||
  }
 | 
			
		||||
@ -607,6 +611,7 @@ export function createPipeSubstitution(): Node<PipeSubstitution> {
 | 
			
		||||
    type: 'PipeSubstitution',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -618,10 +623,12 @@ export function createCallExpressionStdLib(
 | 
			
		||||
    type: 'CallExpression',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
    callee: {
 | 
			
		||||
      type: 'Identifier',
 | 
			
		||||
      start: 0,
 | 
			
		||||
      end: 0,
 | 
			
		||||
      moduleId: 0,
 | 
			
		||||
 | 
			
		||||
      name,
 | 
			
		||||
    },
 | 
			
		||||
@ -638,10 +645,12 @@ export function createCallExpression(
 | 
			
		||||
    type: 'CallExpression',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
    callee: {
 | 
			
		||||
      type: 'Identifier',
 | 
			
		||||
      start: 0,
 | 
			
		||||
      end: 0,
 | 
			
		||||
      moduleId: 0,
 | 
			
		||||
 | 
			
		||||
      name,
 | 
			
		||||
    },
 | 
			
		||||
@ -657,6 +666,7 @@ export function createArrayExpression(
 | 
			
		||||
    type: 'ArrayExpression',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
 | 
			
		||||
    nonCodeMeta: nonCodeMetaEmpty(),
 | 
			
		||||
    elements,
 | 
			
		||||
@ -670,6 +680,7 @@ export function createPipeExpression(
 | 
			
		||||
    type: 'PipeExpression',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
 | 
			
		||||
    body,
 | 
			
		||||
    nonCodeMeta: nonCodeMetaEmpty(),
 | 
			
		||||
@ -686,12 +697,14 @@ export function createVariableDeclaration(
 | 
			
		||||
    type: 'VariableDeclaration',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
 | 
			
		||||
    declarations: [
 | 
			
		||||
      {
 | 
			
		||||
        type: 'VariableDeclarator',
 | 
			
		||||
        start: 0,
 | 
			
		||||
        end: 0,
 | 
			
		||||
        moduleId: 0,
 | 
			
		||||
 | 
			
		||||
        id: createIdentifier(varName),
 | 
			
		||||
        init,
 | 
			
		||||
@ -709,12 +722,14 @@ export function createObjectExpression(properties: {
 | 
			
		||||
    type: 'ObjectExpression',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
 | 
			
		||||
    nonCodeMeta: nonCodeMetaEmpty(),
 | 
			
		||||
    properties: Object.entries(properties).map(([key, value]) => ({
 | 
			
		||||
      type: 'ObjectProperty',
 | 
			
		||||
      start: 0,
 | 
			
		||||
      end: 0,
 | 
			
		||||
      moduleId: 0,
 | 
			
		||||
      key: createIdentifier(key),
 | 
			
		||||
 | 
			
		||||
      value,
 | 
			
		||||
@ -730,6 +745,7 @@ export function createUnaryExpression(
 | 
			
		||||
    type: 'UnaryExpression',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
 | 
			
		||||
    operator,
 | 
			
		||||
    argument,
 | 
			
		||||
@ -745,6 +761,7 @@ export function createBinaryExpression([left, operator, right]: [
 | 
			
		||||
    type: 'BinaryExpression',
 | 
			
		||||
    start: 0,
 | 
			
		||||
    end: 0,
 | 
			
		||||
    moduleId: 0,
 | 
			
		||||
 | 
			
		||||
    operator,
 | 
			
		||||
    left,
 | 
			
		||||
 | 
			
		||||
@ -13,6 +13,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        37,
 | 
			
		||||
        64,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "pathIds": [
 | 
			
		||||
@ -31,6 +32,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        37,
 | 
			
		||||
        64,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "planeId": "UUID",
 | 
			
		||||
@ -56,6 +58,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        70,
 | 
			
		||||
        86,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "edgeIds": [
 | 
			
		||||
@ -77,6 +80,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        92,
 | 
			
		||||
        119,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "edgeCutId": "UUID",
 | 
			
		||||
@ -99,6 +103,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        125,
 | 
			
		||||
        150,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "edgeIds": [
 | 
			
		||||
@ -120,6 +125,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        156,
 | 
			
		||||
        203,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "edgeIds": [
 | 
			
		||||
@ -141,6 +147,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        209,
 | 
			
		||||
        217,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "edgeIds": [],
 | 
			
		||||
@ -162,6 +169,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        231,
 | 
			
		||||
        254,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "edgeIds": [
 | 
			
		||||
@ -289,6 +297,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        260,
 | 
			
		||||
        299,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "consumedEdgeId": "UUID",
 | 
			
		||||
@ -307,6 +316,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        350,
 | 
			
		||||
        377,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "planeId": "UUID",
 | 
			
		||||
@ -331,6 +341,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        383,
 | 
			
		||||
        398,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "edgeIds": [
 | 
			
		||||
@ -352,6 +363,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        404,
 | 
			
		||||
        420,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "edgeIds": [
 | 
			
		||||
@ -373,6 +385,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        426,
 | 
			
		||||
        473,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "edgeIds": [
 | 
			
		||||
@ -394,6 +407,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        479,
 | 
			
		||||
        487,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "edgeIds": [],
 | 
			
		||||
@ -415,6 +429,7 @@ Map {
 | 
			
		||||
      "range": [
 | 
			
		||||
        501,
 | 
			
		||||
        522,
 | 
			
		||||
        0,
 | 
			
		||||
      ],
 | 
			
		||||
    },
 | 
			
		||||
    "edgeIds": [
 | 
			
		||||
 | 
			
		||||
@ -610,7 +610,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        sweepId: '',
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
          range: [37, 64],
 | 
			
		||||
          range: [37, 64, 0],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
    ])
 | 
			
		||||
@ -622,7 +622,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceIds: [],
 | 
			
		||||
        edgeIds: [],
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [231, 254],
 | 
			
		||||
          range: [231, 254, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -632,7 +632,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        planeId: expect.any(String),
 | 
			
		||||
        sweepId: expect.any(String),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [37, 64],
 | 
			
		||||
          range: [37, 64, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
        solid2dId: expect.any(String),
 | 
			
		||||
@ -645,7 +645,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceId: '',
 | 
			
		||||
        edgeIds: [],
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [70, 86],
 | 
			
		||||
          range: [70, 86, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -655,7 +655,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        planeId: expect.any(String),
 | 
			
		||||
        sweepId: expect.any(String),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [37, 64],
 | 
			
		||||
          range: [37, 64, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
        solid2dId: expect.any(String),
 | 
			
		||||
@ -669,7 +669,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        edgeIds: [],
 | 
			
		||||
        surfaceId: '',
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [260, 299],
 | 
			
		||||
          range: [260, 299, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -679,7 +679,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceId: expect.any(String),
 | 
			
		||||
        edgeIds: expect.any(Array),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [92, 119],
 | 
			
		||||
          range: [92, 119, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
        edgeCutId: expect.any(String),
 | 
			
		||||
@ -699,7 +699,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceId: expect.any(String),
 | 
			
		||||
        edgeIds: expect.any(Array),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [156, 203],
 | 
			
		||||
          range: [156, 203, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -710,7 +710,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceIds: expect.any(Array),
 | 
			
		||||
        edgeIds: expect.any(Array),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [231, 254],
 | 
			
		||||
          range: [231, 254, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -727,7 +727,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceId: expect.any(String),
 | 
			
		||||
        edgeIds: expect.any(Array),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [125, 150],
 | 
			
		||||
          range: [125, 150, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -738,7 +738,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceIds: expect.any(Array),
 | 
			
		||||
        edgeIds: expect.any(Array),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [231, 254],
 | 
			
		||||
          range: [231, 254, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -755,7 +755,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceId: expect.any(String),
 | 
			
		||||
        edgeIds: expect.any(Array),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [92, 119],
 | 
			
		||||
          range: [92, 119, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
        edgeCutId: expect.any(String),
 | 
			
		||||
@ -767,7 +767,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceIds: expect.any(Array),
 | 
			
		||||
        edgeIds: expect.any(Array),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [231, 254],
 | 
			
		||||
          range: [231, 254, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -784,7 +784,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceId: expect.any(String),
 | 
			
		||||
        edgeIds: expect.any(Array),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [70, 86],
 | 
			
		||||
          range: [70, 86, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -795,7 +795,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceIds: expect.any(Array),
 | 
			
		||||
        edgeIds: expect.any(Array),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [231, 254],
 | 
			
		||||
          range: [231, 254, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -813,7 +813,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceIds: expect.any(Array),
 | 
			
		||||
        edgeIds: expect.any(Array),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [231, 254],
 | 
			
		||||
          range: [231, 254, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
@ -831,7 +831,7 @@ describe('testing getArtifactsToUpdate', () => {
 | 
			
		||||
        surfaceIds: expect.any(Array),
 | 
			
		||||
        edgeIds: expect.any(Array),
 | 
			
		||||
        codeRef: {
 | 
			
		||||
          range: [231, 254],
 | 
			
		||||
          range: [231, 254, 0],
 | 
			
		||||
          pathToNode: [['body', '']],
 | 
			
		||||
        },
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
@ -1823,11 +1823,13 @@ export const updateStartProfileAtArgs: SketchLineHelper['updateArgs'] = ({
 | 
			
		||||
      modifiedAst: {
 | 
			
		||||
        start: 0,
 | 
			
		||||
        end: 0,
 | 
			
		||||
        moduleId: 0,
 | 
			
		||||
        body: [],
 | 
			
		||||
 | 
			
		||||
        nonCodeMeta: {
 | 
			
		||||
          start: 0,
 | 
			
		||||
          end: 0,
 | 
			
		||||
          moduleId: 0,
 | 
			
		||||
          startNodes: [],
 | 
			
		||||
          nonCodeNodes: [],
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
@ -120,8 +120,8 @@ const initialise = async () => {
 | 
			
		||||
 | 
			
		||||
export const initPromise = initialise()
 | 
			
		||||
 | 
			
		||||
export const rangeTypeFix = (ranges: number[][]): [number, number][] =>
 | 
			
		||||
  ranges.map(([start, end]) => [start, end])
 | 
			
		||||
export const rangeTypeFix = (ranges: number[][]): [number, number, number][] =>
 | 
			
		||||
  ranges.map(([start, end, moduleId]) => [start, end, moduleId])
 | 
			
		||||
 | 
			
		||||
export const parse = (code: string | Error): Node<Program> | Error => {
 | 
			
		||||
  if (err(code)) return code
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										2
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										2
									
								
								src/wasm-lib/Cargo.lock
									
									
									
										generated
									
									
									
								
							@ -3083,6 +3083,7 @@ dependencies = [
 | 
			
		||||
 "chrono",
 | 
			
		||||
 "dyn-clone",
 | 
			
		||||
 "indexmap 1.9.3",
 | 
			
		||||
 "indexmap 2.6.0",
 | 
			
		||||
 "schemars_derive",
 | 
			
		||||
 "serde",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
@ -3883,6 +3884,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
 | 
			
		||||
checksum = "3a2f31991cee3dce1ca4f929a8a04fdd11fd8801aac0f2030b0fa8a0a3fef6b9"
 | 
			
		||||
dependencies = [
 | 
			
		||||
 "chrono",
 | 
			
		||||
 "indexmap 2.6.0",
 | 
			
		||||
 "lazy_static",
 | 
			
		||||
 "serde_json",
 | 
			
		||||
 "thiserror 1.0.68",
 | 
			
		||||
 | 
			
		||||
@ -173,9 +173,7 @@ fn do_stdlib_inner(
 | 
			
		||||
        quote! {
 | 
			
		||||
            let code_blocks = vec![#(#cb),*];
 | 
			
		||||
            code_blocks.iter().map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
@ -750,9 +748,7 @@ fn generate_code_block_test(fn_name: &str, code_block: &str, index: usize) -> pr
 | 
			
		||||
    quote! {
 | 
			
		||||
        #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
        async fn #test_name_mock() {
 | 
			
		||||
            let tokens = crate::token::lexer(#code_block).unwrap();
 | 
			
		||||
            let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
            let program = parser.ast().unwrap();
 | 
			
		||||
            let program = crate::parser::top_level_parse(#code_block).unwrap();
 | 
			
		||||
            let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
            let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
                engine: std::sync::Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await.unwrap())),
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,7 @@
 | 
			
		||||
mod test_examples_someFn {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_someFn0() {
 | 
			
		||||
        let tokens = crate::token::lexer("someFn()").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse("someFn()").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -113,9 +111,7 @@ impl crate::docs::StdLibFn for SomeFn {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,7 @@
 | 
			
		||||
mod test_examples_someFn {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_someFn0() {
 | 
			
		||||
        let tokens = crate::token::lexer("someFn()").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse("someFn()").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -113,9 +111,7 @@ impl crate::docs::StdLibFn for SomeFn {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,9 @@
 | 
			
		||||
mod test_examples_show {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_show0() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nshow").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nshow")
 | 
			
		||||
                .unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -36,9 +36,8 @@ mod test_examples_show {
 | 
			
		||||
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_show1() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -150,9 +149,7 @@ impl crate::docs::StdLibFn for Show {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,8 @@
 | 
			
		||||
mod test_examples_show {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_show0() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Show {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,10 +2,9 @@
 | 
			
		||||
mod test_examples_my_func {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_my_func0() {
 | 
			
		||||
        let tokens =
 | 
			
		||||
            crate::token::lexer("This is another code block.\nyes sirrr.\nmyFunc").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nmyFunc")
 | 
			
		||||
                .unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -37,9 +36,8 @@ mod test_examples_my_func {
 | 
			
		||||
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_my_func1() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmyFunc").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is code.\nIt does other shit.\nmyFunc").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -151,9 +149,7 @@ impl crate::docs::StdLibFn for MyFunc {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,10 +2,9 @@
 | 
			
		||||
mod test_examples_line_to {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_line_to0() {
 | 
			
		||||
        let tokens =
 | 
			
		||||
            crate::token::lexer("This is another code block.\nyes sirrr.\nlineTo").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nlineTo")
 | 
			
		||||
                .unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -37,9 +36,8 @@ mod test_examples_line_to {
 | 
			
		||||
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_line_to1() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is code.\nIt does other shit.\nlineTo").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is code.\nIt does other shit.\nlineTo").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -159,9 +157,7 @@ impl crate::docs::StdLibFn for LineTo {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,8 @@
 | 
			
		||||
mod test_examples_min {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_min0() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is another code block.\nyes sirrr.\nmin").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is another code block.\nyes sirrr.\nmin").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -36,9 +35,8 @@ mod test_examples_min {
 | 
			
		||||
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_min1() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is code.\nIt does other shit.\nmin").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is code.\nIt does other shit.\nmin").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -150,9 +148,7 @@ impl crate::docs::StdLibFn for Min {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,8 @@
 | 
			
		||||
mod test_examples_show {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_show0() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Show {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,8 @@
 | 
			
		||||
mod test_examples_import {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_import0() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Import {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,8 @@
 | 
			
		||||
mod test_examples_import {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_import0() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Import {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,8 @@
 | 
			
		||||
mod test_examples_import {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_import0() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is code.\nIt does other shit.\nimport").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is code.\nIt does other shit.\nimport").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Import {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,8 @@
 | 
			
		||||
mod test_examples_show {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_show0() {
 | 
			
		||||
        let tokens = crate::token::lexer("This is code.\nIt does other shit.\nshow").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program =
 | 
			
		||||
            crate::parser::top_level_parse("This is code.\nIt does other shit.\nshow").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -113,9 +112,7 @@ impl crate::docs::StdLibFn for Show {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -2,9 +2,7 @@
 | 
			
		||||
mod test_examples_some_function {
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_mock_example_some_function0() {
 | 
			
		||||
        let tokens = crate::token::lexer("someFunction()").unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse("someFunction()").unwrap();
 | 
			
		||||
        let id_generator = crate::executor::IdGenerator::default();
 | 
			
		||||
        let ctx = crate::executor::ExecutorContext {
 | 
			
		||||
            engine: std::sync::Arc::new(Box::new(
 | 
			
		||||
@ -108,9 +106,7 @@ impl crate::docs::StdLibFn for SomeFunction {
 | 
			
		||||
        code_blocks
 | 
			
		||||
            .iter()
 | 
			
		||||
            .map(|cb| {
 | 
			
		||||
                let tokens = crate::token::lexer(cb).unwrap();
 | 
			
		||||
                let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
                let program = parser.ast().unwrap();
 | 
			
		||||
                let program = crate::parser::top_level_parse(cb).unwrap();
 | 
			
		||||
                let mut options: crate::ast::types::FormatOptions = Default::default();
 | 
			
		||||
                options.insert_final_newline = false;
 | 
			
		||||
                program.recast(&options, 0)
 | 
			
		||||
 | 
			
		||||
@ -16,8 +16,7 @@ use syn::{parse_macro_input, LitStr};
 | 
			
		||||
pub fn parse(input: TokenStream) -> TokenStream {
 | 
			
		||||
    let input = parse_macro_input!(input as LitStr);
 | 
			
		||||
    let kcl_src = input.value();
 | 
			
		||||
    let tokens = kcl_lib::token::lexer(&kcl_src).unwrap();
 | 
			
		||||
    let ast = kcl_lib::parser::Parser::new(tokens).ast().unwrap();
 | 
			
		||||
    let ast = kcl_lib::parser::top_level_parse(&kcl_src).unwrap();
 | 
			
		||||
    let ast_struct = ast.bake(&Default::default());
 | 
			
		||||
    quote!(#ast_struct).into()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
extern crate alloc;
 | 
			
		||||
use kcl_lib::ast::types::{
 | 
			
		||||
    BodyItem, Expr, Identifier, ItemVisibility, Literal, LiteralValue, Node, Program, VariableDeclaration,
 | 
			
		||||
    BodyItem, Expr, Identifier, ItemVisibility, Literal, LiteralValue, ModuleId, Node, Program, VariableDeclaration,
 | 
			
		||||
    VariableDeclarator, VariableKind,
 | 
			
		||||
};
 | 
			
		||||
use kcl_macros::parse;
 | 
			
		||||
@ -9,6 +9,7 @@ use pretty_assertions::assert_eq;
 | 
			
		||||
#[test]
 | 
			
		||||
fn basic() {
 | 
			
		||||
    let actual = parse!("const y = 4");
 | 
			
		||||
    let module_id = ModuleId::default();
 | 
			
		||||
    let expected = Node {
 | 
			
		||||
        inner: Program {
 | 
			
		||||
            body: vec![BodyItem::VariableDeclaration(Box::new(Node::new(
 | 
			
		||||
@ -22,6 +23,7 @@ fn basic() {
 | 
			
		||||
                                },
 | 
			
		||||
                                6,
 | 
			
		||||
                                7,
 | 
			
		||||
                                module_id,
 | 
			
		||||
                            ),
 | 
			
		||||
                            init: Expr::Literal(Box::new(Node::new(
 | 
			
		||||
                                Literal {
 | 
			
		||||
@ -31,11 +33,13 @@ fn basic() {
 | 
			
		||||
                                },
 | 
			
		||||
                                10,
 | 
			
		||||
                                11,
 | 
			
		||||
                                module_id,
 | 
			
		||||
                            ))),
 | 
			
		||||
                            digest: None,
 | 
			
		||||
                        },
 | 
			
		||||
                        6,
 | 
			
		||||
                        11,
 | 
			
		||||
                        module_id,
 | 
			
		||||
                    )],
 | 
			
		||||
                    visibility: ItemVisibility::Default,
 | 
			
		||||
                    kind: VariableKind::Const,
 | 
			
		||||
@ -43,12 +47,14 @@ fn basic() {
 | 
			
		||||
                },
 | 
			
		||||
                0,
 | 
			
		||||
                11,
 | 
			
		||||
                module_id,
 | 
			
		||||
            )))],
 | 
			
		||||
            non_code_meta: Default::default(),
 | 
			
		||||
            digest: None,
 | 
			
		||||
        },
 | 
			
		||||
        start: 0,
 | 
			
		||||
        end: 11,
 | 
			
		||||
        module_id,
 | 
			
		||||
    };
 | 
			
		||||
    assert_eq!(expected, actual);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -15,7 +15,7 @@ use hyper::{
 | 
			
		||||
    service::{make_service_fn, service_fn},
 | 
			
		||||
    Body, Error, Response, Server,
 | 
			
		||||
};
 | 
			
		||||
use kcl_lib::{executor::ExecutorContext, settings::types::UnitLength, test_server::RequestBody};
 | 
			
		||||
use kcl_lib::{ast::types::ModuleId, executor::ExecutorContext, settings::types::UnitLength, test_server::RequestBody};
 | 
			
		||||
use tokio::{
 | 
			
		||||
    sync::{mpsc, oneshot},
 | 
			
		||||
    task::JoinHandle,
 | 
			
		||||
@ -157,7 +157,8 @@ async fn snapshot_endpoint(body: Bytes, state: ExecutorContext) -> Response<Body
 | 
			
		||||
        Err(e) => return bad_request(format!("Invalid request JSON: {e}")),
 | 
			
		||||
    };
 | 
			
		||||
    let RequestBody { kcl_program, test_name } = body;
 | 
			
		||||
    let parser = match kcl_lib::token::lexer(&kcl_program) {
 | 
			
		||||
    let module_id = ModuleId::default();
 | 
			
		||||
    let parser = match kcl_lib::token::lexer(&kcl_program, module_id) {
 | 
			
		||||
        Ok(ts) => kcl_lib::parser::Parser::new(ts),
 | 
			
		||||
        Err(e) => return bad_request(format!("tokenization error: {e}")),
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
@ -7,9 +7,7 @@ mod conn_mock_core;
 | 
			
		||||
 | 
			
		||||
///Converts the given kcl code to an engine test
 | 
			
		||||
pub async fn kcl_to_engine_core(code: &str) -> Result<String> {
 | 
			
		||||
    let tokens = kcl_lib::token::lexer(code)?;
 | 
			
		||||
    let parser = kcl_lib::parser::Parser::new(tokens);
 | 
			
		||||
    let program = parser.ast()?;
 | 
			
		||||
    let program = kcl_lib::parser::top_level_parse(code)?;
 | 
			
		||||
 | 
			
		||||
    let result = Arc::new(Mutex::new("".into()));
 | 
			
		||||
    let ref_result = Arc::clone(&result);
 | 
			
		||||
 | 
			
		||||
@ -37,14 +37,14 @@ parse-display = "0.9.1"
 | 
			
		||||
pyo3 = { version = "0.22.6", optional = true }
 | 
			
		||||
reqwest = { version = "0.12", default-features = false, features = ["stream", "rustls-tls"] }
 | 
			
		||||
ropey = "1.6.1"
 | 
			
		||||
schemars = { version = "0.8.17", features = ["impl_json_schema", "url", "uuid1", "preserve_order"] }
 | 
			
		||||
schemars = { version = "0.8.17", features = ["impl_json_schema", "indexmap2", "url", "uuid1", "preserve_order"] }
 | 
			
		||||
serde = { version = "1.0.214", features = ["derive"] }
 | 
			
		||||
serde_json = "1.0.128"
 | 
			
		||||
sha2 = "0.10.8"
 | 
			
		||||
tabled = { version = "0.15.0", optional = true }
 | 
			
		||||
thiserror = "2.0.0"
 | 
			
		||||
toml = "0.8.19"
 | 
			
		||||
ts-rs = { version = "10.0.0", features = ["uuid-impl", "url-impl", "chrono-impl", "no-serde-warnings", "serde-json-impl"] }
 | 
			
		||||
ts-rs = { version = "10.0.0", features = ["uuid-impl", "url-impl", "chrono-impl", "indexmap-impl", "no-serde-warnings", "serde-json-impl"] }
 | 
			
		||||
url = { version = "2.5.3", features = ["serde"] }
 | 
			
		||||
urlencoding = "2.1.3"
 | 
			
		||||
uuid = { version = "1.11.0", features = ["v4", "js", "serde"] }
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,10 @@
 | 
			
		||||
use criterion::{black_box, criterion_group, criterion_main, Criterion};
 | 
			
		||||
 | 
			
		||||
pub fn bench_lex(c: &mut Criterion) {
 | 
			
		||||
    c.bench_function("lex_cube", |b| b.iter(|| lex(CUBE_PROGRAM)));
 | 
			
		||||
    c.bench_function("lex_big_kitt", |b| b.iter(|| lex(KITT_PROGRAM)));
 | 
			
		||||
    c.bench_function("lex_pipes_on_pipes", |b| b.iter(|| lex(PIPES_PROGRAM)));
 | 
			
		||||
    let module_id = kcl_lib::ast::types::ModuleId::default();
 | 
			
		||||
    c.bench_function("lex_cube", |b| b.iter(|| lex(CUBE_PROGRAM, module_id)));
 | 
			
		||||
    c.bench_function("lex_big_kitt", |b| b.iter(|| lex(KITT_PROGRAM, module_id)));
 | 
			
		||||
    c.bench_function("lex_pipes_on_pipes", |b| b.iter(|| lex(PIPES_PROGRAM, module_id)));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn bench_parse(c: &mut Criterion) {
 | 
			
		||||
@ -15,7 +16,8 @@ pub fn bench_parse(c: &mut Criterion) {
 | 
			
		||||
        ("mike_stress_test", MIKE_STRESS_TEST_PROGRAM),
 | 
			
		||||
        ("koch snowflake", LSYSTEM_KOCH_SNOWFLAKE_PROGRAM),
 | 
			
		||||
    ] {
 | 
			
		||||
        let tokens = kcl_lib::token::lexer(file).unwrap();
 | 
			
		||||
        let module_id = kcl_lib::ast::types::ModuleId::default();
 | 
			
		||||
        let tokens = kcl_lib::token::lexer(file, module_id).unwrap();
 | 
			
		||||
        c.bench_function(&format!("parse_{name}"), move |b| {
 | 
			
		||||
            let tok = tokens.clone();
 | 
			
		||||
            b.iter(move || {
 | 
			
		||||
@ -26,8 +28,8 @@ pub fn bench_parse(c: &mut Criterion) {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn lex(program: &str) {
 | 
			
		||||
    black_box(kcl_lib::token::lexer(program).unwrap());
 | 
			
		||||
fn lex(program: &str, module_id: kcl_lib::ast::types::ModuleId) {
 | 
			
		||||
    black_box(kcl_lib::token::lexer(program, module_id).unwrap());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
criterion_group!(benches, bench_lex, bench_parse);
 | 
			
		||||
 | 
			
		||||
@ -1,26 +1,32 @@
 | 
			
		||||
use iai::black_box;
 | 
			
		||||
 | 
			
		||||
pub fn parse(program: &str) {
 | 
			
		||||
    let tokens = kcl_lib::token::lexer(program).unwrap();
 | 
			
		||||
    let module_id = kcl_lib::ast::types::ModuleId::default();
 | 
			
		||||
    let tokens = kcl_lib::token::lexer(program, module_id).unwrap();
 | 
			
		||||
    let tok = tokens.clone();
 | 
			
		||||
    let parser = kcl_lib::parser::Parser::new(tok.clone());
 | 
			
		||||
    black_box(parser.ast().unwrap());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn lex_kitt() {
 | 
			
		||||
    black_box(kcl_lib::token::lexer(KITT_PROGRAM).unwrap());
 | 
			
		||||
    let module_id = kcl_lib::ast::types::ModuleId::default();
 | 
			
		||||
    black_box(kcl_lib::token::lexer(KITT_PROGRAM, module_id).unwrap());
 | 
			
		||||
}
 | 
			
		||||
fn lex_pipes() {
 | 
			
		||||
    black_box(kcl_lib::token::lexer(PIPES_PROGRAM).unwrap());
 | 
			
		||||
    let module_id = kcl_lib::ast::types::ModuleId::default();
 | 
			
		||||
    black_box(kcl_lib::token::lexer(PIPES_PROGRAM, module_id).unwrap());
 | 
			
		||||
}
 | 
			
		||||
fn lex_cube() {
 | 
			
		||||
    black_box(kcl_lib::token::lexer(CUBE_PROGRAM).unwrap());
 | 
			
		||||
    let module_id = kcl_lib::ast::types::ModuleId::default();
 | 
			
		||||
    black_box(kcl_lib::token::lexer(CUBE_PROGRAM, module_id).unwrap());
 | 
			
		||||
}
 | 
			
		||||
fn lex_math() {
 | 
			
		||||
    black_box(kcl_lib::token::lexer(MATH_PROGRAM).unwrap());
 | 
			
		||||
    let module_id = kcl_lib::ast::types::ModuleId::default();
 | 
			
		||||
    black_box(kcl_lib::token::lexer(MATH_PROGRAM, module_id).unwrap());
 | 
			
		||||
}
 | 
			
		||||
fn lex_lsystem() {
 | 
			
		||||
    black_box(kcl_lib::token::lexer(LSYSTEM_PROGRAM).unwrap());
 | 
			
		||||
    let module_id = kcl_lib::ast::types::ModuleId::default();
 | 
			
		||||
    black_box(kcl_lib::token::lexer(LSYSTEM_PROGRAM, module_id).unwrap());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
fn parse_kitt() {
 | 
			
		||||
 | 
			
		||||
@ -9,8 +9,7 @@ pub fn bench_digest(c: &mut Criterion) {
 | 
			
		||||
        ("mike_stress_test", MIKE_STRESS_TEST_PROGRAM),
 | 
			
		||||
        ("lsystem", LSYSTEM_PROGRAM),
 | 
			
		||||
    ] {
 | 
			
		||||
        let tokens = kcl_lib::token::lexer(file).unwrap();
 | 
			
		||||
        let prog = kcl_lib::parser::Parser::new(tokens).ast().unwrap();
 | 
			
		||||
        let prog = kcl_lib::parser::top_level_parse(file).unwrap();
 | 
			
		||||
        c.bench_function(&format!("digest_{name}"), move |b| {
 | 
			
		||||
            let prog = prog.clone();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -16,7 +16,7 @@ use crate::{
 | 
			
		||||
    executor::{Point2d, SourceRange},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::types::Node;
 | 
			
		||||
use super::types::{ModuleId, Node};
 | 
			
		||||
 | 
			
		||||
type Point3d = kcmc::shared::Point3d<f64>;
 | 
			
		||||
 | 
			
		||||
@ -38,6 +38,7 @@ const EPSILON: f64 = 0.015625; // or 2^-6
 | 
			
		||||
pub async fn modify_ast_for_sketch(
 | 
			
		||||
    engine: &Arc<Box<dyn EngineManager>>,
 | 
			
		||||
    program: &mut Node<Program>,
 | 
			
		||||
    module_id: ModuleId,
 | 
			
		||||
    // The name of the sketch.
 | 
			
		||||
    sketch_name: &str,
 | 
			
		||||
    // The type of plane the sketch is on. `XY` or `XZ`, etc
 | 
			
		||||
@ -183,9 +184,7 @@ pub async fn modify_ast_for_sketch(
 | 
			
		||||
    let recasted = program.recast(&FormatOptions::default(), 0);
 | 
			
		||||
 | 
			
		||||
    // Re-parse the ast so we get the correct source ranges.
 | 
			
		||||
    let tokens = crate::token::lexer(&recasted)?;
 | 
			
		||||
    let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
    *program = parser.ast()?;
 | 
			
		||||
    *program = crate::parser::parse(&recasted, module_id)?;
 | 
			
		||||
 | 
			
		||||
    Ok(recasted)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -37,6 +37,7 @@ pub(crate) mod digest;
 | 
			
		||||
pub(crate) mod execute;
 | 
			
		||||
mod literal_value;
 | 
			
		||||
mod none;
 | 
			
		||||
pub(crate) mod source_range;
 | 
			
		||||
 | 
			
		||||
use digest::Digest;
 | 
			
		||||
 | 
			
		||||
@ -48,11 +49,14 @@ pub enum Definition<'a> {
 | 
			
		||||
#[derive(Debug, Default, Clone, Deserialize, Serialize, PartialEq, Eq, ts_rs::TS, Bake)]
 | 
			
		||||
#[databake(path = kcl_lib::ast::types)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
pub struct Node<T> {
 | 
			
		||||
    #[serde(flatten)]
 | 
			
		||||
    pub inner: T,
 | 
			
		||||
    pub start: usize,
 | 
			
		||||
    pub end: usize,
 | 
			
		||||
    #[serde(default, skip_serializing_if = "ModuleId::is_top_level")]
 | 
			
		||||
    pub module_id: ModuleId,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T: JsonSchema> schemars::JsonSchema for Node<T> {
 | 
			
		||||
@ -78,8 +82,13 @@ impl<T: JsonSchema> schemars::JsonSchema for Node<T> {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> Node<T> {
 | 
			
		||||
    pub fn new(inner: T, start: usize, end: usize) -> Self {
 | 
			
		||||
        Self { inner, start, end }
 | 
			
		||||
    pub fn new(inner: T, start: usize, end: usize, module_id: ModuleId) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            inner,
 | 
			
		||||
            start,
 | 
			
		||||
            end,
 | 
			
		||||
            module_id,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn no_src(inner: T) -> Self {
 | 
			
		||||
@ -87,15 +96,21 @@ impl<T> Node<T> {
 | 
			
		||||
            inner,
 | 
			
		||||
            start: 0,
 | 
			
		||||
            end: 0,
 | 
			
		||||
            module_id: ModuleId::default(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn boxed(inner: T, start: usize, end: usize) -> BoxNode<T> {
 | 
			
		||||
        Box::new(Node { inner, start, end })
 | 
			
		||||
    pub fn boxed(inner: T, start: usize, end: usize, module_id: ModuleId) -> BoxNode<T> {
 | 
			
		||||
        Box::new(Node {
 | 
			
		||||
            inner,
 | 
			
		||||
            start,
 | 
			
		||||
            end,
 | 
			
		||||
            module_id,
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_source_ranges(&self) -> Vec<SourceRange> {
 | 
			
		||||
        vec![SourceRange([self.start, self.end])]
 | 
			
		||||
        vec![SourceRange([self.start, self.end, self.module_id.as_usize()])]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -121,19 +136,19 @@ impl<T: fmt::Display> fmt::Display for Node<T> {
 | 
			
		||||
 | 
			
		||||
impl<T> From<Node<T>> for crate::executor::SourceRange {
 | 
			
		||||
    fn from(v: Node<T>) -> Self {
 | 
			
		||||
        Self([v.start, v.end])
 | 
			
		||||
        Self([v.start, v.end, v.module_id.as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> From<&Node<T>> for crate::executor::SourceRange {
 | 
			
		||||
    fn from(v: &Node<T>) -> Self {
 | 
			
		||||
        Self([v.start, v.end])
 | 
			
		||||
        Self([v.start, v.end, v.module_id.as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl<T> From<&BoxNode<T>> for crate::executor::SourceRange {
 | 
			
		||||
    fn from(v: &BoxNode<T>) -> Self {
 | 
			
		||||
        Self([v.start, v.end])
 | 
			
		||||
        Self([v.start, v.end, v.module_id.as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -505,6 +520,29 @@ impl Program {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Identifier of a source file.  Uses a u32 to keep the size small.
 | 
			
		||||
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, Hash, Deserialize, Serialize, ts_rs::TS, JsonSchema, Bake)]
 | 
			
		||||
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
 | 
			
		||||
#[databake(path = kcl_lib::ast::types)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
pub struct ModuleId(pub u32);
 | 
			
		||||
 | 
			
		||||
impl ModuleId {
 | 
			
		||||
    pub fn from_usize(id: usize) -> Self {
 | 
			
		||||
        Self(u32::try_from(id).expect("module ID should fit in a u32"))
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_usize(&self) -> usize {
 | 
			
		||||
        usize::try_from(self.0).expect("module ID should fit in a usize")
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Top-level file is the one being executed.
 | 
			
		||||
    /// Represented by module ID of 0, i.e. the default value.
 | 
			
		||||
    pub fn is_top_level(&self) -> bool {
 | 
			
		||||
        *self == Self::default()
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema, Bake)]
 | 
			
		||||
#[databake(path = kcl_lib::ast::types)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
@ -538,13 +576,13 @@ impl BodyItem {
 | 
			
		||||
 | 
			
		||||
impl From<BodyItem> for SourceRange {
 | 
			
		||||
    fn from(item: BodyItem) -> Self {
 | 
			
		||||
        Self([item.start(), item.end()])
 | 
			
		||||
        Self([item.start(), item.end(), item.module_id().as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&BodyItem> for SourceRange {
 | 
			
		||||
    fn from(item: &BodyItem) -> Self {
 | 
			
		||||
        Self([item.start(), item.end()])
 | 
			
		||||
        Self([item.start(), item.end(), item.module_id().as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -568,7 +606,7 @@ pub enum Expr {
 | 
			
		||||
    MemberExpression(BoxNode<MemberExpression>),
 | 
			
		||||
    UnaryExpression(BoxNode<UnaryExpression>),
 | 
			
		||||
    IfExpression(BoxNode<IfExpression>),
 | 
			
		||||
    None(KclNone),
 | 
			
		||||
    None(Node<KclNone>),
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Expr {
 | 
			
		||||
@ -758,13 +796,13 @@ impl Expr {
 | 
			
		||||
 | 
			
		||||
impl From<Expr> for SourceRange {
 | 
			
		||||
    fn from(value: Expr) -> Self {
 | 
			
		||||
        Self([value.start(), value.end()])
 | 
			
		||||
        Self([value.start(), value.end(), value.module_id().as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&Expr> for SourceRange {
 | 
			
		||||
    fn from(value: &Expr) -> Self {
 | 
			
		||||
        Self([value.start(), value.end()])
 | 
			
		||||
        Self([value.start(), value.end(), value.module_id().as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -784,13 +822,13 @@ pub enum BinaryPart {
 | 
			
		||||
 | 
			
		||||
impl From<BinaryPart> for SourceRange {
 | 
			
		||||
    fn from(value: BinaryPart) -> Self {
 | 
			
		||||
        Self([value.start(), value.end()])
 | 
			
		||||
        Self([value.start(), value.end(), value.module_id().as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&BinaryPart> for SourceRange {
 | 
			
		||||
    fn from(value: &BinaryPart) -> Self {
 | 
			
		||||
        Self([value.start(), value.end()])
 | 
			
		||||
        Self([value.start(), value.end(), value.module_id().as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2154,13 +2192,13 @@ impl MemberObject {
 | 
			
		||||
 | 
			
		||||
impl From<MemberObject> for SourceRange {
 | 
			
		||||
    fn from(obj: MemberObject) -> Self {
 | 
			
		||||
        Self([obj.start(), obj.end()])
 | 
			
		||||
        Self([obj.start(), obj.end(), obj.module_id().as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&MemberObject> for SourceRange {
 | 
			
		||||
    fn from(obj: &MemberObject) -> Self {
 | 
			
		||||
        Self([obj.start(), obj.end()])
 | 
			
		||||
        Self([obj.start(), obj.end(), obj.module_id().as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2191,13 +2229,13 @@ impl LiteralIdentifier {
 | 
			
		||||
 | 
			
		||||
impl From<LiteralIdentifier> for SourceRange {
 | 
			
		||||
    fn from(id: LiteralIdentifier) -> Self {
 | 
			
		||||
        Self([id.start(), id.end()])
 | 
			
		||||
        Self([id.start(), id.end(), id.module_id().as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&LiteralIdentifier> for SourceRange {
 | 
			
		||||
    fn from(id: &LiteralIdentifier) -> Self {
 | 
			
		||||
        Self([id.start(), id.end()])
 | 
			
		||||
        Self([id.start(), id.end(), id.module_id().as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -3018,9 +3056,7 @@ fn ghi = (x) => {
 | 
			
		||||
 | 
			
		||||
ghi("things")
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(code).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(code).unwrap();
 | 
			
		||||
        let folding_ranges = program.get_lsp_folding_ranges();
 | 
			
		||||
        assert_eq!(folding_ranges.len(), 3);
 | 
			
		||||
        assert_eq!(folding_ranges[0].start_line, 29);
 | 
			
		||||
@ -3056,9 +3092,7 @@ fn ghi = (x) => {
 | 
			
		||||
  return x
 | 
			
		||||
}
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(code).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(code).unwrap();
 | 
			
		||||
        let symbols = program.get_lsp_symbols(code).unwrap();
 | 
			
		||||
        assert_eq!(symbols.len(), 7);
 | 
			
		||||
    }
 | 
			
		||||
@ -3078,9 +3112,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
     }, %)
 | 
			
		||||
  |> extrude(h, %)
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let value = program.get_non_code_meta_for_position(50);
 | 
			
		||||
 | 
			
		||||
@ -3103,9 +3135,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
     }, %)
 | 
			
		||||
  |> extrude(h, %)
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let value = program.get_non_code_meta_for_position(124);
 | 
			
		||||
 | 
			
		||||
@ -3118,9 +3148,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
  |> startProfileAt([0,0], %)
 | 
			
		||||
  |> xLine(5, %) // lin
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let value = program.get_non_code_meta_for_position(86);
 | 
			
		||||
 | 
			
		||||
@ -3132,9 +3160,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
        let some_program_string = r#"fn thing = (arg0: number, arg1: string, tag?: string) => {
 | 
			
		||||
    return arg0
 | 
			
		||||
}"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        // Check the program output for the types of the parameters.
 | 
			
		||||
        let function = program.body.first().unwrap();
 | 
			
		||||
@ -3156,9 +3182,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
        let some_program_string = r#"fn thing = (arg0: number[], arg1: string[], tag?: string) => {
 | 
			
		||||
    return arg0
 | 
			
		||||
}"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        // Check the program output for the types of the parameters.
 | 
			
		||||
        let function = program.body.first().unwrap();
 | 
			
		||||
@ -3180,9 +3204,8 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
        let some_program_string = r#"fn thing = (arg0: number[], arg1: {thing: number, things: string[], more?: string}, tag?: string) => {
 | 
			
		||||
    return arg0
 | 
			
		||||
}"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let program = crate::parser::parse(some_program_string, module_id).unwrap();
 | 
			
		||||
 | 
			
		||||
        // Check the program output for the types of the parameters.
 | 
			
		||||
        let function = program.body.first().unwrap();
 | 
			
		||||
@ -3207,6 +3230,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
                            },
 | 
			
		||||
                            35,
 | 
			
		||||
                            40,
 | 
			
		||||
                            module_id,
 | 
			
		||||
                        ),
 | 
			
		||||
                        type_: Some(FnArgType::Primitive(FnArgPrimitive::Number)),
 | 
			
		||||
                        optional: false,
 | 
			
		||||
@ -3220,6 +3244,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
                            },
 | 
			
		||||
                            50,
 | 
			
		||||
                            56,
 | 
			
		||||
                            module_id,
 | 
			
		||||
                        ),
 | 
			
		||||
                        type_: Some(FnArgType::Array(FnArgPrimitive::String)),
 | 
			
		||||
                        optional: false,
 | 
			
		||||
@ -3233,6 +3258,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
                            },
 | 
			
		||||
                            68,
 | 
			
		||||
                            72,
 | 
			
		||||
                            module_id,
 | 
			
		||||
                        ),
 | 
			
		||||
                        type_: Some(FnArgType::Primitive(FnArgPrimitive::String)),
 | 
			
		||||
                        optional: true,
 | 
			
		||||
@ -3249,9 +3275,8 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
        let some_program_string = r#"fn thing = () => {thing: number, things: string[], more?: string} {
 | 
			
		||||
    return 1
 | 
			
		||||
}"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let program = crate::parser::parse(some_program_string, module_id).unwrap();
 | 
			
		||||
 | 
			
		||||
        // Check the program output for the types of the parameters.
 | 
			
		||||
        let function = program.body.first().unwrap();
 | 
			
		||||
@ -3275,6 +3300,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
                            },
 | 
			
		||||
                            18,
 | 
			
		||||
                            23,
 | 
			
		||||
                            module_id,
 | 
			
		||||
                        ),
 | 
			
		||||
                        type_: Some(FnArgType::Primitive(FnArgPrimitive::Number)),
 | 
			
		||||
                        optional: false,
 | 
			
		||||
@ -3288,6 +3314,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
                            },
 | 
			
		||||
                            33,
 | 
			
		||||
                            39,
 | 
			
		||||
                            module_id,
 | 
			
		||||
                        ),
 | 
			
		||||
                        type_: Some(FnArgType::Array(FnArgPrimitive::String)),
 | 
			
		||||
                        optional: false,
 | 
			
		||||
@ -3301,6 +3328,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
                            },
 | 
			
		||||
                            51,
 | 
			
		||||
                            55,
 | 
			
		||||
                            module_id,
 | 
			
		||||
                        ),
 | 
			
		||||
                        type_: Some(FnArgType::Primitive(FnArgPrimitive::String)),
 | 
			
		||||
                        optional: true,
 | 
			
		||||
@ -3349,6 +3377,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
                        },
 | 
			
		||||
                        start: 0,
 | 
			
		||||
                        end: 0,
 | 
			
		||||
                        module_id: ModuleId::default(),
 | 
			
		||||
                    },
 | 
			
		||||
                    return_type: None,
 | 
			
		||||
                    digest: None,
 | 
			
		||||
@ -3375,6 +3404,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
                        },
 | 
			
		||||
                        start: 0,
 | 
			
		||||
                        end: 0,
 | 
			
		||||
                        module_id: ModuleId::default(),
 | 
			
		||||
                    },
 | 
			
		||||
                    return_type: None,
 | 
			
		||||
                    digest: None,
 | 
			
		||||
@ -3412,6 +3442,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
                        },
 | 
			
		||||
                        start: 0,
 | 
			
		||||
                        end: 0,
 | 
			
		||||
                        module_id: ModuleId::default(),
 | 
			
		||||
                    },
 | 
			
		||||
                    return_type: None,
 | 
			
		||||
                    digest: None,
 | 
			
		||||
@ -3429,9 +3460,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_parse_object_bool() {
 | 
			
		||||
        let some_program_string = r#"some_func({thing: true, other_thing: false})"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        // We want to get the bool and verify it is a bool.
 | 
			
		||||
 | 
			
		||||
@ -3479,14 +3508,12 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
    |> startProfileAt([0, 0], %)
 | 
			
		||||
    |> line([5, 5], %, $xLine)
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        let result = crate::parser::top_level_parse(some_program_string);
 | 
			
		||||
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err().to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([76, 82])], message: "Cannot assign a tag to a reserved keyword: xLine" }"#
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([76, 82, 0])], message: "Cannot assign a tag to a reserved keyword: xLine" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -3496,14 +3523,12 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
    |> startProfileAt([0, 0], %)
 | 
			
		||||
    |> line([5, 5], %, $)
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        let result = crate::parser::top_level_parse(some_program_string);
 | 
			
		||||
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err().to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([57, 59])], message: "Unexpected token: |>" }"#
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([57, 59, 0])], message: "Unexpected token: |>" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -3513,17 +3538,13 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
    |> startProfileAt([0, 0], %)
 | 
			
		||||
    |> line([5, 5], %)
 | 
			
		||||
"#;
 | 
			
		||||
        let prog1_tokens = crate::token::lexer(prog1_string).unwrap();
 | 
			
		||||
        let prog1_parser = crate::parser::Parser::new(prog1_tokens);
 | 
			
		||||
        let prog1_digest = prog1_parser.ast().unwrap().compute_digest();
 | 
			
		||||
        let prog1_digest = crate::parser::top_level_parse(prog1_string).unwrap().compute_digest();
 | 
			
		||||
 | 
			
		||||
        let prog2_string = r#"startSketchOn('XY')
 | 
			
		||||
    |> startProfileAt([0, 2], %)
 | 
			
		||||
    |> line([5, 5], %)
 | 
			
		||||
"#;
 | 
			
		||||
        let prog2_tokens = crate::token::lexer(prog2_string).unwrap();
 | 
			
		||||
        let prog2_parser = crate::parser::Parser::new(prog2_tokens);
 | 
			
		||||
        let prog2_digest = prog2_parser.ast().unwrap().compute_digest();
 | 
			
		||||
        let prog2_digest = crate::parser::top_level_parse(prog2_string).unwrap().compute_digest();
 | 
			
		||||
 | 
			
		||||
        assert!(prog1_digest != prog2_digest);
 | 
			
		||||
 | 
			
		||||
@ -3531,9 +3552,7 @@ const cylinder = startSketchOn('-XZ')
 | 
			
		||||
    |> startProfileAt([0, 0], %)
 | 
			
		||||
    |> line([5, 5], %)
 | 
			
		||||
"#;
 | 
			
		||||
        let prog3_tokens = crate::token::lexer(prog3_string).unwrap();
 | 
			
		||||
        let prog3_parser = crate::parser::Parser::new(prog3_tokens);
 | 
			
		||||
        let prog3_digest = prog3_parser.ast().unwrap().compute_digest();
 | 
			
		||||
        let prog3_digest = crate::parser::top_level_parse(prog3_string).unwrap().compute_digest();
 | 
			
		||||
 | 
			
		||||
        assert_eq!(prog1_digest, prog3_digest);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -50,7 +50,7 @@ impl Node<IfExpression> {
 | 
			
		||||
impl Node<ElseIf> {
 | 
			
		||||
    #[allow(dead_code)]
 | 
			
		||||
    fn source_ranges(&self) -> Vec<SourceRange> {
 | 
			
		||||
        vec![SourceRange([self.start, self.end])]
 | 
			
		||||
        vec![SourceRange([self.start, self.end, self.module_id.as_usize()])]
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -235,7 +235,7 @@ pub(crate) async fn execute_pipe_body(
 | 
			
		||||
    // they use the % from the parent. After all, this pipe expression hasn't been executed yet, so it doesn't have any % value
 | 
			
		||||
    // of its own.
 | 
			
		||||
    let meta = Metadata {
 | 
			
		||||
        source_range: SourceRange([first.start(), first.end()]),
 | 
			
		||||
        source_range: SourceRange::from(first),
 | 
			
		||||
    };
 | 
			
		||||
    let output = ctx
 | 
			
		||||
        .execute_expr(first, exec_state, &meta, StatementKind::Expression)
 | 
			
		||||
@ -285,7 +285,7 @@ async fn inner_execute_pipe_body(
 | 
			
		||||
            | Expr::None(_) => {}
 | 
			
		||||
        };
 | 
			
		||||
        let metadata = Metadata {
 | 
			
		||||
            source_range: SourceRange([expression.start(), expression.end()]),
 | 
			
		||||
            source_range: SourceRange::from(expression),
 | 
			
		||||
        };
 | 
			
		||||
        let output = ctx
 | 
			
		||||
            .execute_expr(expression, exec_state, &metadata, StatementKind::Expression)
 | 
			
		||||
 | 
			
		||||
@ -6,9 +6,11 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::ConstraintLevel,
 | 
			
		||||
    executor::{KclValue, SourceRange, UserVal},
 | 
			
		||||
    executor::{KclValue, UserVal},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use super::Node;
 | 
			
		||||
 | 
			
		||||
const KCL_NONE_ID: &str = "KCL_NONE_ID";
 | 
			
		||||
 | 
			
		||||
/// KCL value for an optional parameter which was not given an argument.
 | 
			
		||||
@ -19,9 +21,6 @@ const KCL_NONE_ID: &str = "KCL_NONE_ID";
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(tag = "type")]
 | 
			
		||||
pub struct KclNone {
 | 
			
		||||
    // TODO: Convert this to be an Option<SourceRange>.
 | 
			
		||||
    pub start: usize,
 | 
			
		||||
    pub end: usize,
 | 
			
		||||
    #[serde(deserialize_with = "deser_private")]
 | 
			
		||||
    #[ts(skip)]
 | 
			
		||||
    #[schemars(skip)]
 | 
			
		||||
@ -29,12 +28,8 @@ pub struct KclNone {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl KclNone {
 | 
			
		||||
    pub fn new(start: usize, end: usize) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            start,
 | 
			
		||||
            end,
 | 
			
		||||
            __private: Private {},
 | 
			
		||||
        }
 | 
			
		||||
    pub fn new() -> Self {
 | 
			
		||||
        Self { __private: Private {} }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -63,12 +58,6 @@ where
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&KclNone> for SourceRange {
 | 
			
		||||
    fn from(v: &KclNone) -> Self {
 | 
			
		||||
        Self([v.start, v.end])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&KclNone> for UserVal {
 | 
			
		||||
    fn from(none: &KclNone) -> Self {
 | 
			
		||||
        UserVal {
 | 
			
		||||
@ -85,16 +74,18 @@ impl From<&KclNone> for KclValue {
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl KclNone {
 | 
			
		||||
    pub fn source_range(&self) -> SourceRange {
 | 
			
		||||
        SourceRange([self.start, self.end])
 | 
			
		||||
impl From<&Node<KclNone>> for KclValue {
 | 
			
		||||
    fn from(none: &Node<KclNone>) -> Self {
 | 
			
		||||
        Self::from(&none.inner)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Node<KclNone> {
 | 
			
		||||
    /// Get the constraint level.
 | 
			
		||||
    /// KCL None is never constrained.
 | 
			
		||||
    pub fn get_constraint_level(&self) -> ConstraintLevel {
 | 
			
		||||
        ConstraintLevel::None {
 | 
			
		||||
            source_ranges: vec![self.source_range()],
 | 
			
		||||
            source_ranges: self.as_source_ranges(),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										66
									
								
								src/wasm-lib/kcl/src/ast/types/source_range.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/wasm-lib/kcl/src/ast/types/source_range.rs
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,66 @@
 | 
			
		||||
use super::{BinaryPart, BodyItem, Expr, LiteralIdentifier, MemberObject, ModuleId};
 | 
			
		||||
 | 
			
		||||
impl BodyItem {
 | 
			
		||||
    pub fn module_id(&self) -> ModuleId {
 | 
			
		||||
        match self {
 | 
			
		||||
            BodyItem::ImportStatement(stmt) => stmt.module_id,
 | 
			
		||||
            BodyItem::ExpressionStatement(expression_statement) => expression_statement.module_id,
 | 
			
		||||
            BodyItem::VariableDeclaration(variable_declaration) => variable_declaration.module_id,
 | 
			
		||||
            BodyItem::ReturnStatement(return_statement) => return_statement.module_id,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Expr {
 | 
			
		||||
    pub fn module_id(&self) -> ModuleId {
 | 
			
		||||
        match self {
 | 
			
		||||
            Expr::Literal(literal) => literal.module_id,
 | 
			
		||||
            Expr::Identifier(identifier) => identifier.module_id,
 | 
			
		||||
            Expr::TagDeclarator(tag) => tag.module_id,
 | 
			
		||||
            Expr::BinaryExpression(binary_expression) => binary_expression.module_id,
 | 
			
		||||
            Expr::FunctionExpression(function_expression) => function_expression.module_id,
 | 
			
		||||
            Expr::CallExpression(call_expression) => call_expression.module_id,
 | 
			
		||||
            Expr::PipeExpression(pipe_expression) => pipe_expression.module_id,
 | 
			
		||||
            Expr::PipeSubstitution(pipe_substitution) => pipe_substitution.module_id,
 | 
			
		||||
            Expr::ArrayExpression(array_expression) => array_expression.module_id,
 | 
			
		||||
            Expr::ArrayRangeExpression(array_range) => array_range.module_id,
 | 
			
		||||
            Expr::ObjectExpression(object_expression) => object_expression.module_id,
 | 
			
		||||
            Expr::MemberExpression(member_expression) => member_expression.module_id,
 | 
			
		||||
            Expr::UnaryExpression(unary_expression) => unary_expression.module_id,
 | 
			
		||||
            Expr::IfExpression(expr) => expr.module_id,
 | 
			
		||||
            Expr::None(none) => none.module_id,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl BinaryPart {
 | 
			
		||||
    pub fn module_id(&self) -> ModuleId {
 | 
			
		||||
        match self {
 | 
			
		||||
            BinaryPart::Literal(literal) => literal.module_id,
 | 
			
		||||
            BinaryPart::Identifier(identifier) => identifier.module_id,
 | 
			
		||||
            BinaryPart::BinaryExpression(binary_expression) => binary_expression.module_id,
 | 
			
		||||
            BinaryPart::CallExpression(call_expression) => call_expression.module_id,
 | 
			
		||||
            BinaryPart::UnaryExpression(unary_expression) => unary_expression.module_id,
 | 
			
		||||
            BinaryPart::MemberExpression(member_expression) => member_expression.module_id,
 | 
			
		||||
            BinaryPart::IfExpression(e) => e.module_id,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl MemberObject {
 | 
			
		||||
    pub fn module_id(&self) -> ModuleId {
 | 
			
		||||
        match self {
 | 
			
		||||
            MemberObject::MemberExpression(member_expression) => member_expression.module_id,
 | 
			
		||||
            MemberObject::Identifier(identifier) => identifier.module_id,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl LiteralIdentifier {
 | 
			
		||||
    pub fn module_id(&self) -> ModuleId {
 | 
			
		||||
        match self {
 | 
			
		||||
            LiteralIdentifier::Identifier(identifier) => identifier.module_id,
 | 
			
		||||
            LiteralIdentifier::Literal(literal) => literal.module_id,
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use thiserror::Error;
 | 
			
		||||
use tower_lsp::lsp_types::{Diagnostic, DiagnosticSeverity};
 | 
			
		||||
 | 
			
		||||
use crate::{executor::SourceRange, lsp::IntoDiagnostic};
 | 
			
		||||
use crate::{ast::types::ModuleId, executor::SourceRange, lsp::IntoDiagnostic};
 | 
			
		||||
 | 
			
		||||
#[derive(Error, Debug, Serialize, Deserialize, ts_rs::TS, Clone, PartialEq, Eq)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
@ -147,6 +147,13 @@ impl IntoDiagnostic for KclError {
 | 
			
		||||
        let message = self.get_message();
 | 
			
		||||
        let source_ranges = self.source_ranges();
 | 
			
		||||
 | 
			
		||||
        // Limit to only errors in the top-level file.
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let source_ranges = source_ranges
 | 
			
		||||
            .iter()
 | 
			
		||||
            .filter(|r| r.module_id() == module_id)
 | 
			
		||||
            .collect::<Vec<_>>();
 | 
			
		||||
 | 
			
		||||
        Diagnostic {
 | 
			
		||||
            range: source_ranges.first().map(|r| r.to_lsp_range(code)).unwrap_or_default(),
 | 
			
		||||
            severity: Some(self.severity()),
 | 
			
		||||
 | 
			
		||||
@ -7,6 +7,7 @@ use std::{
 | 
			
		||||
 | 
			
		||||
use anyhow::Result;
 | 
			
		||||
use async_recursion::async_recursion;
 | 
			
		||||
use indexmap::IndexMap;
 | 
			
		||||
use kcmc::{
 | 
			
		||||
    each_cmd as mcmd,
 | 
			
		||||
    ok_response::{output::TakeSnapshot, OkModelingCmdResponse},
 | 
			
		||||
@ -26,8 +27,8 @@ type Point3D = kcmc::shared::Point3d<f64>;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{
 | 
			
		||||
        human_friendly_type, BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, Node, NodeRef, Program,
 | 
			
		||||
        TagDeclarator, TagNode,
 | 
			
		||||
        human_friendly_type, BodyItem, Expr, FunctionExpression, ItemVisibility, KclNone, ModuleId, Node, NodeRef,
 | 
			
		||||
        Program, TagDeclarator, TagNode,
 | 
			
		||||
    },
 | 
			
		||||
    engine::{EngineManager, ExecutionKind},
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
@ -55,11 +56,32 @@ pub struct ExecState {
 | 
			
		||||
    /// The stack of import statements for detecting circular module imports.
 | 
			
		||||
    /// If this is empty, we're not currently executing an import statement.
 | 
			
		||||
    pub import_stack: Vec<std::path::PathBuf>,
 | 
			
		||||
    /// Map from source file absolute path to module ID.
 | 
			
		||||
    pub path_to_source_id: IndexMap<std::path::PathBuf, ModuleId>,
 | 
			
		||||
    /// Map from module ID to module info.
 | 
			
		||||
    pub module_infos: IndexMap<ModuleId, ModuleInfo>,
 | 
			
		||||
    /// The directory of the current project.  This is used for resolving import
 | 
			
		||||
    /// paths.  If None is given, the current working directory is used.
 | 
			
		||||
    pub project_directory: Option<String>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl ExecState {
 | 
			
		||||
    pub fn add_module(&mut self, path: std::path::PathBuf) -> ModuleId {
 | 
			
		||||
        // Need to avoid borrowing self in the closure.
 | 
			
		||||
        let new_module_id = ModuleId::from_usize(self.path_to_source_id.len());
 | 
			
		||||
        let mut is_new = false;
 | 
			
		||||
        let id = *self.path_to_source_id.entry(path.clone()).or_insert_with(|| {
 | 
			
		||||
            is_new = true;
 | 
			
		||||
            new_module_id
 | 
			
		||||
        });
 | 
			
		||||
        if is_new {
 | 
			
		||||
            let module_info = ModuleInfo { id, path };
 | 
			
		||||
            self.module_infos.insert(id, module_info);
 | 
			
		||||
        }
 | 
			
		||||
        id
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
#[serde(rename_all = "camelCase")]
 | 
			
		||||
@ -1373,21 +1395,33 @@ pub enum BodyType {
 | 
			
		||||
    Block,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Info about a module.  Right now, this is pretty minimal.  We hope to cache
 | 
			
		||||
/// modules here in the future.
 | 
			
		||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize, ts_rs::TS, JsonSchema)]
 | 
			
		||||
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
pub struct ModuleInfo {
 | 
			
		||||
    /// The ID of the module.
 | 
			
		||||
    id: ModuleId,
 | 
			
		||||
    /// Absolute path of the module's source file.
 | 
			
		||||
    path: std::path::PathBuf,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[derive(Debug, Default, Deserialize, Serialize, PartialEq, Copy, Clone, ts_rs::TS, JsonSchema, Hash, Eq)]
 | 
			
		||||
#[cfg_attr(feature = "pyo3", pyo3::pyclass)]
 | 
			
		||||
#[ts(export)]
 | 
			
		||||
pub struct SourceRange(#[ts(type = "[number, number]")] pub [usize; 2]);
 | 
			
		||||
pub struct SourceRange(#[ts(type = "[number, number]")] pub [usize; 3]);
 | 
			
		||||
 | 
			
		||||
impl From<[usize; 2]> for SourceRange {
 | 
			
		||||
    fn from(value: [usize; 2]) -> Self {
 | 
			
		||||
impl From<[usize; 3]> for SourceRange {
 | 
			
		||||
    fn from(value: [usize; 3]) -> Self {
 | 
			
		||||
        Self(value)
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl SourceRange {
 | 
			
		||||
    /// Create a new source range.
 | 
			
		||||
    pub fn new(start: usize, end: usize) -> Self {
 | 
			
		||||
        Self([start, end])
 | 
			
		||||
    pub fn new(start: usize, end: usize, module_id: ModuleId) -> Self {
 | 
			
		||||
        Self([start, end, module_id.as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the start of the range.
 | 
			
		||||
@ -1400,6 +1434,11 @@ impl SourceRange {
 | 
			
		||||
        self.0[1]
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Get the module ID of the range.
 | 
			
		||||
    pub fn module_id(&self) -> ModuleId {
 | 
			
		||||
        ModuleId::from_usize(self.0[2])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Check if the range contains a position.
 | 
			
		||||
    pub fn contains(&self, pos: usize) -> bool {
 | 
			
		||||
        pos >= self.start() && pos <= self.end()
 | 
			
		||||
@ -1533,7 +1572,7 @@ impl From<SourceRange> for Metadata {
 | 
			
		||||
impl<T> From<NodeRef<'_, T>> for Metadata {
 | 
			
		||||
    fn from(node: NodeRef<'_, T>) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            source_range: SourceRange::new(node.start, node.end),
 | 
			
		||||
            source_range: SourceRange::new(node.start, node.end, node.module_id),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -2171,6 +2210,8 @@ impl ExecutorContext {
 | 
			
		||||
            project_directory,
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
        };
 | 
			
		||||
        // TODO: Use the top-level file's path.
 | 
			
		||||
        exec_state.add_module(std::path::PathBuf::from(""));
 | 
			
		||||
        // Before we even start executing the program, set the units.
 | 
			
		||||
        self.engine
 | 
			
		||||
            .batch_modeling_cmd(
 | 
			
		||||
@ -2210,6 +2251,13 @@ impl ExecutorContext {
 | 
			
		||||
                BodyItem::ImportStatement(import_stmt) => {
 | 
			
		||||
                    let source_range = SourceRange::from(import_stmt);
 | 
			
		||||
                    let path = import_stmt.path.clone();
 | 
			
		||||
                    // Empty path is used by the top-level module.
 | 
			
		||||
                    if path.is_empty() {
 | 
			
		||||
                        return Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                            message: "import path cannot be empty".to_owned(),
 | 
			
		||||
                            source_ranges: vec![source_range],
 | 
			
		||||
                        }));
 | 
			
		||||
                    }
 | 
			
		||||
                    let resolved_path = if let Some(project_dir) = &exec_state.project_directory {
 | 
			
		||||
                        std::path::PathBuf::from(project_dir).join(&path)
 | 
			
		||||
                    } else {
 | 
			
		||||
@ -2230,8 +2278,9 @@ impl ExecutorContext {
 | 
			
		||||
                            source_ranges: vec![import_stmt.into()],
 | 
			
		||||
                        }));
 | 
			
		||||
                    }
 | 
			
		||||
                    let module_id = exec_state.add_module(resolved_path.clone());
 | 
			
		||||
                    let source = self.fs.read_to_string(&resolved_path, source_range).await?;
 | 
			
		||||
                    let program = crate::parser::parse(&source)?;
 | 
			
		||||
                    let program = crate::parser::parse(&source, module_id)?;
 | 
			
		||||
                    let (module_memory, module_exports) = {
 | 
			
		||||
                        exec_state.import_stack.push(resolved_path.clone());
 | 
			
		||||
                        let original_execution = self.engine.replace_execution_kind(ExecutionKind::Isolated);
 | 
			
		||||
@ -2359,7 +2408,7 @@ impl ExecutorContext {
 | 
			
		||||
                    // True here tells the engine to flush all the end commands as well like fillets
 | 
			
		||||
                    // and chamfers where the engine would otherwise eat the ID of the segments.
 | 
			
		||||
                    true,
 | 
			
		||||
                    SourceRange([program.end, program.end]),
 | 
			
		||||
                    SourceRange([program.end, program.end, program.module_id.as_usize()]),
 | 
			
		||||
                )
 | 
			
		||||
                .await?;
 | 
			
		||||
        }
 | 
			
		||||
@ -2525,7 +2574,12 @@ fn assign_args_to_params(
 | 
			
		||||
            if param.optional {
 | 
			
		||||
                // If the corresponding parameter is optional,
 | 
			
		||||
                // then it's fine, the user doesn't need to supply it.
 | 
			
		||||
                let none = KclNone::new(param.identifier.start, param.identifier.end);
 | 
			
		||||
                let none = Node {
 | 
			
		||||
                    inner: KclNone::new(),
 | 
			
		||||
                    start: param.identifier.start,
 | 
			
		||||
                    end: param.identifier.end,
 | 
			
		||||
                    module_id: param.identifier.module_id,
 | 
			
		||||
                };
 | 
			
		||||
                fn_memory.add(
 | 
			
		||||
                    ¶m.identifier.name,
 | 
			
		||||
                    KclValue::from(&none),
 | 
			
		||||
@ -2586,9 +2640,8 @@ mod tests {
 | 
			
		||||
    use crate::ast::types::{Identifier, Node, Parameter};
 | 
			
		||||
 | 
			
		||||
    pub async fn parse_execute(code: &str) -> Result<ProgramMemory> {
 | 
			
		||||
        let tokens = crate::token::lexer(code)?;
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast()?;
 | 
			
		||||
        let program = crate::parser::top_level_parse(code)?;
 | 
			
		||||
 | 
			
		||||
        let ctx = ExecutorContext {
 | 
			
		||||
            engine: Arc::new(Box::new(crate::engine::conn_mock::EngineConnection::new().await?)),
 | 
			
		||||
            fs: Arc::new(crate::fs::FileManager::new()),
 | 
			
		||||
@ -3027,7 +3080,7 @@ const answer = returnX()"#;
 | 
			
		||||
            err,
 | 
			
		||||
            KclError::UndefinedValue(KclErrorDetails {
 | 
			
		||||
                message: "memory item key `x` is not defined".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([64, 65]), SourceRange([97, 106])],
 | 
			
		||||
                source_ranges: vec![SourceRange([64, 65, 0]), SourceRange([97, 106, 0])],
 | 
			
		||||
            }),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@ -3062,7 +3115,7 @@ let shape = layer() |> patternTransform(10, transform, %)
 | 
			
		||||
            err,
 | 
			
		||||
            KclError::UndefinedValue(KclErrorDetails {
 | 
			
		||||
                message: "memory item key `x` is not defined".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([80, 81])],
 | 
			
		||||
                source_ranges: vec![SourceRange([80, 81, 0])],
 | 
			
		||||
            }),
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
@ -3317,7 +3370,7 @@ let notNull = !myNull
 | 
			
		||||
            parse_execute(code1).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: null".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([56, 63])],
 | 
			
		||||
                source_ranges: vec![SourceRange([56, 63, 0])],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -3326,7 +3379,7 @@ let notNull = !myNull
 | 
			
		||||
            parse_execute(code2).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: 0".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([14, 16])],
 | 
			
		||||
                source_ranges: vec![SourceRange([14, 16, 0])],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -3337,7 +3390,7 @@ let notEmptyString = !""
 | 
			
		||||
            parse_execute(code3).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: \"\"".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([22, 25])],
 | 
			
		||||
                source_ranges: vec![SourceRange([22, 25, 0])],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -3349,7 +3402,7 @@ let notMember = !obj.a
 | 
			
		||||
            parse_execute(code4).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: 1".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([36, 42])],
 | 
			
		||||
                source_ranges: vec![SourceRange([36, 42, 0])],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -3360,7 +3413,7 @@ let notArray = !a";
 | 
			
		||||
            parse_execute(code5).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: []".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([27, 29])],
 | 
			
		||||
                source_ranges: vec![SourceRange([27, 29, 0])],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -3371,7 +3424,7 @@ let notObject = !x";
 | 
			
		||||
            parse_execute(code6).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                message: "Cannot apply unary operator ! to non-boolean value: {}".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([28, 30])],
 | 
			
		||||
                source_ranges: vec![SourceRange([28, 30, 0])],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -3424,7 +3477,7 @@ let notTagIdentifier = !myTag";
 | 
			
		||||
            parse_execute(code10).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Syntax(KclErrorDetails {
 | 
			
		||||
                message: "Unexpected token: !".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([14, 15])],
 | 
			
		||||
                source_ranges: vec![SourceRange([14, 15, 0])],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -3437,7 +3490,7 @@ let notPipeSub = 1 |> identity(!%))";
 | 
			
		||||
            parse_execute(code11).await.unwrap_err().downcast::<KclError>().unwrap(),
 | 
			
		||||
            KclError::Syntax(KclErrorDetails {
 | 
			
		||||
                message: "Unexpected token: |>".to_owned(),
 | 
			
		||||
                source_ranges: vec![SourceRange([54, 56])],
 | 
			
		||||
                source_ranges: vec![SourceRange([54, 56, 0])],
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
 | 
			
		||||
@ -3483,7 +3536,7 @@ test([0, 0])
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err().to_string(),
 | 
			
		||||
            r#"undefined value: KclErrorDetails { source_ranges: [SourceRange([10, 34])], message: "Result of user-defined function test is undefined" }"#.to_owned()
 | 
			
		||||
            r#"undefined value: KclErrorDetails { source_ranges: [SourceRange([10, 34, 0])], message: "Result of user-defined function test is undefined" }"#.to_owned()
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -3600,7 +3653,7 @@ let w = f() + f()
 | 
			
		||||
                vec![req_param("x")],
 | 
			
		||||
                vec![],
 | 
			
		||||
                Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                    source_ranges: vec![SourceRange([0, 0])],
 | 
			
		||||
                    source_ranges: vec![SourceRange([0, 0, 0])],
 | 
			
		||||
                    message: "Expected 1 arguments, got 0".to_owned(),
 | 
			
		||||
                })),
 | 
			
		||||
            ),
 | 
			
		||||
@ -3618,7 +3671,7 @@ let w = f() + f()
 | 
			
		||||
                vec![req_param("x"), opt_param("y")],
 | 
			
		||||
                vec![],
 | 
			
		||||
                Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                    source_ranges: vec![SourceRange([0, 0])],
 | 
			
		||||
                    source_ranges: vec![SourceRange([0, 0, 0])],
 | 
			
		||||
                    message: "Expected 1-2 arguments, got 0".to_owned(),
 | 
			
		||||
                })),
 | 
			
		||||
            ),
 | 
			
		||||
@ -3645,7 +3698,7 @@ let w = f() + f()
 | 
			
		||||
                vec![req_param("x"), opt_param("y")],
 | 
			
		||||
                vec![mem(1), mem(2), mem(3)],
 | 
			
		||||
                Err(KclError::Semantic(KclErrorDetails {
 | 
			
		||||
                    source_ranges: vec![SourceRange([0, 0])],
 | 
			
		||||
                    source_ranges: vec![SourceRange([0, 0, 0])],
 | 
			
		||||
                    message: "Expected 1-2 arguments, got 3".to_owned(),
 | 
			
		||||
                })),
 | 
			
		||||
            ),
 | 
			
		||||
@ -3661,6 +3714,7 @@ let w = f() + f()
 | 
			
		||||
                    },
 | 
			
		||||
                    start: 0,
 | 
			
		||||
                    end: 0,
 | 
			
		||||
                    module_id: ModuleId::default(),
 | 
			
		||||
                },
 | 
			
		||||
                return_type: None,
 | 
			
		||||
                digest: None,
 | 
			
		||||
 | 
			
		||||
@ -29,7 +29,10 @@ fn lint_lower_camel_case_var(decl: &VariableDeclarator) -> Result<Vec<Discovered
 | 
			
		||||
    let name = &ident.name;
 | 
			
		||||
 | 
			
		||||
    if !name.is_case(convert_case::Case::Camel) {
 | 
			
		||||
        findings.push(Z0001.at(format!("found '{}'", name), SourceRange::new(ident.start, ident.end)));
 | 
			
		||||
        findings.push(Z0001.at(
 | 
			
		||||
            format!("found '{}'", name),
 | 
			
		||||
            SourceRange::new(ident.start, ident.end, ident.module_id),
 | 
			
		||||
        ));
 | 
			
		||||
        return Ok(findings);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -42,7 +45,10 @@ fn lint_lower_camel_case_property(decl: &ObjectProperty) -> Result<Vec<Discovere
 | 
			
		||||
    let name = &ident.name;
 | 
			
		||||
 | 
			
		||||
    if !name.is_case(convert_case::Case::Camel) {
 | 
			
		||||
        findings.push(Z0001.at(format!("found '{}'", name), SourceRange::new(ident.start, ident.end)));
 | 
			
		||||
        findings.push(Z0001.at(
 | 
			
		||||
            format!("found '{}'", name),
 | 
			
		||||
            SourceRange::new(ident.start, ident.end, ident.module_id),
 | 
			
		||||
        ));
 | 
			
		||||
        return Ok(findings);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -144,7 +144,7 @@ pub fn lint_should_be_offset_plane(node: Node) -> Result<Vec<Discovered>> {
 | 
			
		||||
        return Ok(vec![]);
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    let call_source_range = SourceRange::new(call.start, call.end);
 | 
			
		||||
    let call_source_range = SourceRange::new(call.start, call.end, call.module_id);
 | 
			
		||||
    Ok(vec![Z0003.at(
 | 
			
		||||
        format!(
 | 
			
		||||
            "custom plane in startSketchOn; offsetPlane from {} would work here",
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ fn lint_too_many_args_std_lib_function(
 | 
			
		||||
        if exp.arguments.len() != 2 {
 | 
			
		||||
            findings.push(Z0002.at(
 | 
			
		||||
                format!("expected 2 arguments, found {}", exp.arguments.len()),
 | 
			
		||||
                SourceRange::new(exp.start, exp.end),
 | 
			
		||||
                SourceRange::new(exp.start, exp.end, exp.module_id),
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
        return Ok(findings);
 | 
			
		||||
@ -38,7 +38,7 @@ fn lint_too_many_args_std_lib_function(
 | 
			
		||||
        if exp.arguments.len() < 2 {
 | 
			
		||||
            findings.push(Z0002.at(
 | 
			
		||||
                format!("expected at least 2 arguments, found {}", exp.arguments.len()),
 | 
			
		||||
                SourceRange::new(exp.start, exp.end),
 | 
			
		||||
                SourceRange::new(exp.start, exp.end, exp.module_id),
 | 
			
		||||
            ));
 | 
			
		||||
        }
 | 
			
		||||
        return Ok(findings);
 | 
			
		||||
@ -48,7 +48,7 @@ fn lint_too_many_args_std_lib_function(
 | 
			
		||||
    if exp.arguments.len() > fn_args_len {
 | 
			
		||||
        findings.push(Z0002.at(
 | 
			
		||||
            format!("expected {} arguments, found {}", fn_args_len, exp.arguments.len()),
 | 
			
		||||
            SourceRange::new(exp.start, exp.end),
 | 
			
		||||
            SourceRange::new(exp.start, exp.end, exp.module_id),
 | 
			
		||||
        ));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -182,9 +182,7 @@ mod test {
 | 
			
		||||
 | 
			
		||||
    macro_rules! assert_no_finding {
 | 
			
		||||
        ( $check:expr, $finding:expr, $kcl:expr ) => {
 | 
			
		||||
            let tokens = $crate::token::lexer($kcl).unwrap();
 | 
			
		||||
            let parser = $crate::parser::Parser::new(tokens);
 | 
			
		||||
            let prog = parser.ast().unwrap();
 | 
			
		||||
            let prog = $crate::parser::top_level_parse($kcl).unwrap();
 | 
			
		||||
            for discovered_finding in prog.lint($check).unwrap() {
 | 
			
		||||
                if discovered_finding.finding == $finding {
 | 
			
		||||
                    assert!(false, "Finding {:?} was emitted", $finding.code);
 | 
			
		||||
@ -195,9 +193,7 @@ mod test {
 | 
			
		||||
 | 
			
		||||
    macro_rules! assert_finding {
 | 
			
		||||
        ( $check:expr, $finding:expr, $kcl:expr ) => {
 | 
			
		||||
            let tokens = $crate::token::lexer($kcl).unwrap();
 | 
			
		||||
            let parser = $crate::parser::Parser::new(tokens);
 | 
			
		||||
            let prog = parser.ast().unwrap();
 | 
			
		||||
            let prog = $crate::parser::top_level_parse($kcl).unwrap();
 | 
			
		||||
 | 
			
		||||
            for discovered_finding in prog.lint($check).unwrap() {
 | 
			
		||||
                if discovered_finding.finding == $finding {
 | 
			
		||||
 | 
			
		||||
@ -40,7 +40,7 @@ use tower_lsp::{
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{Expr, Node, NodeRef, VariableKind},
 | 
			
		||||
    ast::types::{Expr, ModuleId, Node, NodeRef, VariableKind},
 | 
			
		||||
    executor::{IdGenerator, SourceRange},
 | 
			
		||||
    lsp::{backend::Backend as _, util::IntoDiagnostic},
 | 
			
		||||
    parser::PIPE_OPERATOR,
 | 
			
		||||
@ -188,7 +188,8 @@ impl crate::lsp::backend::Backend for Backend {
 | 
			
		||||
        // We already updated the code map in the shared backend.
 | 
			
		||||
 | 
			
		||||
        // Lets update the tokens.
 | 
			
		||||
        let tokens = match crate::token::lexer(¶ms.text) {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let tokens = match crate::token::lexer(¶ms.text, module_id) {
 | 
			
		||||
            Ok(tokens) => tokens,
 | 
			
		||||
            Err(err) => {
 | 
			
		||||
                self.add_to_diagnostics(¶ms, &[err], true).await;
 | 
			
		||||
@ -1235,7 +1236,8 @@ impl LanguageServer for Backend {
 | 
			
		||||
        // Parse the ast.
 | 
			
		||||
        // I don't know if we need to do this again since it should be updated in the context.
 | 
			
		||||
        // But I figure better safe than sorry since this will write back out to the file.
 | 
			
		||||
        let Ok(tokens) = crate::token::lexer(current_code) else {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let Ok(tokens) = crate::token::lexer(current_code, module_id) else {
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        };
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
@ -1251,7 +1253,7 @@ impl LanguageServer for Backend {
 | 
			
		||||
            },
 | 
			
		||||
            0,
 | 
			
		||||
        );
 | 
			
		||||
        let source_range = SourceRange([0, current_code.len()]);
 | 
			
		||||
        let source_range = SourceRange::new(0, current_code.len(), module_id);
 | 
			
		||||
        let range = source_range.to_lsp_range(current_code);
 | 
			
		||||
        Ok(Some(vec![TextEdit {
 | 
			
		||||
            new_text: recast,
 | 
			
		||||
@ -1272,7 +1274,8 @@ impl LanguageServer for Backend {
 | 
			
		||||
        // Parse the ast.
 | 
			
		||||
        // I don't know if we need to do this again since it should be updated in the context.
 | 
			
		||||
        // But I figure better safe than sorry since this will write back out to the file.
 | 
			
		||||
        let Ok(tokens) = crate::token::lexer(current_code) else {
 | 
			
		||||
        let module_id = ModuleId::default();
 | 
			
		||||
        let Ok(tokens) = crate::token::lexer(current_code, module_id) else {
 | 
			
		||||
            return Ok(None);
 | 
			
		||||
        };
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
@ -1286,7 +1289,7 @@ impl LanguageServer for Backend {
 | 
			
		||||
        ast.rename_symbol(¶ms.new_name, pos);
 | 
			
		||||
        // Now recast it.
 | 
			
		||||
        let recast = ast.recast(&Default::default(), 0);
 | 
			
		||||
        let source_range = SourceRange([0, current_code.len() - 1]);
 | 
			
		||||
        let source_range = SourceRange::new(0, current_code.len() - 1, module_id);
 | 
			
		||||
        let range = source_range.to_lsp_range(current_code);
 | 
			
		||||
        Ok(Some(WorkspaceEdit {
 | 
			
		||||
            changes: Some(HashMap::from([(
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{Node, Program},
 | 
			
		||||
    ast::types::{ModuleId, Node, Program},
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
    token::{Token, TokenType},
 | 
			
		||||
@ -12,9 +12,15 @@ pub(crate) mod parser_impl;
 | 
			
		||||
pub const PIPE_SUBSTITUTION_OPERATOR: &str = "%";
 | 
			
		||||
pub const PIPE_OPERATOR: &str = "|>";
 | 
			
		||||
 | 
			
		||||
/// Parse the given KCL code into an AST.  This is the top-level.
 | 
			
		||||
pub fn top_level_parse(code: &str) -> Result<Node<Program>, KclError> {
 | 
			
		||||
    let module_id = ModuleId::default();
 | 
			
		||||
    parse(code, module_id)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Parse the given KCL code into an AST.
 | 
			
		||||
pub fn parse(code: &str) -> Result<Node<Program>, KclError> {
 | 
			
		||||
    let tokens = crate::token::lexer(code)?;
 | 
			
		||||
pub fn parse(code: &str, module_id: ModuleId) -> Result<Node<Program>, KclError> {
 | 
			
		||||
    let tokens = crate::token::lexer(code, module_id)?;
 | 
			
		||||
    let parser = Parser::new(tokens);
 | 
			
		||||
    parser.ast()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -5,7 +5,8 @@ mod tests {
 | 
			
		||||
        ($func_name:ident, $test_kcl_program:expr) => {
 | 
			
		||||
            #[test]
 | 
			
		||||
            fn $func_name() {
 | 
			
		||||
                if let Ok(v) = $crate::token::lexer($test_kcl_program) {
 | 
			
		||||
                let module_id = $crate::parser::ModuleId::default();
 | 
			
		||||
                if let Ok(v) = $crate::token::lexer($test_kcl_program, module_id) {
 | 
			
		||||
                    let _ = $crate::parser::Parser::new(v).ast();
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,7 @@ fn evaluate(rpn: Vec<BinaryExpressionToken>) -> Result<Node<BinaryExpression>, K
 | 
			
		||||
                };
 | 
			
		||||
                let start = left.start();
 | 
			
		||||
                let end = right.end();
 | 
			
		||||
                let module_id = left.module_id();
 | 
			
		||||
 | 
			
		||||
                BinaryPart::BinaryExpression(Node::boxed(
 | 
			
		||||
                    BinaryExpression {
 | 
			
		||||
@ -40,6 +41,7 @@ fn evaluate(rpn: Vec<BinaryExpressionToken>) -> Result<Node<BinaryExpression>, K
 | 
			
		||||
                    },
 | 
			
		||||
                    start,
 | 
			
		||||
                    end,
 | 
			
		||||
                    module_id,
 | 
			
		||||
                ))
 | 
			
		||||
            }
 | 
			
		||||
            BinaryExpressionToken::Operand(o) => o,
 | 
			
		||||
@ -60,11 +62,11 @@ fn source_range(tokens: &[BinaryExpressionToken]) -> Vec<SourceRange> {
 | 
			
		||||
        .iter()
 | 
			
		||||
        .filter_map(|op| match op {
 | 
			
		||||
            BinaryExpressionToken::Operator(_) => None,
 | 
			
		||||
            BinaryExpressionToken::Operand(o) => Some((o.start(), o.end())),
 | 
			
		||||
            BinaryExpressionToken::Operand(o) => Some((o.start(), o.end(), o.module_id())),
 | 
			
		||||
        })
 | 
			
		||||
        .collect();
 | 
			
		||||
    match (sources.first(), sources.last()) {
 | 
			
		||||
        (Some((start, _)), Some((_, end))) => vec![SourceRange([*start, *end])],
 | 
			
		||||
        (Some((start, _, module_id)), Some((_, end, _))) => vec![SourceRange([*start, *end, module_id.as_usize()])],
 | 
			
		||||
        _ => Vec::new(),
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -124,7 +126,7 @@ impl From<BinaryOperator> for BinaryExpressionToken {
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
mod tests {
 | 
			
		||||
    use super::*;
 | 
			
		||||
    use crate::ast::types::Literal;
 | 
			
		||||
    use crate::ast::types::{Literal, ModuleId};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn parse_and_evaluate() {
 | 
			
		||||
@ -138,6 +140,7 @@ mod tests {
 | 
			
		||||
                },
 | 
			
		||||
                0,
 | 
			
		||||
                0,
 | 
			
		||||
                ModuleId::default(),
 | 
			
		||||
            )))
 | 
			
		||||
        }
 | 
			
		||||
        let tests: Vec<Vec<BinaryExpressionToken>> = vec![
 | 
			
		||||
@ -158,6 +161,7 @@ mod tests {
 | 
			
		||||
                    },
 | 
			
		||||
                    0,
 | 
			
		||||
                    0,
 | 
			
		||||
                    ModuleId::default(),
 | 
			
		||||
                ))
 | 
			
		||||
                .into(),
 | 
			
		||||
                BinaryOperator::Pow.into(),
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -1,13 +1,12 @@
 | 
			
		||||
use winnow::{
 | 
			
		||||
    error::{ErrorKind, ParseError, StrContext},
 | 
			
		||||
    stream::Stream,
 | 
			
		||||
    Located,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    errors::{KclError, KclErrorDetails},
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
    token::Token,
 | 
			
		||||
    token::{Input, Token},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/// Accumulate context while backtracking errors
 | 
			
		||||
@ -20,9 +19,10 @@ pub struct ContextError<C = StrContext> {
 | 
			
		||||
    pub cause: Option<KclError>,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<ParseError<Located<&str>, winnow::error::ContextError>> for KclError {
 | 
			
		||||
    fn from(err: ParseError<Located<&str>, winnow::error::ContextError>) -> Self {
 | 
			
		||||
impl From<ParseError<Input<'_>, winnow::error::ContextError>> for KclError {
 | 
			
		||||
    fn from(err: ParseError<Input<'_>, winnow::error::ContextError>) -> Self {
 | 
			
		||||
        let (input, offset): (Vec<char>, usize) = (err.input().chars().collect(), err.offset());
 | 
			
		||||
        let module_id = err.input().state.module_id;
 | 
			
		||||
 | 
			
		||||
        if offset >= input.len() {
 | 
			
		||||
            // From the winnow docs:
 | 
			
		||||
@ -31,7 +31,7 @@ impl From<ParseError<Located<&str>, winnow::error::ContextError>> for KclError {
 | 
			
		||||
            // the end of input (input.len()) on eof errors.
 | 
			
		||||
 | 
			
		||||
            return KclError::Lexical(KclErrorDetails {
 | 
			
		||||
                source_ranges: vec![SourceRange([offset, offset])],
 | 
			
		||||
                source_ranges: vec![SourceRange([offset, offset, module_id.as_usize()])],
 | 
			
		||||
                message: "unexpected EOF while parsing".to_string(),
 | 
			
		||||
            });
 | 
			
		||||
        }
 | 
			
		||||
@ -42,7 +42,7 @@ impl From<ParseError<Located<&str>, winnow::error::ContextError>> for KclError {
 | 
			
		||||
        // TODO: Add the Winnow parser context to the error.
 | 
			
		||||
        // See https://github.com/KittyCAD/modeling-app/issues/784
 | 
			
		||||
        KclError::Lexical(KclErrorDetails {
 | 
			
		||||
            source_ranges: vec![SourceRange([offset, offset + 1])],
 | 
			
		||||
            source_ranges: vec![SourceRange([offset, offset + 1, module_id.as_usize()])],
 | 
			
		||||
            message: format!("found unknown token '{}'", bad_token),
 | 
			
		||||
        })
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{Node, Program},
 | 
			
		||||
    ast::types::{ModuleId, Node, Program},
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    parser::Parser,
 | 
			
		||||
    token::Token,
 | 
			
		||||
@ -44,7 +44,7 @@ fn read(filename: &'static str, test_name: &str) -> String {
 | 
			
		||||
 | 
			
		||||
fn tokenize(test_name: &str) {
 | 
			
		||||
    let input = read("input.kcl", test_name);
 | 
			
		||||
    let token_res = crate::token::lexer(&input);
 | 
			
		||||
    let token_res = crate::token::lexer(&input, ModuleId::default());
 | 
			
		||||
 | 
			
		||||
    assert_snapshot(test_name, "Result of tokenizing", || {
 | 
			
		||||
        insta::assert_json_snapshot!("tokens", token_res);
 | 
			
		||||
 | 
			
		||||
@ -6,7 +6,6 @@ use serde::{Deserialize, Serialize};
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{BodyItem, Expr, FunctionExpression, Node, Program},
 | 
			
		||||
    docs::{StdLibFn, StdLibFnData},
 | 
			
		||||
    token::lexer,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
pub trait KclStdLibFn: StdLibFn {
 | 
			
		||||
@ -83,8 +82,7 @@ impl Serialize for Box<dyn KclStdLibFn> {
 | 
			
		||||
/// Return the program and its single function.
 | 
			
		||||
/// Return None if those expectations aren't met.
 | 
			
		||||
pub fn extract_function(source: &str) -> Option<(Node<Program>, crate::ast::types::BoxNode<FunctionExpression>)> {
 | 
			
		||||
    let tokens = lexer(source).unwrap();
 | 
			
		||||
    let src = crate::parser::Parser::new(tokens).ast().ok()?;
 | 
			
		||||
    let src = crate::parser::top_level_parse(source).ok()?;
 | 
			
		||||
    assert_eq!(src.body.len(), 1);
 | 
			
		||||
    let BodyItem::ExpressionStatement(expr) = src.body.last()? else {
 | 
			
		||||
        panic!("expected expression statement");
 | 
			
		||||
 | 
			
		||||
@ -17,9 +17,7 @@ pub struct RequestBody {
 | 
			
		||||
/// This returns the bytes of the snapshot.
 | 
			
		||||
pub async fn execute_and_snapshot(code: &str, units: UnitLength) -> anyhow::Result<image::DynamicImage> {
 | 
			
		||||
    let ctx = new_context(units, true).await?;
 | 
			
		||||
    let tokens = crate::token::lexer(code)?;
 | 
			
		||||
    let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
    let program = parser.ast()?;
 | 
			
		||||
    let program = crate::parser::top_level_parse(code)?;
 | 
			
		||||
    do_execute_and_snapshot(&ctx, program).await.map(|(_state, snap)| snap)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -37,9 +35,7 @@ pub async fn execute_and_snapshot_ast(
 | 
			
		||||
 | 
			
		||||
pub async fn execute_and_snapshot_no_auth(code: &str, units: UnitLength) -> anyhow::Result<image::DynamicImage> {
 | 
			
		||||
    let ctx = new_context(units, false).await?;
 | 
			
		||||
    let tokens = crate::token::lexer(code)?;
 | 
			
		||||
    let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
    let program = parser.ast()?;
 | 
			
		||||
    let program = crate::parser::top_level_parse(code)?;
 | 
			
		||||
    do_execute_and_snapshot(&ctx, program).await.map(|(_state, snap)| snap)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -8,13 +8,16 @@ use tower_lsp::lsp_types::SemanticTokenType;
 | 
			
		||||
use winnow::stream::ContainsToken;
 | 
			
		||||
 | 
			
		||||
use crate::{
 | 
			
		||||
    ast::types::{ItemVisibility, VariableKind},
 | 
			
		||||
    ast::types::{ItemVisibility, ModuleId, VariableKind},
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::SourceRange,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
mod tokeniser;
 | 
			
		||||
 | 
			
		||||
// Re-export
 | 
			
		||||
pub use tokeniser::Input;
 | 
			
		||||
 | 
			
		||||
/// The types of tokens.
 | 
			
		||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Deserialize, Serialize, ts_rs::TS, JsonSchema, FromStr, Display)]
 | 
			
		||||
#[cfg_attr(feature = "pyo3", pyo3::pyclass(eq, eq_int))]
 | 
			
		||||
@ -161,6 +164,8 @@ pub struct Token {
 | 
			
		||||
    pub start: usize,
 | 
			
		||||
    /// Offset in the source code where this token ends.
 | 
			
		||||
    pub end: usize,
 | 
			
		||||
    #[serde(default, skip_serializing_if = "ModuleId::is_top_level")]
 | 
			
		||||
    pub module_id: ModuleId,
 | 
			
		||||
    pub value: String,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -177,10 +182,16 @@ impl ContainsToken<Token> for TokenType {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl Token {
 | 
			
		||||
    pub fn from_range(range: std::ops::Range<usize>, token_type: TokenType, value: String) -> Self {
 | 
			
		||||
    pub fn from_range(
 | 
			
		||||
        range: std::ops::Range<usize>,
 | 
			
		||||
        module_id: ModuleId,
 | 
			
		||||
        token_type: TokenType,
 | 
			
		||||
        value: String,
 | 
			
		||||
    ) -> Self {
 | 
			
		||||
        Self {
 | 
			
		||||
            start: range.start,
 | 
			
		||||
            end: range.end,
 | 
			
		||||
            module_id,
 | 
			
		||||
            value,
 | 
			
		||||
            token_type,
 | 
			
		||||
        }
 | 
			
		||||
@ -193,7 +204,7 @@ impl Token {
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_source_range(&self) -> SourceRange {
 | 
			
		||||
        SourceRange([self.start, self.end])
 | 
			
		||||
        SourceRange([self.start, self.end, self.module_id.as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pub fn as_source_ranges(&self) -> Vec<SourceRange> {
 | 
			
		||||
@ -227,18 +238,18 @@ impl Token {
 | 
			
		||||
 | 
			
		||||
impl From<Token> for SourceRange {
 | 
			
		||||
    fn from(token: Token) -> Self {
 | 
			
		||||
        Self([token.start, token.end])
 | 
			
		||||
        Self([token.start, token.end, token.module_id.as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
impl From<&Token> for SourceRange {
 | 
			
		||||
    fn from(token: &Token) -> Self {
 | 
			
		||||
        Self([token.start, token.end])
 | 
			
		||||
        Self([token.start, token.end, token.module_id.as_usize()])
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pub fn lexer(s: &str) -> Result<Vec<Token>, KclError> {
 | 
			
		||||
    tokeniser::lexer(s).map_err(From::from)
 | 
			
		||||
pub fn lexer(s: &str, module_id: ModuleId) -> Result<Vec<Token>, KclError> {
 | 
			
		||||
    tokeniser::lexer(s, module_id).map_err(From::from)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[cfg(test)]
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -573,7 +573,7 @@ impl FunctionExpression {
 | 
			
		||||
mod tests {
 | 
			
		||||
    use pretty_assertions::assert_eq;
 | 
			
		||||
 | 
			
		||||
    use crate::ast::types::FormatOptions;
 | 
			
		||||
    use crate::ast::types::{FormatOptions, ModuleId};
 | 
			
		||||
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_recast_if_else_if_same() {
 | 
			
		||||
@ -585,9 +585,7 @@ mod tests {
 | 
			
		||||
  5
 | 
			
		||||
}
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(input).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(input).unwrap();
 | 
			
		||||
        let output = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(output, input);
 | 
			
		||||
    }
 | 
			
		||||
@ -600,9 +598,7 @@ mod tests {
 | 
			
		||||
  5
 | 
			
		||||
}
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(input).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(input).unwrap();
 | 
			
		||||
        let output = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(output, input);
 | 
			
		||||
    }
 | 
			
		||||
@ -616,9 +612,7 @@ import a as aaa, b from "a.kcl"
 | 
			
		||||
import a, b as bbb from "a.kcl"
 | 
			
		||||
import a as aaa, b as bbb from "a.kcl"
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(input).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(input).unwrap();
 | 
			
		||||
        let output = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(output, input);
 | 
			
		||||
    }
 | 
			
		||||
@ -627,7 +621,7 @@ import a as aaa, b as bbb from "a.kcl"
 | 
			
		||||
    fn test_recast_import_as_same_name() {
 | 
			
		||||
        let input = r#"import a as a from "a.kcl"
 | 
			
		||||
"#;
 | 
			
		||||
        let program = crate::parser::parse(input).unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(input).unwrap();
 | 
			
		||||
        let output = program.recast(&Default::default(), 0);
 | 
			
		||||
        let expected = r#"import a from "a.kcl"
 | 
			
		||||
"#;
 | 
			
		||||
@ -640,9 +634,7 @@ import a as aaa, b as bbb from "a.kcl"
 | 
			
		||||
  return 0
 | 
			
		||||
}
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(input).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(input).unwrap();
 | 
			
		||||
        let output = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(output, input);
 | 
			
		||||
    }
 | 
			
		||||
@ -765,9 +757,7 @@ fn zoo = (x0, y0) => {
 | 
			
		||||
 | 
			
		||||
zoo(zoo_x, zoo_y)
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted, some_program_string);
 | 
			
		||||
@ -836,9 +826,7 @@ outsideRevolve = startSketchOn('XZ')
 | 
			
		||||
  |> line([overHangLength - thickness, 0], %)
 | 
			
		||||
  |> close(%)
 | 
			
		||||
  |> revolve({ axis: 'y' }, %)"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -914,9 +902,7 @@ outsideRevolve = startSketchOn('XZ')
 | 
			
		||||
        let some_program_string = r#"bing = { yo: 55 }
 | 
			
		||||
myNestedVar = [{ prop: callExp(bing.yo) }]
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted, some_program_string);
 | 
			
		||||
@ -927,9 +913,7 @@ myNestedVar = [{ prop: callExp(bing.yo) }]
 | 
			
		||||
        let some_program_string = r#"bing = { yo: 55 }
 | 
			
		||||
myNestedVar = [callExp(bing.yo)]
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted, some_program_string);
 | 
			
		||||
@ -941,9 +925,7 @@ myNestedVar = [callExp(bing.yo)]
 | 
			
		||||
ten = 10
 | 
			
		||||
bar = [0 + 1 .. ten]
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted, some_program_string);
 | 
			
		||||
@ -957,9 +939,7 @@ bar = [0 + 1 .. ten]
 | 
			
		||||
 | 
			
		||||
thing ( 1 )
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -982,9 +962,7 @@ myNestedVar = [
 | 
			
		||||
}
 | 
			
		||||
]
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1000,9 +978,7 @@ myNestedVar = [
 | 
			
		||||
    #[test]
 | 
			
		||||
    fn test_recast_empty_file() {
 | 
			
		||||
        let some_program_string = r#""#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        // Its VERY important this comes back with zero new lines.
 | 
			
		||||
@ -1013,9 +989,7 @@ myNestedVar = [
 | 
			
		||||
    fn test_recast_empty_file_new_line() {
 | 
			
		||||
        let some_program_string = r#"
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        // Its VERY important this comes back with zero new lines.
 | 
			
		||||
@ -1026,14 +1000,12 @@ myNestedVar = [
 | 
			
		||||
    fn test_recast_shebang_only() {
 | 
			
		||||
        let some_program_string = r#"#!/usr/local/env zoo kcl"#;
 | 
			
		||||
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let result = parser.ast();
 | 
			
		||||
        let result = crate::parser::top_level_parse(some_program_string);
 | 
			
		||||
 | 
			
		||||
        assert!(result.is_err());
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
            result.unwrap_err().to_string(),
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([21, 24])], message: "Unexpected end of file. The compiler expected a function body items (functions are made up of variable declarations, expressions, and return statements, each of those is a possible body item" }"#
 | 
			
		||||
            r#"syntax: KclErrorDetails { source_ranges: [SourceRange([21, 24, 0])], message: "Unexpected end of file. The compiler expected a function body items (functions are made up of variable declarations, expressions, and return statements, each of those is a possible body item" }"#
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -1048,9 +1020,7 @@ part001 = startSketchOn('XY')
 | 
			
		||||
  |> close(%)
 | 
			
		||||
"#;
 | 
			
		||||
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1081,9 +1051,7 @@ part001 = startSketchOn('XY')
 | 
			
		||||
  |> close(%)
 | 
			
		||||
"#;
 | 
			
		||||
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1113,9 +1081,7 @@ part001 = startSketchOn('XY')
 | 
			
		||||
  |> close(%)
 | 
			
		||||
"#;
 | 
			
		||||
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1253,9 +1219,7 @@ tabs_l = startSketchOn({
 | 
			
		||||
       distance: length - 10
 | 
			
		||||
     }, %)
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        // Its VERY important this comes back with zero new lines.
 | 
			
		||||
@ -1393,9 +1357,7 @@ tabs_l = startSketchOn({
 | 
			
		||||
  |> close(%)
 | 
			
		||||
  |> extrude(scale, %)
 | 
			
		||||
}"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1419,9 +1381,7 @@ tabs_l = startSketchOn({
 | 
			
		||||
  |> startProfileAt([0.0, 5.0], %)
 | 
			
		||||
              |> line([0.4900857016, -0.0240763666], %)
 | 
			
		||||
    |> line([0.6804562304, 0.9087880491], %)"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1440,9 +1400,7 @@ tabs_l = startSketchOn({
 | 
			
		||||
  |> startProfileAt([0.0, 5.0], %)
 | 
			
		||||
              |> line([0.4900857016, -0.0240763666], %) // hello world
 | 
			
		||||
    |> line([0.6804562304, 0.9087880491], %)"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1461,9 +1419,7 @@ tabs_l = startSketchOn({
 | 
			
		||||
              |> line([0.4900857016, -0.0240763666], %)
 | 
			
		||||
        // hello world
 | 
			
		||||
    |> line([0.6804562304, 0.9087880491], %)"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1488,9 +1444,7 @@ tabs_l = startSketchOn({
 | 
			
		||||
  // this is also a comment
 | 
			
		||||
    return things
 | 
			
		||||
}"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1514,9 +1468,7 @@ tabs_l = startSketchOn({
 | 
			
		||||
// this is also a comment
 | 
			
		||||
thing = 'foo'
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1537,9 +1489,7 @@ key = 'c'
 | 
			
		||||
// hello
 | 
			
		||||
thing = 'foo'
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1567,9 +1517,7 @@ thing = 'c'
 | 
			
		||||
 | 
			
		||||
foo = 'bar' //
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1595,9 +1543,7 @@ foo = 'bar' //
 | 
			
		||||
// hello
 | 
			
		||||
thing = 'foo'
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1616,9 +1562,7 @@ thing = 'foo'
 | 
			
		||||
/* comment at start */
 | 
			
		||||
 | 
			
		||||
mySk1 = startSketchAt([0, 0])"#;
 | 
			
		||||
        let tokens = crate::token::lexer(test_program).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(test_program).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1648,9 +1592,7 @@ mySk1 = startSketchOn('XY')
 | 
			
		||||
  |> ry(45, %)
 | 
			
		||||
  |> rx(45, %)
 | 
			
		||||
// one more for good measure"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1686,9 +1628,7 @@ mySk1 = startSketchOn('XY')
 | 
			
		||||
       intersectTag: seg01
 | 
			
		||||
     }, %)
 | 
			
		||||
  |> line([-0.42, -1.72], %)"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted.trim(), some_program_string);
 | 
			
		||||
@ -1712,9 +1652,7 @@ yo = [
 | 
			
		||||
  "  hey oooooo really long long long"
 | 
			
		||||
]
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted, some_program_string);
 | 
			
		||||
@ -1730,9 +1668,7 @@ key = 'c'
 | 
			
		||||
things = "things"
 | 
			
		||||
 | 
			
		||||
// this is also a comment"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        let expected = some_program_string.trim();
 | 
			
		||||
@ -1751,9 +1687,7 @@ things = "things"
 | 
			
		||||
 // a comment
 | 
			
		||||
   "
 | 
			
		||||
}"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted.trim(), some_program_string.trim());
 | 
			
		||||
@ -1777,9 +1711,7 @@ part001 = startSketchOn('XY')
 | 
			
		||||
       -angleToMatchLengthY(seg01, myVar, %),
 | 
			
		||||
       myVar
 | 
			
		||||
     ], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted.trim(), some_program_string);
 | 
			
		||||
@ -1804,9 +1736,7 @@ part001 = startSketchOn('XY')
 | 
			
		||||
         myVar
 | 
			
		||||
      ], %) // ln-lineTo-yAbsolute should use angleToMatchLengthY helper
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(
 | 
			
		||||
            &FormatOptions {
 | 
			
		||||
@ -1835,9 +1765,7 @@ fn ghi = (part001) => {
 | 
			
		||||
  return part001
 | 
			
		||||
}
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let mut program = parser.ast().unwrap();
 | 
			
		||||
        let mut program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
        program.rename_symbol("mySuperCoolPart", 6);
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
@ -1865,9 +1793,7 @@ fn ghi = (part001) => {
 | 
			
		||||
        let some_program_string = r#"fn ghi = (x, y, z) => {
 | 
			
		||||
  return x
 | 
			
		||||
}"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let mut program = parser.ast().unwrap();
 | 
			
		||||
        let mut program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
        program.rename_symbol("newName", 10);
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
@ -1889,9 +1815,7 @@ fn ghi = (part001) => {
 | 
			
		||||
    angle_start: 0,
 | 
			
		||||
    angle_end: 180,
 | 
			
		||||
  }, %)"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1921,9 +1845,7 @@ firstExtrude = startSketchOn('XY')
 | 
			
		||||
  |> close(%)
 | 
			
		||||
  |> extrude(h, %)
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1960,9 +1882,7 @@ firstExtrude = startSketchOn('XY')
 | 
			
		||||
  |> close(%)
 | 
			
		||||
  |> extrude(h, %)
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(
 | 
			
		||||
@ -1988,9 +1908,7 @@ firstExtrude = startSketchOn('XY')
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn test_recast_math_start_negative() {
 | 
			
		||||
        let some_program_string = r#"myVar = -5 + 6"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted.trim(), some_program_string);
 | 
			
		||||
@ -2007,9 +1925,7 @@ startSketchOn('XY')
 | 
			
		||||
  |> line([0, -(5 - thickness)], %)
 | 
			
		||||
  |> line([0, -(5 - 1)], %)
 | 
			
		||||
  |> line([0, -(-5 - 1)], %)"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted.trim(), some_program_string);
 | 
			
		||||
@ -2023,9 +1939,7 @@ FOS = 2
 | 
			
		||||
sigmaAllow = 8
 | 
			
		||||
width = 20
 | 
			
		||||
thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted.trim(), some_program_string);
 | 
			
		||||
@ -2034,9 +1948,7 @@ thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
 | 
			
		||||
    #[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
    async fn no_vardec_keyword() {
 | 
			
		||||
        let some_program_string = r#"distance = 5"#;
 | 
			
		||||
        let tokens = crate::token::lexer(some_program_string).unwrap();
 | 
			
		||||
        let parser = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let program = parser.ast().unwrap();
 | 
			
		||||
        let program = crate::parser::top_level_parse(some_program_string).unwrap();
 | 
			
		||||
 | 
			
		||||
        let recasted = program.recast(&Default::default(), 0);
 | 
			
		||||
        assert_eq!(recasted.trim(), some_program_string);
 | 
			
		||||
@ -2066,7 +1978,7 @@ thickness = sqrt(distance * p * FOS * 6 / (sigmaAllow * width))"#;
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .enumerate()
 | 
			
		||||
        {
 | 
			
		||||
            let tokens = crate::token::lexer(raw).unwrap();
 | 
			
		||||
            let tokens = crate::token::lexer(raw, ModuleId::default()).unwrap();
 | 
			
		||||
            let literal = crate::parser::parser_impl::unsigned_number_literal
 | 
			
		||||
                .parse(&tokens)
 | 
			
		||||
                .unwrap();
 | 
			
		||||
@ -2099,9 +2011,7 @@ sketch002 = startSketchOn({
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
"#;
 | 
			
		||||
        let tokens = crate::token::lexer(input).unwrap();
 | 
			
		||||
        let p = crate::parser::Parser::new(tokens);
 | 
			
		||||
        let ast = p.ast().unwrap();
 | 
			
		||||
        let ast = crate::parser::top_level_parse(input).unwrap();
 | 
			
		||||
        let actual = ast.recast(&FormatOptions::new(), 0);
 | 
			
		||||
        assert_eq!(actual, expected);
 | 
			
		||||
    }
 | 
			
		||||
@ -2127,7 +2037,7 @@ sketch002 = startSketchOn({
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .enumerate()
 | 
			
		||||
        {
 | 
			
		||||
            let tokens = crate::token::lexer(input).unwrap();
 | 
			
		||||
            let tokens = crate::token::lexer(input, ModuleId::default()).unwrap();
 | 
			
		||||
            crate::parser::parser_impl::print_tokens(&tokens);
 | 
			
		||||
            let expr = crate::parser::parser_impl::object.parse(&tokens).unwrap();
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
@ -2225,7 +2135,7 @@ sketch002 = startSketchOn({
 | 
			
		||||
        .into_iter()
 | 
			
		||||
        .enumerate()
 | 
			
		||||
        {
 | 
			
		||||
            let tokens = crate::token::lexer(input).unwrap();
 | 
			
		||||
            let tokens = crate::token::lexer(input, ModuleId::default()).unwrap();
 | 
			
		||||
            let expr = crate::parser::parser_impl::array_elem_by_elem.parse(&tokens).unwrap();
 | 
			
		||||
            assert_eq!(
 | 
			
		||||
                expr.recast(&FormatOptions::new(), 0, false),
 | 
			
		||||
 | 
			
		||||
@ -42,30 +42,30 @@ pub enum Node<'a> {
 | 
			
		||||
impl From<&Node<'_>> for SourceRange {
 | 
			
		||||
    fn from(node: &Node) -> Self {
 | 
			
		||||
        match node {
 | 
			
		||||
            Node::Program(p) => SourceRange([p.start, p.end]),
 | 
			
		||||
            Node::ImportStatement(e) => SourceRange([e.start, e.end]),
 | 
			
		||||
            Node::ExpressionStatement(e) => SourceRange([e.start, e.end]),
 | 
			
		||||
            Node::VariableDeclaration(v) => SourceRange([v.start, v.end]),
 | 
			
		||||
            Node::ReturnStatement(r) => SourceRange([r.start, r.end]),
 | 
			
		||||
            Node::VariableDeclarator(v) => SourceRange([v.start, v.end]),
 | 
			
		||||
            Node::Literal(l) => SourceRange([l.start, l.end]),
 | 
			
		||||
            Node::TagDeclarator(t) => SourceRange([t.start, t.end]),
 | 
			
		||||
            Node::Identifier(i) => SourceRange([i.start, i.end]),
 | 
			
		||||
            Node::BinaryExpression(b) => SourceRange([b.start, b.end]),
 | 
			
		||||
            Node::FunctionExpression(f) => SourceRange([f.start, f.end]),
 | 
			
		||||
            Node::CallExpression(c) => SourceRange([c.start, c.end]),
 | 
			
		||||
            Node::PipeExpression(p) => SourceRange([p.start, p.end]),
 | 
			
		||||
            Node::PipeSubstitution(p) => SourceRange([p.start, p.end]),
 | 
			
		||||
            Node::ArrayExpression(a) => SourceRange([a.start, a.end]),
 | 
			
		||||
            Node::ArrayRangeExpression(a) => SourceRange([a.start, a.end]),
 | 
			
		||||
            Node::ObjectExpression(o) => SourceRange([o.start, o.end]),
 | 
			
		||||
            Node::MemberExpression(m) => SourceRange([m.start, m.end]),
 | 
			
		||||
            Node::UnaryExpression(u) => SourceRange([u.start, u.end]),
 | 
			
		||||
            Node::Parameter(p) => SourceRange([p.identifier.start, p.identifier.end]),
 | 
			
		||||
            Node::ObjectProperty(o) => SourceRange([o.start, o.end]),
 | 
			
		||||
            Node::MemberObject(m) => SourceRange([m.start(), m.end()]),
 | 
			
		||||
            Node::IfExpression(m) => SourceRange([m.start, m.end]),
 | 
			
		||||
            Node::LiteralIdentifier(l) => SourceRange([l.start(), l.end()]),
 | 
			
		||||
            Node::Program(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::ImportStatement(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::ExpressionStatement(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::VariableDeclaration(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::ReturnStatement(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::VariableDeclarator(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::Literal(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::TagDeclarator(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::Identifier(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::BinaryExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::FunctionExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::CallExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::PipeExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::PipeSubstitution(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::ArrayExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::ArrayRangeExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::ObjectExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::MemberExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::UnaryExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::Parameter(p) => SourceRange::from(&p.identifier),
 | 
			
		||||
            Node::ObjectProperty(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::MemberObject(m) => SourceRange([m.start(), m.end(), m.module_id().as_usize()]),
 | 
			
		||||
            Node::IfExpression(n) => SourceRange::from(*n),
 | 
			
		||||
            Node::LiteralIdentifier(l) => SourceRange([l.start(), l.end(), l.module_id().as_usize()]),
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -315,9 +315,7 @@ mod tests {
 | 
			
		||||
 | 
			
		||||
    macro_rules! kcl {
 | 
			
		||||
        ( $kcl:expr ) => {{
 | 
			
		||||
            let tokens = $crate::token::lexer($kcl).unwrap();
 | 
			
		||||
            let parser = $crate::parser::Parser::new(tokens);
 | 
			
		||||
            parser.ast().unwrap()
 | 
			
		||||
            $crate::parser::top_level_parse($kcl).unwrap()
 | 
			
		||||
        }};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -123,7 +123,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                7,
 | 
			
		||||
                32
 | 
			
		||||
                32,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -136,7 +137,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                38,
 | 
			
		||||
                834
 | 
			
		||||
                834,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                6,
 | 
			
		||||
                15
 | 
			
		||||
                15,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -61,7 +62,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                27,
 | 
			
		||||
                39
 | 
			
		||||
                39,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -80,7 +82,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                51,
 | 
			
		||||
                68
 | 
			
		||||
                68,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                175,
 | 
			
		||||
                188
 | 
			
		||||
                188,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -52,7 +53,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                79,
 | 
			
		||||
                80
 | 
			
		||||
                80,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -71,7 +73,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                5,
 | 
			
		||||
                11
 | 
			
		||||
                11,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -90,7 +93,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                95,
 | 
			
		||||
                107
 | 
			
		||||
                107,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -110,7 +114,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                194,
 | 
			
		||||
                206
 | 
			
		||||
                206,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -128,7 +133,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                341,
 | 
			
		||||
                373
 | 
			
		||||
                373,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -141,7 +147,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                88,
 | 
			
		||||
                89
 | 
			
		||||
                89,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
 | 
			
		||||
@ -51,7 +51,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                5,
 | 
			
		||||
                19
 | 
			
		||||
                19,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
 | 
			
		||||
@ -751,7 +751,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                10,
 | 
			
		||||
                316
 | 
			
		||||
                316,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -766,7 +767,8 @@ snapshot_kind: text
 | 
			
		||||
              "id": "[uuid]",
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                206,
 | 
			
		||||
                219
 | 
			
		||||
                219,
 | 
			
		||||
                0
 | 
			
		||||
              ],
 | 
			
		||||
              "tag": null,
 | 
			
		||||
              "type": "extrudePlane"
 | 
			
		||||
@ -776,7 +778,8 @@ snapshot_kind: text
 | 
			
		||||
              "id": "[uuid]",
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                225,
 | 
			
		||||
                238
 | 
			
		||||
                238,
 | 
			
		||||
                0
 | 
			
		||||
              ],
 | 
			
		||||
              "tag": null,
 | 
			
		||||
              "type": "extrudePlane"
 | 
			
		||||
@ -786,7 +789,8 @@ snapshot_kind: text
 | 
			
		||||
              "id": "[uuid]",
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                244,
 | 
			
		||||
                257
 | 
			
		||||
                257,
 | 
			
		||||
                0
 | 
			
		||||
              ],
 | 
			
		||||
              "tag": null,
 | 
			
		||||
              "type": "extrudePlane"
 | 
			
		||||
@ -796,7 +800,8 @@ snapshot_kind: text
 | 
			
		||||
              "id": "[uuid]",
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                263,
 | 
			
		||||
                276
 | 
			
		||||
                276,
 | 
			
		||||
                0
 | 
			
		||||
              ],
 | 
			
		||||
              "tag": null,
 | 
			
		||||
              "type": "extrudePlane"
 | 
			
		||||
@ -811,7 +816,8 @@ snapshot_kind: text
 | 
			
		||||
                  "id": "[uuid]",
 | 
			
		||||
                  "sourceRange": [
 | 
			
		||||
                    206,
 | 
			
		||||
                    219
 | 
			
		||||
                    219,
 | 
			
		||||
                    0
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "from": [
 | 
			
		||||
@ -830,7 +836,8 @@ snapshot_kind: text
 | 
			
		||||
                  "id": "[uuid]",
 | 
			
		||||
                  "sourceRange": [
 | 
			
		||||
                    225,
 | 
			
		||||
                    238
 | 
			
		||||
                    238,
 | 
			
		||||
                    0
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "from": [
 | 
			
		||||
@ -849,7 +856,8 @@ snapshot_kind: text
 | 
			
		||||
                  "id": "[uuid]",
 | 
			
		||||
                  "sourceRange": [
 | 
			
		||||
                    244,
 | 
			
		||||
                    257
 | 
			
		||||
                    257,
 | 
			
		||||
                    0
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "from": [
 | 
			
		||||
@ -868,7 +876,8 @@ snapshot_kind: text
 | 
			
		||||
                  "id": "[uuid]",
 | 
			
		||||
                  "sourceRange": [
 | 
			
		||||
                    263,
 | 
			
		||||
                    276
 | 
			
		||||
                    276,
 | 
			
		||||
                    0
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "from": [
 | 
			
		||||
@ -887,7 +896,8 @@ snapshot_kind: text
 | 
			
		||||
                  "id": "[uuid]",
 | 
			
		||||
                  "sourceRange": [
 | 
			
		||||
                    282,
 | 
			
		||||
                    290
 | 
			
		||||
                    290,
 | 
			
		||||
                    0
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "from": [
 | 
			
		||||
@ -942,7 +952,8 @@ snapshot_kind: text
 | 
			
		||||
                "id": "[uuid]",
 | 
			
		||||
                "sourceRange": [
 | 
			
		||||
                  183,
 | 
			
		||||
                  200
 | 
			
		||||
                  200,
 | 
			
		||||
                  0
 | 
			
		||||
                ]
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
@ -950,7 +961,8 @@ snapshot_kind: text
 | 
			
		||||
              {
 | 
			
		||||
                "sourceRange": [
 | 
			
		||||
                  183,
 | 
			
		||||
                  200
 | 
			
		||||
                  200,
 | 
			
		||||
                  0
 | 
			
		||||
                ]
 | 
			
		||||
              }
 | 
			
		||||
            ]
 | 
			
		||||
@ -962,7 +974,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                183,
 | 
			
		||||
                200
 | 
			
		||||
                200,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
---
 | 
			
		||||
source: kcl/src/tests.rs
 | 
			
		||||
source: kcl/src/simulation_tests.rs
 | 
			
		||||
description: Result of tokenizing cube.kcl
 | 
			
		||||
snapshot_kind: text
 | 
			
		||||
---
 | 
			
		||||
 | 
			
		||||
@ -123,7 +123,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                15,
 | 
			
		||||
                40
 | 
			
		||||
                40,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -140,7 +141,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                47,
 | 
			
		||||
                53
 | 
			
		||||
                53,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -157,7 +159,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                90,
 | 
			
		||||
                107
 | 
			
		||||
                107,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
 | 
			
		||||
@ -41,7 +41,8 @@ snapshot_kind: text
 | 
			
		||||
              "id": "[uuid]",
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                35,
 | 
			
		||||
                76
 | 
			
		||||
                76,
 | 
			
		||||
                0
 | 
			
		||||
              ],
 | 
			
		||||
              "tag": null,
 | 
			
		||||
              "type": "extrudeArc"
 | 
			
		||||
@ -56,7 +57,8 @@ snapshot_kind: text
 | 
			
		||||
                  "id": "[uuid]",
 | 
			
		||||
                  "sourceRange": [
 | 
			
		||||
                    35,
 | 
			
		||||
                    76
 | 
			
		||||
                    76,
 | 
			
		||||
                    0
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "ccw": true,
 | 
			
		||||
@ -117,7 +119,8 @@ snapshot_kind: text
 | 
			
		||||
                "id": "[uuid]",
 | 
			
		||||
                "sourceRange": [
 | 
			
		||||
                  35,
 | 
			
		||||
                  76
 | 
			
		||||
                  76,
 | 
			
		||||
                  0
 | 
			
		||||
                ]
 | 
			
		||||
              }
 | 
			
		||||
            },
 | 
			
		||||
@ -125,7 +128,8 @@ snapshot_kind: text
 | 
			
		||||
              {
 | 
			
		||||
                "sourceRange": [
 | 
			
		||||
                  35,
 | 
			
		||||
                  76
 | 
			
		||||
                  76,
 | 
			
		||||
                  0
 | 
			
		||||
                ]
 | 
			
		||||
              }
 | 
			
		||||
            ]
 | 
			
		||||
@ -137,7 +141,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                35,
 | 
			
		||||
                76
 | 
			
		||||
                76,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
 | 
			
		||||
@ -39,7 +39,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                64,
 | 
			
		||||
                65
 | 
			
		||||
                65,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -52,7 +53,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                199,
 | 
			
		||||
                200
 | 
			
		||||
                200,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -65,7 +67,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                332,
 | 
			
		||||
                333
 | 
			
		||||
                333,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
 | 
			
		||||
@ -43,7 +43,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                43,
 | 
			
		||||
                55
 | 
			
		||||
                55,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -56,7 +57,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                256,
 | 
			
		||||
                266
 | 
			
		||||
                266,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -69,7 +71,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                93,
 | 
			
		||||
                101
 | 
			
		||||
                101,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -82,7 +85,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                277,
 | 
			
		||||
                285
 | 
			
		||||
                285,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
 | 
			
		||||
@ -42,7 +42,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                56,
 | 
			
		||||
                74
 | 
			
		||||
                74,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -60,7 +61,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                529,
 | 
			
		||||
                543
 | 
			
		||||
                543,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -73,7 +75,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                122,
 | 
			
		||||
                132
 | 
			
		||||
                132,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -86,7 +89,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                356,
 | 
			
		||||
                362
 | 
			
		||||
                362,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -99,7 +103,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                553,
 | 
			
		||||
                570
 | 
			
		||||
                570,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -112,7 +117,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                757,
 | 
			
		||||
                770
 | 
			
		||||
                770,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -125,7 +131,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                342,
 | 
			
		||||
                347
 | 
			
		||||
                347,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
 | 
			
		||||
@ -329,7 +329,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                10,
 | 
			
		||||
                157
 | 
			
		||||
                157,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -957,7 +958,8 @@ snapshot_kind: text
 | 
			
		||||
                      {
 | 
			
		||||
                        "sourceRange": [
 | 
			
		||||
                          10,
 | 
			
		||||
                          157
 | 
			
		||||
                          157,
 | 
			
		||||
                          0
 | 
			
		||||
                        ]
 | 
			
		||||
                      }
 | 
			
		||||
                    ]
 | 
			
		||||
@ -973,7 +975,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                170,
 | 
			
		||||
                369
 | 
			
		||||
                369,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -986,7 +989,8 @@ snapshot_kind: text
 | 
			
		||||
              {
 | 
			
		||||
                "sourceRange": [
 | 
			
		||||
                  52,
 | 
			
		||||
                  77
 | 
			
		||||
                  77,
 | 
			
		||||
                  0
 | 
			
		||||
                ]
 | 
			
		||||
              }
 | 
			
		||||
            ],
 | 
			
		||||
@ -1023,7 +1027,8 @@ snapshot_kind: text
 | 
			
		||||
                  "id": "[uuid]",
 | 
			
		||||
                  "sourceRange": [
 | 
			
		||||
                    83,
 | 
			
		||||
                    98
 | 
			
		||||
                    98,
 | 
			
		||||
                    0
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "from": [
 | 
			
		||||
@ -1042,7 +1047,8 @@ snapshot_kind: text
 | 
			
		||||
                  "id": "[uuid]",
 | 
			
		||||
                  "sourceRange": [
 | 
			
		||||
                    104,
 | 
			
		||||
                    119
 | 
			
		||||
                    119,
 | 
			
		||||
                    0
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "from": [
 | 
			
		||||
@ -1061,7 +1067,8 @@ snapshot_kind: text
 | 
			
		||||
                  "id": "[uuid]",
 | 
			
		||||
                  "sourceRange": [
 | 
			
		||||
                    125,
 | 
			
		||||
                    141
 | 
			
		||||
                    141,
 | 
			
		||||
                    0
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "from": [
 | 
			
		||||
@ -1080,7 +1087,8 @@ snapshot_kind: text
 | 
			
		||||
                  "id": "[uuid]",
 | 
			
		||||
                  "sourceRange": [
 | 
			
		||||
                    147,
 | 
			
		||||
                    155
 | 
			
		||||
                    155,
 | 
			
		||||
                    0
 | 
			
		||||
                  ]
 | 
			
		||||
                },
 | 
			
		||||
                "from": [
 | 
			
		||||
@ -1100,7 +1108,8 @@ snapshot_kind: text
 | 
			
		||||
                "id": "[uuid]",
 | 
			
		||||
                "sourceRange": [
 | 
			
		||||
                  52,
 | 
			
		||||
                  77
 | 
			
		||||
                  77,
 | 
			
		||||
                  0
 | 
			
		||||
                ]
 | 
			
		||||
              },
 | 
			
		||||
              "from": [
 | 
			
		||||
@ -1119,7 +1128,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                52,
 | 
			
		||||
                77
 | 
			
		||||
                77,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
@ -1134,7 +1144,8 @@ snapshot_kind: text
 | 
			
		||||
                  {
 | 
			
		||||
                    "sourceRange": [
 | 
			
		||||
                      242,
 | 
			
		||||
                      267
 | 
			
		||||
                      267,
 | 
			
		||||
                      0
 | 
			
		||||
                    ]
 | 
			
		||||
                  }
 | 
			
		||||
                ],
 | 
			
		||||
@ -1171,7 +1182,8 @@ snapshot_kind: text
 | 
			
		||||
                      "id": "[uuid]",
 | 
			
		||||
                      "sourceRange": [
 | 
			
		||||
                        277,
 | 
			
		||||
                        292
 | 
			
		||||
                        292,
 | 
			
		||||
                        0
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "from": [
 | 
			
		||||
@ -1190,7 +1202,8 @@ snapshot_kind: text
 | 
			
		||||
                      "id": "[uuid]",
 | 
			
		||||
                      "sourceRange": [
 | 
			
		||||
                        302,
 | 
			
		||||
                        317
 | 
			
		||||
                        317,
 | 
			
		||||
                        0
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "from": [
 | 
			
		||||
@ -1209,7 +1222,8 @@ snapshot_kind: text
 | 
			
		||||
                      "id": "[uuid]",
 | 
			
		||||
                      "sourceRange": [
 | 
			
		||||
                        327,
 | 
			
		||||
                        343
 | 
			
		||||
                        343,
 | 
			
		||||
                        0
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "from": [
 | 
			
		||||
@ -1228,7 +1242,8 @@ snapshot_kind: text
 | 
			
		||||
                      "id": "[uuid]",
 | 
			
		||||
                      "sourceRange": [
 | 
			
		||||
                        353,
 | 
			
		||||
                        361
 | 
			
		||||
                        361,
 | 
			
		||||
                        0
 | 
			
		||||
                      ]
 | 
			
		||||
                    },
 | 
			
		||||
                    "from": [
 | 
			
		||||
@ -1248,7 +1263,8 @@ snapshot_kind: text
 | 
			
		||||
                    "id": "[uuid]",
 | 
			
		||||
                    "sourceRange": [
 | 
			
		||||
                      242,
 | 
			
		||||
                      267
 | 
			
		||||
                      267,
 | 
			
		||||
                      0
 | 
			
		||||
                    ]
 | 
			
		||||
                  },
 | 
			
		||||
                  "from": [
 | 
			
		||||
@ -1269,7 +1285,8 @@ snapshot_kind: text
 | 
			
		||||
            {
 | 
			
		||||
              "sourceRange": [
 | 
			
		||||
                187,
 | 
			
		||||
                367
 | 
			
		||||
                367,
 | 
			
		||||
                0
 | 
			
		||||
              ]
 | 
			
		||||
            }
 | 
			
		||||
          ]
 | 
			
		||||
 | 
			
		||||
@ -8,7 +8,7 @@ use std::{
 | 
			
		||||
use futures::stream::TryStreamExt;
 | 
			
		||||
use gloo_utils::format::JsValueSerdeExt;
 | 
			
		||||
use kcl_lib::{
 | 
			
		||||
    ast::types::{Node, Program},
 | 
			
		||||
    ast::types::{ModuleId, Node, Program},
 | 
			
		||||
    coredump::CoreDump,
 | 
			
		||||
    engine::EngineManager,
 | 
			
		||||
    executor::ExecutorSettings,
 | 
			
		||||
@ -153,9 +153,11 @@ pub async fn modify_ast_for_sketch_wasm(
 | 
			
		||||
            .map_err(|e| format!("{:?}", e))?,
 | 
			
		||||
    ));
 | 
			
		||||
 | 
			
		||||
    let module_id = ModuleId::default();
 | 
			
		||||
    let _ = kcl_lib::ast::modify::modify_ast_for_sketch(
 | 
			
		||||
        &engine,
 | 
			
		||||
        &mut program,
 | 
			
		||||
        module_id,
 | 
			
		||||
        sketch_name,
 | 
			
		||||
        plane,
 | 
			
		||||
        uuid::Uuid::parse_str(sketch_id).map_err(|e| e.to_string())?,
 | 
			
		||||
@ -193,7 +195,8 @@ pub fn deserialize_files(data: &[u8]) -> Result<JsValue, JsError> {
 | 
			
		||||
pub fn lexer_wasm(js: &str) -> Result<JsValue, JsError> {
 | 
			
		||||
    console_error_panic_hook::set_once();
 | 
			
		||||
 | 
			
		||||
    let tokens = kcl_lib::token::lexer(js).map_err(JsError::from)?;
 | 
			
		||||
    let module_id = ModuleId::default();
 | 
			
		||||
    let tokens = kcl_lib::token::lexer(js, module_id).map_err(JsError::from)?;
 | 
			
		||||
    Ok(JsValue::from_serde(&tokens)?)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -201,7 +204,8 @@ pub fn lexer_wasm(js: &str) -> Result<JsValue, JsError> {
 | 
			
		||||
pub fn parse_wasm(js: &str) -> Result<JsValue, String> {
 | 
			
		||||
    console_error_panic_hook::set_once();
 | 
			
		||||
 | 
			
		||||
    let tokens = kcl_lib::token::lexer(js).map_err(String::from)?;
 | 
			
		||||
    let module_id = ModuleId::default();
 | 
			
		||||
    let tokens = kcl_lib::token::lexer(js, module_id).map_err(String::from)?;
 | 
			
		||||
    let parser = kcl_lib::parser::Parser::new(tokens);
 | 
			
		||||
    let program = parser.ast().map_err(String::from)?;
 | 
			
		||||
    // The serde-wasm-bindgen does not work here because of weird HashMap issues so we use the
 | 
			
		||||
 | 
			
		||||
@ -28,7 +28,7 @@ async fn kcl_test_fillet_duplicate_tags() {
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([203, 249])], message: "Duplicate tags are not allowed." }"#,
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([203, 249, 0])], message: "Duplicate tags are not allowed." }"#,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -83,7 +83,7 @@ async fn kcl_test_execute_engine_error_return() {
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"engine: KclErrorDetails { source_ranges: [SourceRange([216, 229])], message: "Modeling command failed: [ApiError { error_code: BadRequest, message: \"The path is not closed.  Solid2D construction requires a closed path!\" }]" }"#,
 | 
			
		||||
        r#"engine: KclErrorDetails { source_ranges: [SourceRange([216, 229, 0])], message: "Modeling command failed: [ApiError { error_code: BadRequest, message: \"The path is not closed.  Solid2D construction requires a closed path!\" }]" }"#,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -515,7 +515,7 @@ async fn kcl_test_import_file_doesnt_exist() {
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([8, 27])], message: "File `thing.obj` does not exist." }"#
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([8, 27, 0])], message: "File `thing.obj` does not exist." }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -583,7 +583,7 @@ async fn kcl_test_import_ext_doesnt_match() {
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([8, 76])], message: "The given format does not match the file extension. Expected: `gltf`, Given: `obj`" }"#
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([8, 76, 0])], message: "The given format does not match the file extension. Expected: `gltf`, Given: `obj`" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -742,7 +742,7 @@ part002 = startSketchOn(part001, part001.sketch.tags.here)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([88, 133]), SourceRange([210, 226])], message: "could not sketch tangential arc, because its center would be infinitely far away in the X direction" }"#
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([88, 133, 0]), SourceRange([210, 226, 0])], message: "could not sketch tangential arc, because its center would be infinitely far away in the X direction" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -799,7 +799,7 @@ async fn kcl_test_stdlib_kcl_error_right_code_path() {
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([151, 189])], message: "Expected an argument at index 1" }"#,
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([151, 189, 0])], message: "Expected an argument at index 1" }"#,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -869,7 +869,7 @@ part = rectShape([0, 0], 20, 20)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([863, 912])], message: "Argument at index 0 was supposed to be type kcl_lib::std::shapes::CircleData but found string (text)" }"#,
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([863, 912, 0])], message: "Argument at index 0 was supposed to be type kcl_lib::std::shapes::CircleData but found string (text)" }"#,
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -954,7 +954,7 @@ async fn kcl_test_revolve_bad_angle_low() {
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([272, 308])], message: "Expected angle to be between -360 and 360 and not 0, found `-455`" }"#
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([272, 308, 0])], message: "Expected angle to be between -360 and 360 and not 0, found `-455`" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -979,7 +979,7 @@ async fn kcl_test_revolve_bad_angle_high() {
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([272, 307])], message: "Expected angle to be between -360 and 360 and not 0, found `455`" }"#
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([272, 307, 0])], message: "Expected angle to be between -360 and 360 and not 0, found `455`" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1073,7 +1073,7 @@ sketch001 = startSketchOn(box, revolveAxis)
 | 
			
		||||
    //this fails right now, but slightly differently, lets just say its enough for it to fail - mike
 | 
			
		||||
    //assert_eq!(
 | 
			
		||||
    //    result.err().unwrap().to_string(),
 | 
			
		||||
    //    r#"engine: KclErrorDetails { source_ranges: [SourceRange([346, 390])], message: "Modeling command failed: [ApiError { error_code: InternalEngine, message: \"Solid3D revolve failed:  sketch profile must lie entirely on one side of the revolution axis\" }]" }"#
 | 
			
		||||
    //    r#"engine: KclErrorDetails { source_ranges: [SourceRange([346, 390, 0])], message: "Modeling command failed: [ApiError { error_code: InternalEngine, message: \"Solid3D revolve failed:  sketch profile must lie entirely on one side of the revolution axis\" }]" }"#
 | 
			
		||||
    //);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1354,7 +1354,7 @@ secondSketch = startSketchOn(part001, '')
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([260, 286])], message: "Argument at index 1 was supposed to be type kcl_lib::std::sketch::FaceTag but found string (text)" }"#
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([260, 286, 0])], message: "Argument at index 1 was supposed to be type kcl_lib::std::sketch::FaceTag but found string (text)" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1385,7 +1385,7 @@ extrusion = startSketchOn('XY')
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([68, 334]), SourceRange([428, 461])], message: "Expected 2 arguments, got 3" }"#
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([68, 334, 0]), SourceRange([428, 461, 0])], message: "Expected 2 arguments, got 3" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1681,7 +1681,7 @@ part001 = cube([0,0], 20)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([259, 345])], message: "You can only tag one edge at a time with a tagged chamfer. Either delete the tag for the chamfer fn if you don't need it OR separate into individual chamfer functions for each tag." }"#
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([259, 345, 0])], message: "You can only tag one edge at a time with a tagged chamfer. Either delete the tag for the chamfer fn if you don't need it OR separate into individual chamfer functions for each tag." }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1708,7 +1708,7 @@ let p = triangle(200)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"value already defined: KclErrorDetails { source_ranges: [SourceRange([311, 313]), SourceRange([326, 339])], message: "Cannot redefine `a`" }"#
 | 
			
		||||
        r#"value already defined: KclErrorDetails { source_ranges: [SourceRange([311, 313, 0]), SourceRange([326, 339, 0])], message: "Cannot redefine `a`" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1783,7 +1783,7 @@ async fn kcl_test_arc_error_same_start_end() {
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([57, 140])], message: "Arc start and end angles must be different" }"#
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([57, 140, 0])], message: "Arc start and end angles must be different" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1803,7 +1803,7 @@ example = extrude(10, exampleSketch)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 111])], message: "Cannot have an x constrained angle of 90 degrees" }"#
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 111, 0])], message: "Cannot have an x constrained angle of 90 degrees" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1823,7 +1823,7 @@ example = extrude(10, exampleSketch)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 112])], message: "Cannot have an x constrained angle of 270 degrees" }"#
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 112, 0])], message: "Cannot have an x constrained angle of 270 degrees" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1843,7 +1843,7 @@ example = extrude(10, exampleSketch)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 110])], message: "Cannot have a y constrained angle of 0 degrees" }"#
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 110, 0])], message: "Cannot have a y constrained angle of 0 degrees" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1863,7 +1863,7 @@ example = extrude(10, exampleSketch)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 112])], message: "Cannot have a y constrained angle of 180 degrees" }"#
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([72, 112, 0])], message: "Cannot have a y constrained angle of 180 degrees" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1883,7 +1883,7 @@ extrusion = extrude(10, sketch001)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([68, 125])], message: "Cannot have an x constrained angle of 90 degrees" }"#
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([68, 125, 0])], message: "Cannot have an x constrained angle of 90 degrees" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1903,7 +1903,7 @@ extrusion = extrude(10, sketch001)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([68, 125])], message: "Cannot have an x constrained angle of 90 degrees" }"#
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([68, 125, 0])], message: "Cannot have an x constrained angle of 90 degrees" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1925,7 +1925,7 @@ example = extrude(10, exampleSketch)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 142])], message: "Cannot have a y constrained angle of 0 degrees" }"#
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 142, 0])], message: "Cannot have a y constrained angle of 0 degrees" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1947,7 +1947,7 @@ example = extrude(10, exampleSketch)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 144])], message: "Cannot have a y constrained angle of 180 degrees" }"#
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 144, 0])], message: "Cannot have a y constrained angle of 180 degrees" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1969,7 +1969,7 @@ example = extrude(10, exampleSketch)
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 145])], message: "Cannot have a y constrained angle of 180 degrees" }"#
 | 
			
		||||
        r#"type: KclErrorDetails { source_ranges: [SourceRange([94, 145, 0])], message: "Cannot have a y constrained angle of 180 degrees" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -1986,7 +1986,7 @@ someFunction('INVALID')
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([37, 61]), SourceRange([65, 88])], message: "Argument at index 0 was supposed to be type kcl_lib::std::sketch::SketchData but found string (text)" }"#
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([37, 61, 0]), SourceRange([65, 88, 0])], message: "Argument at index 0 was supposed to be type kcl_lib::std::sketch::SketchData but found string (text)" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -2007,7 +2007,7 @@ someFunction('INVALID')
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.err().unwrap().to_string(),
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([89, 114]), SourceRange([126, 155]), SourceRange([159, 182])], message: "Argument at index 0 was supposed to be type kcl_lib::std::sketch::SketchData but found string (text)" }"#
 | 
			
		||||
        r#"semantic: KclErrorDetails { source_ranges: [SourceRange([89, 114, 0]), SourceRange([126, 155, 0]), SourceRange([159, 182, 0])], message: "Argument at index 0 was supposed to be type kcl_lib::std::sketch::SketchData but found string (text)" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,5 @@
 | 
			
		||||
use kcl_lib::{
 | 
			
		||||
    ast::types::{Node, Program},
 | 
			
		||||
    ast::types::{ModuleId, Node, Program},
 | 
			
		||||
    errors::KclError,
 | 
			
		||||
    executor::{ExecutorContext, IdGenerator},
 | 
			
		||||
    parser,
 | 
			
		||||
@ -28,7 +28,8 @@ macro_rules! gen_test_parse_fail {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn setup(program: &str) -> (ExecutorContext, Node<Program>, IdGenerator) {
 | 
			
		||||
    let tokens = kcl_lib::token::lexer(program).unwrap();
 | 
			
		||||
    let module_id = ModuleId::default();
 | 
			
		||||
    let tokens = kcl_lib::token::lexer(program, module_id).unwrap();
 | 
			
		||||
    let parser = kcl_lib::parser::Parser::new(tokens);
 | 
			
		||||
    let program = parser.ast().unwrap();
 | 
			
		||||
    let ctx = kcl_lib::executor::ExecutorContext {
 | 
			
		||||
@ -60,7 +61,7 @@ async fn run_fail(code: &str) -> KclError {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async fn run_parse_fail(code: &str) -> KclError {
 | 
			
		||||
    let Err(e) = parser::parse(code) else {
 | 
			
		||||
    let Err(e) = parser::top_level_parse(code) else {
 | 
			
		||||
        panic!("Expected this KCL program to fail to parse, but it (incorrectly) never threw an error.");
 | 
			
		||||
    };
 | 
			
		||||
    e
 | 
			
		||||
 | 
			
		||||
@ -2,7 +2,7 @@ use anyhow::Result;
 | 
			
		||||
use kcl_lib::{
 | 
			
		||||
    ast::{
 | 
			
		||||
        modify::modify_ast_for_sketch,
 | 
			
		||||
        types::{Node, Program},
 | 
			
		||||
        types::{ModuleId, Node, Program},
 | 
			
		||||
    },
 | 
			
		||||
    executor::{ExecutorContext, IdGenerator, KclValue, PlaneType, Sketch, SourceRange},
 | 
			
		||||
};
 | 
			
		||||
@ -10,10 +10,9 @@ use kittycad_modeling_cmds::{each_cmd as mcmd, length_unit::LengthUnit, shared::
 | 
			
		||||
use pretty_assertions::assert_eq;
 | 
			
		||||
 | 
			
		||||
/// Setup the engine and parse code for an ast.
 | 
			
		||||
async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Node<Program>, uuid::Uuid)> {
 | 
			
		||||
    let tokens = kcl_lib::token::lexer(code)?;
 | 
			
		||||
    let parser = kcl_lib::parser::Parser::new(tokens);
 | 
			
		||||
    let program = parser.ast()?;
 | 
			
		||||
async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Node<Program>, ModuleId, uuid::Uuid)> {
 | 
			
		||||
    let module_id = ModuleId::default();
 | 
			
		||||
    let program = kcl_lib::parser::parse(code, module_id)?;
 | 
			
		||||
    let ctx = kcl_lib::executor::ExecutorContext::new_with_default_client(Default::default()).await?;
 | 
			
		||||
    let exec_state = ctx.run(&program, None, IdGenerator::default(), None).await?;
 | 
			
		||||
 | 
			
		||||
@ -60,7 +59,7 @@ async fn setup(code: &str, name: &str) -> Result<(ExecutorContext, Node<Program>
 | 
			
		||||
        )
 | 
			
		||||
        .await?;
 | 
			
		||||
 | 
			
		||||
    Ok((ctx, program, sketch_id))
 | 
			
		||||
    Ok((ctx, program, module_id, sketch_id))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#[tokio::test(flavor = "multi_thread")]
 | 
			
		||||
@ -76,9 +75,9 @@ async fn kcl_test_modify_sketch_part001() {
 | 
			
		||||
        name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let mut new_program = program.clone();
 | 
			
		||||
    let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
 | 
			
		||||
    let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id)
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
@ -101,9 +100,9 @@ async fn kcl_test_modify_sketch_part002() {
 | 
			
		||||
        name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let mut new_program = program.clone();
 | 
			
		||||
    let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
 | 
			
		||||
    let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id)
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
@ -128,9 +127,9 @@ async fn kcl_test_modify_close_sketch() {
 | 
			
		||||
        name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let mut new_program = program.clone();
 | 
			
		||||
    let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
 | 
			
		||||
    let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id)
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
@ -154,9 +153,9 @@ async fn kcl_test_modify_line_to_close_sketch() {
 | 
			
		||||
        name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let mut new_program = program.clone();
 | 
			
		||||
    let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
 | 
			
		||||
    let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id)
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
@ -191,14 +190,14 @@ const {} = startSketchOn("XY")
 | 
			
		||||
        name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let mut new_program = program.clone();
 | 
			
		||||
    let result = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id).await;
 | 
			
		||||
    let result = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id).await;
 | 
			
		||||
 | 
			
		||||
    assert!(result.is_err());
 | 
			
		||||
    assert_eq!(
 | 
			
		||||
        result.unwrap_err().to_string(),
 | 
			
		||||
        r#"engine: KclErrorDetails { source_ranges: [SourceRange([188, 193])], message: "Sketch part002 is constrained `partial` and cannot be modified" }"#
 | 
			
		||||
        r#"engine: KclErrorDetails { source_ranges: [SourceRange([188, 193, 0])], message: "Sketch part002 is constrained `partial` and cannot be modified" }"#
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -216,9 +215,9 @@ async fn kcl_test_modify_line_should_close_sketch() {
 | 
			
		||||
        name
 | 
			
		||||
    );
 | 
			
		||||
 | 
			
		||||
    let (ctx, program, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let (ctx, program, module_id, sketch_id) = setup(&code, name).await.unwrap();
 | 
			
		||||
    let mut new_program = program.clone();
 | 
			
		||||
    let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, name, PlaneType::XY, sketch_id)
 | 
			
		||||
    let new_code = modify_ast_for_sketch(&ctx.engine, &mut new_program, module_id, name, PlaneType::XY, sketch_id)
 | 
			
		||||
        .await
 | 
			
		||||
        .unwrap();
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user