Compare commits
	
		
			9 Commits
		
	
	
		
			nightly-v2
			...
			parser-in-
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| ae068b7ed6 | |||
| 5bdc899831 | |||
| e0e6acf231 | |||
| 668bc863df | |||
| 59ce602d6e | |||
| 8ce819b28f | |||
| ed5e276377 | |||
| 8549bd9486 | |||
| 56f9078812 | 
| @ -137,33 +137,34 @@ export function useCalc({ | ||||
|     setAvailableVarInfo(varInfo) | ||||
|   }, [kclManager.ast, kclManager.programMemory, selectionRange]) | ||||
|  | ||||
|   useEffect(() => { | ||||
|   useEffect(async () => { | ||||
|     try { | ||||
|       const code = `const __result__ = ${value}` | ||||
|       const ast = parse(code) | ||||
|       const _programMem: any = { root: {}, return: null } | ||||
|       availableVarInfo.variables.forEach(({ key, value }) => { | ||||
|         _programMem.root[key] = { type: 'userVal', value, __meta: [] } | ||||
|       }) | ||||
|       executeAst({ | ||||
|         ast, | ||||
|         engineCommandManager, | ||||
|         useFakeExecutor: true, | ||||
|         programMemoryOverride: JSON.parse( | ||||
|           JSON.stringify(kclManager.programMemory) | ||||
|         ), | ||||
|       }).then(({ programMemory }) => { | ||||
|         const resultDeclaration = ast.body.find( | ||||
|           (a) => | ||||
|             a.type === 'VariableDeclaration' && | ||||
|             a.declarations?.[0]?.id?.name === '__result__' | ||||
|         ) | ||||
|         const init = | ||||
|           resultDeclaration?.type === 'VariableDeclaration' && | ||||
|           resultDeclaration?.declarations?.[0]?.init | ||||
|         const result = programMemory?.root?.__result__?.value | ||||
|         setCalcResult(typeof result === 'number' ? String(result) : 'NAN') | ||||
|         init && setValueNode(init) | ||||
|       parse(code).then((ast) => { | ||||
|         const _programMem: any = { root: {}, return: null } | ||||
|         availableVarInfo.variables.forEach(({ key, value }) => { | ||||
|           _programMem.root[key] = { type: 'userVal', value, __meta: [] } | ||||
|         }) | ||||
|         executeAst({ | ||||
|           ast, | ||||
|           engineCommandManager, | ||||
|           useFakeExecutor: true, | ||||
|           programMemoryOverride: JSON.parse( | ||||
|             JSON.stringify(kclManager.programMemory) | ||||
|           ), | ||||
|         }).then(({ programMemory }) => { | ||||
|           const resultDeclaration = ast.body.find( | ||||
|             (a) => | ||||
|               a.type === 'VariableDeclaration' && | ||||
|               a.declarations?.[0]?.id?.name === '__result__' | ||||
|           ) | ||||
|           const init = | ||||
|             resultDeclaration?.type === 'VariableDeclaration' && | ||||
|             resultDeclaration?.declarations?.[0]?.init | ||||
|           const result = programMemory?.root?.__result__?.value | ||||
|           setCalcResult(typeof result === 'number' ? String(result) : 'NAN') | ||||
|           init && setValueNode(init) | ||||
|         }) | ||||
|       }) | ||||
|     } catch (e) { | ||||
|       setCalcResult('NAN') | ||||
|  | ||||
| @ -26,7 +26,7 @@ describe('processMemory', () => { | ||||
|     |> lineTo([0.98, 5.16], %) | ||||
|     |> lineTo([2.15, 4.32], %) | ||||
|     // |> rx(90, %)` | ||||
|     const ast = parse(code) | ||||
|     const ast = await parse(code) | ||||
|     const programMemory = await enginelessExecutor(ast, { | ||||
|       root: {}, | ||||
|       return: null, | ||||
|  | ||||
| @ -43,14 +43,16 @@ export class KclManager { | ||||
|     const ast = this.safeParse(code) | ||||
|     if (!ast) return | ||||
|     try { | ||||
|         parse(recast(ast)).then((newAst) => { | ||||
|       const fmtAndStringify = (ast: Program) => | ||||
|         JSON.stringify(parse(recast(ast))) | ||||
|         JSON.stringify(newAst) | ||||
|       const isAstTheSame = fmtAndStringify(ast) === fmtAndStringify(this._ast) | ||||
|       if (isAstTheSame) return | ||||
|       this.executeAst(ast) | ||||
|         }) | ||||
|     } catch (e) { | ||||
|       console.error(e) | ||||
|     } | ||||
|     this.executeAst(ast) | ||||
|   }, 600) | ||||
|  | ||||
|   private _isExecutingCallback: (arg: boolean) => void = () => {} | ||||
| @ -143,9 +145,9 @@ export class KclManager { | ||||
|     this._executeCallback = callback | ||||
|   } | ||||
|  | ||||
|   safeParse(code: string): Program | null { | ||||
|   async safeParse(code: string): Promise<Program | null> { | ||||
|     try { | ||||
|       const ast = parse(code) | ||||
|       const ast = await parse(code) | ||||
|       this.kclErrors = [] | ||||
|       return ast | ||||
|     } catch (e) { | ||||
|  | ||||
| @ -4,8 +4,8 @@ import { initPromise, parse } from './wasm' | ||||
| beforeAll(() => initPromise) | ||||
|  | ||||
| describe('testing AST', () => { | ||||
|   test('5 + 6', () => { | ||||
|     const result = parse('5 +6') | ||||
|   test('5 + 6', async () => { | ||||
|     const result = await parse('5 +6') | ||||
|     delete (result as any).nonCodeMeta | ||||
|     expect(result.body).toEqual([ | ||||
|       { | ||||
| @ -35,8 +35,8 @@ describe('testing AST', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|   test('const myVar = 5', () => { | ||||
|     const { body } = parse('const myVar = 5') | ||||
|   test('const myVar = 5', async () => { | ||||
|     const { body } = await parse('const myVar = 5') | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
|         type: 'VariableDeclaration', | ||||
| @ -66,11 +66,11 @@ describe('testing AST', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|   test('multi-line', () => { | ||||
|   test('multi-line', async () => { | ||||
|     const code = `const myVar = 5 | ||||
| const newVar = myVar + 1 | ||||
| ` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
|         type: 'VariableDeclaration', | ||||
| @ -141,8 +141,8 @@ const newVar = myVar + 1 | ||||
| }) | ||||
|  | ||||
| describe('testing function declaration', () => { | ||||
|   test('fn funcN = (a, b) => {return a + b}', () => { | ||||
|     const { body } = parse( | ||||
|   test('fn funcN = (a, b) => {return a + b}', async () => { | ||||
|     const { body } = await parse( | ||||
|       ['fn funcN = (a, b) => {', '  return a + b', '}'].join('\n') | ||||
|     ) | ||||
|     delete (body[0] as any).declarations[0].init.body.nonCodeMeta | ||||
| @ -224,10 +224,10 @@ describe('testing function declaration', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|   test('call expression assignment', () => { | ||||
|   test('call expression assignment', async () => { | ||||
|     const code = `fn funcN = (a, b) => { return a + b } | ||||
| const myVar = funcN(1, 2)` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     delete (body[0] as any).declarations[0].init.body.nonCodeMeta | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
| @ -357,14 +357,14 @@ const myVar = funcN(1, 2)` | ||||
| }) | ||||
|  | ||||
| describe('testing pipe operator special', () => { | ||||
|   test('pipe operator with sketch', () => { | ||||
|   test('pipe operator with sketch', async () => { | ||||
|     let code = `const mySketch = startSketchAt([0, 0]) | ||||
|   |> lineTo([2, 3], %) | ||||
|   |> lineTo([0, 1], %, "myPath") | ||||
|   |> lineTo([1, 1], %) | ||||
|   |> rx(45, %) | ||||
| ` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     delete (body[0] as any).declarations[0].init.nonCodeMeta | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
| @ -562,9 +562,9 @@ describe('testing pipe operator special', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|   test('pipe operator with binary expression', () => { | ||||
|   test('pipe operator with binary expression', async () => { | ||||
|     let code = `const myVar = 5 + 6 |> myFunc(45, %)` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     delete (body as any)[0].declarations[0].init.nonCodeMeta | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
| @ -641,9 +641,9 @@ describe('testing pipe operator special', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|   test('array expression', () => { | ||||
|   test('array expression', async () => { | ||||
|     let code = `const yo = [1, '2', three, 4 + 5]` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
|         type: 'VariableDeclaration', | ||||
| @ -713,12 +713,12 @@ describe('testing pipe operator special', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|   test('object expression ast', () => { | ||||
|   test('object expression ast', async () => { | ||||
|     const code = [ | ||||
|       'const three = 3', | ||||
|       "const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}", | ||||
|     ].join('\n') | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
|         type: 'VariableDeclaration', | ||||
| @ -858,11 +858,11 @@ describe('testing pipe operator special', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|   test('nested object expression ast', () => { | ||||
|   test('nested object expression ast', async () => { | ||||
|     const code = `const yo = {key: { | ||||
|   key2: 'value' | ||||
| }}` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
|         type: 'VariableDeclaration', | ||||
| @ -928,9 +928,9 @@ describe('testing pipe operator special', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|   test('object expression with array ast', () => { | ||||
|   test('object expression with array ast', async () => { | ||||
|     const code = `const yo = {key: [1, '2']}` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
|         type: 'VariableDeclaration', | ||||
| @ -992,9 +992,9 @@ describe('testing pipe operator special', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|   test('object memberExpression simple', () => { | ||||
|   test('object memberExpression simple', async () => { | ||||
|     const code = `const prop = yo.one.two` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
|         type: 'VariableDeclaration', | ||||
| @ -1047,9 +1047,9 @@ describe('testing pipe operator special', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|   test('object memberExpression with square braces', () => { | ||||
|   test('object memberExpression with square braces', async () => { | ||||
|     const code = `const prop = yo.one["two"]` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
|         type: 'VariableDeclaration', | ||||
| @ -1103,9 +1103,9 @@ describe('testing pipe operator special', () => { | ||||
|       }, | ||||
|     ]) | ||||
|   }) | ||||
|   test('object memberExpression with two square braces literal and identifier', () => { | ||||
|   test('object memberExpression with two square braces literal and identifier', async () => { | ||||
|     const code = `const prop = yo["one"][two]` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect(body).toEqual([ | ||||
|       { | ||||
|         type: 'VariableDeclaration', | ||||
| @ -1162,9 +1162,9 @@ describe('testing pipe operator special', () => { | ||||
| }) | ||||
|  | ||||
| describe('nests binary expressions correctly', () => { | ||||
|   it('works with the simple case', () => { | ||||
|   it('works with the simple case', async () => { | ||||
|     const code = `const yo = 1 + 2` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect(body[0]).toEqual({ | ||||
|       type: 'VariableDeclaration', | ||||
|       start: 0, | ||||
| @ -1205,10 +1205,10 @@ describe('nests binary expressions correctly', () => { | ||||
|       ], | ||||
|     }) | ||||
|   }) | ||||
|   it('should nest according to precedence with multiply first', () => { | ||||
|   it('should nest according to precedence with multiply first', async () => { | ||||
|     // should be binExp { binExp { lit-1 * lit-2 } + lit} | ||||
|     const code = `const yo = 1 * 2 + 3` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect(body[0]).toEqual({ | ||||
|       type: 'VariableDeclaration', | ||||
|       start: 0, | ||||
| @ -1262,10 +1262,10 @@ describe('nests binary expressions correctly', () => { | ||||
|       ], | ||||
|     }) | ||||
|   }) | ||||
|   it('should nest according to precedence with sum first', () => { | ||||
|   it('should nest according to precedence with sum first', async () => { | ||||
|     // should be binExp { lit-1 + binExp { lit-2 * lit-3 } } | ||||
|     const code = `const yo = 1 + 2 * 3` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect(body[0]).toEqual({ | ||||
|       type: 'VariableDeclaration', | ||||
|       start: 0, | ||||
| @ -1319,9 +1319,9 @@ describe('nests binary expressions correctly', () => { | ||||
|       ], | ||||
|     }) | ||||
|   }) | ||||
|   it('should nest properly with two operators of equal precedence', () => { | ||||
|   it('should nest properly with two operators of equal precedence', async () => { | ||||
|     const code = `const yo = 1 + 2 - 3` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect((body[0] as any).declarations[0].init).toEqual({ | ||||
|       type: 'BinaryExpression', | ||||
|       start: 11, | ||||
| @ -1356,9 +1356,9 @@ describe('nests binary expressions correctly', () => { | ||||
|       }, | ||||
|     }) | ||||
|   }) | ||||
|   it('should nest properly with two operators of equal (but higher) precedence', () => { | ||||
|   it('should nest properly with two operators of equal (but higher) precedence', async () => { | ||||
|     const code = `const yo = 1 * 2 / 3` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     expect((body[0] as any).declarations[0].init).toEqual({ | ||||
|       type: 'BinaryExpression', | ||||
|       start: 11, | ||||
| @ -1393,9 +1393,9 @@ describe('nests binary expressions correctly', () => { | ||||
|       }, | ||||
|     }) | ||||
|   }) | ||||
|   it('should nest properly with longer example', () => { | ||||
|   it('should nest properly with longer example', async () => { | ||||
|     const code = `const yo = 1 + 2 * (3 - 4) / 5 + 6` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     const init = (body[0] as any).declarations[0].init | ||||
|     expect(init).toEqual({ | ||||
|       type: 'BinaryExpression', | ||||
| @ -1443,7 +1443,7 @@ describe('nests binary expressions correctly', () => { | ||||
| }) | ||||
|  | ||||
| describe('check nonCodeMeta data is attached to the AST correctly', () => { | ||||
|   it('comments between expressions', () => { | ||||
|   it('comments between expressions', async () => { | ||||
|     const code = ` | ||||
| const yo = { a: { b: { c: '123' } } } | ||||
| // this is a comment | ||||
| @ -1458,12 +1458,14 @@ const key = 'c'` | ||||
|         value: 'this is a comment', | ||||
|       }, | ||||
|     } | ||||
|     const { nonCodeMeta } = parse(code) | ||||
|     const { nonCodeMeta } = await parse(code) | ||||
|     expect(nonCodeMeta.nonCodeNodes[0][0]).toEqual(nonCodeMetaInstance) | ||||
|  | ||||
|     // extra whitespace won't change it's position (0) or value (NB the start end would have changed though) | ||||
|     const codeWithExtraStartWhitespace = '\n\n\n' + code | ||||
|     const { nonCodeMeta: nonCodeMeta2 } = parse(codeWithExtraStartWhitespace) | ||||
|     const { nonCodeMeta: nonCodeMeta2 } = await parse( | ||||
|       codeWithExtraStartWhitespace | ||||
|     ) | ||||
|     expect(nonCodeMeta2.nonCodeNodes[0][0].value).toStrictEqual( | ||||
|       nonCodeMetaInstance.value | ||||
|     ) | ||||
| @ -1471,7 +1473,7 @@ const key = 'c'` | ||||
|       nonCodeMetaInstance.start | ||||
|     ) | ||||
|   }) | ||||
|   it('comments nested within a block statement', () => { | ||||
|   it('comments nested within a block statement', async () => { | ||||
|     const code = `const mySketch = startSketchAt([0,0]) | ||||
|   |> lineTo([0, 1], %, 'myPath') | ||||
|   |> lineTo([1, 1], %) /* this is | ||||
| @ -1481,7 +1483,7 @@ const key = 'c'` | ||||
|   |> close(%) | ||||
| ` | ||||
|  | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     const indexOfSecondLineToExpression = 2 | ||||
|     const sketchNonCodeMeta = (body as any)[0].declarations[0].init.nonCodeMeta | ||||
|       .nonCodeNodes | ||||
| @ -1496,7 +1498,7 @@ const key = 'c'` | ||||
|       }, | ||||
|     }) | ||||
|   }) | ||||
|   it('comments in a pipe expression', () => { | ||||
|   it('comments in a pipe expression', async () => { | ||||
|     const code = [ | ||||
|       'const mySk1 = startSketchAt([0, 0])', | ||||
|       '  |> lineTo([1, 1], %)', | ||||
| @ -1506,7 +1508,7 @@ const key = 'c'` | ||||
|       '  |> rx(90, %)', | ||||
|     ].join('\n') | ||||
|  | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta | ||||
|       .nonCodeNodes[3][0] | ||||
|     expect(sketchNonCodeMeta).toEqual({ | ||||
| @ -1523,9 +1525,9 @@ const key = 'c'` | ||||
| }) | ||||
|  | ||||
| describe('test UnaryExpression', () => { | ||||
|   it('should parse a unary expression in simple var dec situation', () => { | ||||
|   it('should parse a unary expression in simple var dec situation', async () => { | ||||
|     const code = `const myVar = -min(4, 100)` | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     const myVarInit = (body?.[0] as any).declarations[0]?.init | ||||
|     expect(myVarInit).toEqual({ | ||||
|       type: 'UnaryExpression', | ||||
| @ -1548,9 +1550,9 @@ describe('test UnaryExpression', () => { | ||||
| }) | ||||
|  | ||||
| describe('testing nested call expressions', () => { | ||||
|   it('callExp in a binExp in a callExp', () => { | ||||
|   it('callExp in a binExp in a callExp', async () => { | ||||
|     const code = 'const myVar = min(100, 1 + legLen(5, 3))' | ||||
|     const { body } = parse(code) | ||||
|     const { body } = await parse(code) | ||||
|     const myVarInit = (body?.[0] as any).declarations[0]?.init | ||||
|     expect(myVarInit).toEqual({ | ||||
|       type: 'CallExpression', | ||||
| @ -1585,8 +1587,8 @@ describe('testing nested call expressions', () => { | ||||
|  | ||||
| describe('should recognise callExpresions in binaryExpressions', () => { | ||||
|   const code = "xLineTo(segEndX('seg02', %) + 1, %)" | ||||
|   it('should recognise the callExp', () => { | ||||
|     const { body } = parse(code) | ||||
|   it('should recognise the callExp', async () => { | ||||
|     const { body } = await parse(code) | ||||
|     const callExpArgs = (body?.[0] as any).expression?.arguments | ||||
|     expect(callExpArgs).toEqual([ | ||||
|       { | ||||
|  | ||||
| @ -12,7 +12,7 @@ const mySketch001 = startSketchOn('XY') | ||||
|   |> lineTo([-1.59, -1.54], %) | ||||
|   |> lineTo([0.46, -5.82], %) | ||||
|   // |> rx(45, %)` | ||||
|     const programMemory = await enginelessExecutor(parse(code)) | ||||
|     const programMemory = await enginelessExecutor(await parse(code)) | ||||
|     // @ts-ignore | ||||
|     const sketch001 = programMemory?.root?.mySketch001 | ||||
|     expect(sketch001).toEqual({ | ||||
| @ -68,7 +68,7 @@ const mySketch001 = startSketchOn('XY') | ||||
|   |> lineTo([0.46, -5.82], %) | ||||
|   // |> rx(45, %) | ||||
|   |> extrude(2, %)` | ||||
|     const programMemory = await enginelessExecutor(parse(code)) | ||||
|     const programMemory = await enginelessExecutor(await parse(code)) | ||||
|     // @ts-ignore | ||||
|     const sketch001 = programMemory?.root?.mySketch001 | ||||
|     expect(sketch001).toEqual({ | ||||
| @ -150,7 +150,7 @@ const sk2 = startSketchOn('XY') | ||||
|   |> extrude(2, %) | ||||
|  | ||||
| ` | ||||
|     const programMemory = await enginelessExecutor(parse(code)) | ||||
|     const programMemory = await enginelessExecutor(await parse(code)) | ||||
|     // @ts-ignore | ||||
|     const geos = [programMemory?.root?.theExtrude, programMemory?.root?.sk2] | ||||
|     expect(geos).toEqual([ | ||||
|  | ||||
| @ -398,7 +398,7 @@ async function exe( | ||||
|   code: string, | ||||
|   programMemory: ProgramMemory = { root: {}, return: null } | ||||
| ) { | ||||
|   const ast = parse(code) | ||||
|   const ast = await parse(code) | ||||
|  | ||||
|   const result = await enginelessExecutor(ast, programMemory) | ||||
|   return result | ||||
|  | ||||
| @ -4,7 +4,7 @@ import { Identifier, parse, initPromise, Parameter } from './wasm' | ||||
| beforeAll(() => initPromise) | ||||
|  | ||||
| describe('testing getNodePathFromSourceRange', () => { | ||||
|   it('test it gets the right path for a `lineTo` CallExpression within a SketchExpression', () => { | ||||
|   it('test it gets the right path for a `lineTo` CallExpression within a SketchExpression', async () => { | ||||
|     const code = ` | ||||
| const myVar = 5 | ||||
| const sk3 = startSketchAt([0, 0]) | ||||
| @ -19,14 +19,14 @@ const sk3 = startSketchAt([0, 0]) | ||||
|       lineToSubstringIndex + subStr.length, | ||||
|     ] | ||||
|  | ||||
|     const ast = parse(code) | ||||
|     const ast = await parse(code) | ||||
|     const nodePath = getNodePathFromSourceRange(ast, sourceRange) | ||||
|     const { node } = getNodeFromPath<any>(ast, nodePath) | ||||
|  | ||||
|     expect([node.start, node.end]).toEqual(sourceRange) | ||||
|     expect(node.type).toBe('CallExpression') | ||||
|   }) | ||||
|   it('gets path right for function definition params', () => { | ||||
|   it('gets path right for function definition params', async () => { | ||||
|     const code = `fn cube = (pos, scale) => { | ||||
|   const sg = startSketchAt(pos) | ||||
|     |> line([0, scale], %) | ||||
| @ -44,7 +44,7 @@ const b1 = cube([0,0], 10)` | ||||
|       subStrIndex + 'pos'.length, | ||||
|     ] | ||||
|  | ||||
|     const ast = parse(code) | ||||
|     const ast = await parse(code) | ||||
|     const nodePath = getNodePathFromSourceRange(ast, sourceRange) | ||||
|     const node = getNodeFromPath<Parameter>(ast, nodePath).node | ||||
|  | ||||
| @ -60,7 +60,7 @@ const b1 = cube([0,0], 10)` | ||||
|     expect(node.type).toBe('Parameter') | ||||
|     expect(node.identifier.name).toBe('pos') | ||||
|   }) | ||||
|   it('gets path right for deep within function definition body', () => { | ||||
|   it('gets path right for deep within function definition body', async () => { | ||||
|     const code = `fn cube = (pos, scale) => { | ||||
|   const sg = startSketchAt(pos) | ||||
|     |> line([0, scale], %) | ||||
| @ -78,7 +78,7 @@ const b1 = cube([0,0], 10)` | ||||
|       subStrIndex + 'scale'.length, | ||||
|     ] | ||||
|  | ||||
|     const ast = parse(code) | ||||
|     const ast = await parse(code) | ||||
|     const nodePath = getNodePathFromSourceRange(ast, sourceRange) | ||||
|     const node = getNodeFromPath<Identifier>(ast, nodePath).node | ||||
|     expect(nodePath).toEqual([ | ||||
|  | ||||
							
								
								
									
										81
									
								
								src/lang/parser.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										81
									
								
								src/lang/parser.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,81 @@ | ||||
| import { | ||||
|   ParserWorkerResponse, | ||||
|   WasmWorker, | ||||
|   WasmWorkerEventType, | ||||
|   ParserWorkerCallResponse, | ||||
| } from 'lang/workers/types' | ||||
| import Worker from 'lang/workers/parser?worker' | ||||
| import { Program, wasmUrl } from 'lang/wasm' | ||||
| import { KCLError } from 'lang/errors' | ||||
| import { v4 as uuidv4 } from 'uuid' | ||||
|  | ||||
| export default class Parser { | ||||
|   worker: any = new Worker({ name: 'parse' }) | ||||
|   intitalized: boolean = false | ||||
|   mappings: Map<string, Program | KCLError> = new Map() | ||||
|  | ||||
|   async parse(code: string): Promise<Program> { | ||||
|     this.ensureWorker() | ||||
|     const uuid = uuidv4() | ||||
|     this.worker.postMessage({ | ||||
|       worker: WasmWorker.Parser, | ||||
|       eventType: WasmWorkerEventType.Call, | ||||
|       eventData: { | ||||
|         uuid, | ||||
|         code, | ||||
|       }, | ||||
|     }) | ||||
|     let result = await this.waitForResult(uuid) | ||||
|     if (result instanceof KCLError) { | ||||
|       throw result | ||||
|     } | ||||
|     return result | ||||
|   } | ||||
|  | ||||
|   waitForResult(uuid: string): Promise<Program | KCLError> { | ||||
|     return new Promise((resolve) => { | ||||
|       const result = this.mappings.get(uuid) | ||||
|       if (result) { | ||||
|         this.mappings.delete(uuid) | ||||
|         resolve(result) | ||||
|       } else { | ||||
|         setTimeout(() => { | ||||
|           resolve(this.waitForResult(uuid)) | ||||
|         }, 100) | ||||
|       } | ||||
|     }) | ||||
|   } | ||||
|  | ||||
|   ensureWorker() { | ||||
|     if (!this.intitalized) { | ||||
|       this.start() | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   // Start the worker. | ||||
|   start() { | ||||
|     if (this.intitalized) { | ||||
|       console.log('Worker already initialized') | ||||
|       return | ||||
|     } | ||||
|     this.worker.postMessage({ | ||||
|       worker: WasmWorker.Parser, | ||||
|       eventType: WasmWorkerEventType.Init, | ||||
|       eventData: { | ||||
|         wasmUrl: wasmUrl(), | ||||
|       }, | ||||
|     }) | ||||
|  | ||||
|     this.worker.onmessage = function (r: ParserWorkerResponse) { | ||||
|       switch (r.eventType) { | ||||
|         case WasmWorkerEventType.Init: | ||||
|           this.intitalized = true | ||||
|           break | ||||
|         case WasmWorkerEventType.Call: | ||||
|           const c = r.response as ParserWorkerCallResponse | ||||
|           this.mappings.set(c.uuid, c.response) | ||||
|           break | ||||
|       } | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @ -4,53 +4,53 @@ import fs from 'node:fs' | ||||
| beforeAll(() => initPromise) | ||||
|  | ||||
| describe('recast', () => { | ||||
|   it('recasts a simple program', () => { | ||||
|   it('recasts a simple program', async () => { | ||||
|     const code = '1 + 2' | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code) | ||||
|   }) | ||||
|   it('variable declaration', () => { | ||||
|   it('variable declaration', async () => { | ||||
|     const code = 'const myVar = 5' | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code) | ||||
|   }) | ||||
|   it("variable declaration that's binary with string", () => { | ||||
|   it("variable declaration that's binary with string", async () => { | ||||
|     const code = "const myVar = 5 + 'yo'" | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code) | ||||
|     const codeWithOtherQuotes = 'const myVar = 5 + "yo"' | ||||
|     const { ast: ast2 } = code2ast(codeWithOtherQuotes) | ||||
|     const { ast: ast2 } = await code2ast(codeWithOtherQuotes) | ||||
|     expect(recast(ast2).trim()).toBe(codeWithOtherQuotes) | ||||
|   }) | ||||
|   it('test assigning two variables, the second summing with the first', () => { | ||||
|   it('test assigning two variables, the second summing with the first', async () => { | ||||
|     const code = `const myVar = 5 | ||||
| const newVar = myVar + 1 | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
|   it('test assigning a var by cont concatenating two strings string', () => { | ||||
|   it('test assigning a var by cont concatenating two strings string', async () => { | ||||
|     const code = fs.readFileSync( | ||||
|       './src/lang/testExamples/variableDeclaration.cado', | ||||
|       'utf-8' | ||||
|     ) | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code.trim()) | ||||
|   }) | ||||
|   it('test with function call', () => { | ||||
|   it('test with function call', async () => { | ||||
|     const code = `const myVar = "hello" | ||||
| log(5, myVar) | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
|   it('function declaration with call', () => { | ||||
|   it('function declaration with call', async () => { | ||||
|     const code = [ | ||||
|       'fn funcN = (a, b) => {', | ||||
|       '  return a + b', | ||||
| @ -58,22 +58,22 @@ log(5, myVar) | ||||
|       'const theVar = 60', | ||||
|       'const magicNum = funcN(9, theVar)', | ||||
|     ].join('\n') | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code) | ||||
|   }) | ||||
|   it('recast sketch declaration', () => { | ||||
|   it('recast sketch declaration', async () => { | ||||
|     let code = `const mySketch = startSketchAt([0, 0]) | ||||
|   |> lineTo([0, 1], %, "myPath") | ||||
|   |> lineTo([1, 1], %) | ||||
|   |> lineTo([1, 0], %, "rightPath") | ||||
|   |> close(%) | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
|   it('sketch piped into callExpression', () => { | ||||
|   it('sketch piped into callExpression', async () => { | ||||
|     const code = [ | ||||
|       'const mySk1 = startSketchAt([0, 0])', | ||||
|       '  |> lineTo([1, 1], %)', | ||||
| @ -81,11 +81,11 @@ log(5, myVar) | ||||
|       '  |> lineTo([1, 1], %)', | ||||
|       '  |> rx(90, %)', | ||||
|     ].join('\n') | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code.trim()) | ||||
|   }) | ||||
|   it('recast BinaryExpression piped into CallExpression', () => { | ||||
|   it('recast BinaryExpression piped into CallExpression', async () => { | ||||
|     const code = [ | ||||
|       'fn myFn = (a) => {', | ||||
|       '  return a + 1', | ||||
| @ -93,49 +93,49 @@ log(5, myVar) | ||||
|       'const myVar = 5 + 1', | ||||
|       '  |> myFn(%)', | ||||
|     ].join('\n') | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code) | ||||
|   }) | ||||
|   it('recast nested binary expression', () => { | ||||
|   it('recast nested binary expression', async () => { | ||||
|     const code = ['const myVar = 1 + 2 * 5'].join('\n') | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code.trim()) | ||||
|   }) | ||||
|   it('recast nested binary expression with parans', () => { | ||||
|   it('recast nested binary expression with parans', async () => { | ||||
|     const code = ['const myVar = 1 + (1 + 2) * 5'].join('\n') | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code.trim()) | ||||
|   }) | ||||
|   it('unnecessary paran wrap will be remove', () => { | ||||
|   it('unnecessary paran wrap will be remove', async () => { | ||||
|     const code = ['const myVar = 1 + (2 * 5)'].join('\n') | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code.replace('(', '').replace(')', '')) | ||||
|   }) | ||||
|   it('complex nested binary expression', () => { | ||||
|   it('complex nested binary expression', async () => { | ||||
|     const code = ['1 * ((2 + 3) / 4 + 5)'].join('\n') | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code.trim()) | ||||
|   }) | ||||
|   it('multiplied paren expressions', () => { | ||||
|   it('multiplied paren expressions', async () => { | ||||
|     const code = ['3 + (1 + 2) * (3 + 4)'].join('\n') | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code.trim()) | ||||
|   }) | ||||
|   it('recast array declaration', () => { | ||||
|   it('recast array declaration', async () => { | ||||
|     const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join( | ||||
|       '\n' | ||||
|     ) | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code.trim()) | ||||
|   }) | ||||
|   it('recast long array declaration', () => { | ||||
|   it('recast long array declaration', async () => { | ||||
|     const code = [ | ||||
|       'const three = 3', | ||||
|       'const yo = [', | ||||
| @ -146,11 +146,11 @@ log(5, myVar) | ||||
|       "  'hey oooooo really long long long'", | ||||
|       ']', | ||||
|     ].join('\n') | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code.trim()) | ||||
|   }) | ||||
|   it('recast long object execution', () => { | ||||
|   it('recast long object execution', async () => { | ||||
|     const code = `const three = 3 | ||||
| const yo = { | ||||
|   aStr: 'str', | ||||
| @ -159,43 +159,43 @@ const yo = { | ||||
|   binExp: 4 + 5 | ||||
| } | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
|   it('recast short object execution', () => { | ||||
|   it('recast short object execution', async () => { | ||||
|     const code = `const yo = { key: 'val' } | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
|   it('recast object execution with member expression', () => { | ||||
|   it('recast object execution with member expression', async () => { | ||||
|     const code = `const yo = { a: { b: { c: '123' } } } | ||||
| const key = 'c' | ||||
| const myVar = yo.a['b'][key] | ||||
| const key2 = 'b' | ||||
| const myVar2 = yo['a'][key2].c | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| describe('testing recasting with comments and whitespace', () => { | ||||
|   it('code with comments', () => { | ||||
|   it('code with comments', async () => { | ||||
|     const code = `const yo = { a: { b: { c: '123' } } } | ||||
| // this is a comment | ||||
| const key = 'c' | ||||
| ` | ||||
|  | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|  | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
|   it('code with comment and extra lines', () => { | ||||
|   it('code with comment and extra lines', async () => { | ||||
|     const code = `const yo = 'c' | ||||
|  | ||||
| /* this is | ||||
| @ -203,23 +203,23 @@ a | ||||
| comment */ | ||||
| const yo = 'bing' | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
|   it('comments at the start and end', () => { | ||||
|   it('comments at the start and end', async () => { | ||||
|     const code = `// this is a comment | ||||
| const yo = { a: { b: { c: '123' } } } | ||||
| const key = 'c' | ||||
|  | ||||
| // this is also a comment | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
|   it('comments in a fn block', () => { | ||||
|     const code = `fn myFn = () => { | ||||
|   it('comments in a fn block', async () => { | ||||
|     const code = `fn myFn = async () => { | ||||
|   // this is a comment | ||||
|   const yo = { a: { b: { c: '123' } } } | ||||
|  | ||||
| @ -229,11 +229,11 @@ const key = 'c' | ||||
|   // this is also a comment | ||||
| } | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
|   it('comments in a pipe expression', () => { | ||||
|   it('comments in a pipe expression', async () => { | ||||
|     const code = [ | ||||
|       'const mySk1 = startSketchAt([0, 0])', | ||||
|       '  |> lineTo([1, 1], %)', | ||||
| @ -242,11 +242,11 @@ const key = 'c' | ||||
|       '  // a comment', | ||||
|       '  |> rx(90, %)', | ||||
|     ].join('\n') | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code) | ||||
|   }) | ||||
|   it('comments sprinkled in all over the place', () => { | ||||
|   it('comments sprinkled in all over the place', async () => { | ||||
|     const code = ` | ||||
| /* comment at start */ | ||||
|  | ||||
| @ -268,7 +268,7 @@ const mySk1 = startSketchAt([0, 0]) | ||||
|   one more for good measure | ||||
|   */ | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(`/* comment at start */ | ||||
|  | ||||
| @ -289,37 +289,37 @@ const mySk1 = startSketchAt([0, 0]) | ||||
| }) | ||||
|  | ||||
| describe('testing call Expressions in BinaryExpressions and UnaryExpressions', () => { | ||||
|   it('nested callExpression in binaryExpression', () => { | ||||
|   it('nested callExpression in binaryExpression', async () => { | ||||
|     const code = 'const myVar = 2 + min(100, legLen(5, 3))' | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code) | ||||
|   }) | ||||
|   it('nested callExpression in unaryExpression', () => { | ||||
|   it('nested callExpression in unaryExpression', async () => { | ||||
|     const code = 'const myVar = -min(100, legLen(5, 3))' | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code) | ||||
|   }) | ||||
|   it('with unaryExpression in callExpression', () => { | ||||
|   it('with unaryExpression in callExpression', async () => { | ||||
|     const code = 'const myVar = min(5, -legLen(5, 4))' | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code) | ||||
|   }) | ||||
|   it('with unaryExpression in sketch situation', () => { | ||||
|   it('with unaryExpression in sketch situation', async () => { | ||||
|     const code = [ | ||||
|       'const part001 = startSketchAt([0, 0])', | ||||
|       '  |> line([-2.21, -legLen(5, min(3, 999))], %)', | ||||
|     ].join('\n') | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted.trim()).toBe(code) | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| describe('it recasts wrapped object expressions in pipe bodies with correct indentation', () => { | ||||
|   it('with a single line', () => { | ||||
|   it('with a single line', async () => { | ||||
|     const code = `const part001 = startSketchAt([-0.01, -0.08]) | ||||
|   |> line([0.62, 4.15], %, 'seg01') | ||||
|   |> line([2.77, -1.24], %) | ||||
| @ -330,35 +330,35 @@ describe('it recasts wrapped object expressions in pipe bodies with correct inde | ||||
|      }, %) | ||||
|   |> line([-0.42, -1.72], %) | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
|   it('recasts wrapped object expressions NOT in pipe body correctly', () => { | ||||
|   it('recasts wrapped object expressions NOT in pipe body correctly', async () => { | ||||
|     const code = `angledLineThatIntersects({ | ||||
|   angle: 201, | ||||
|   offset: -1.35, | ||||
|   intersectTag: 'seg01' | ||||
| }, %) | ||||
| ` | ||||
|     const { ast } = code2ast(code) | ||||
|     const { ast } = await code2ast(code) | ||||
|     const recasted = recast(ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| describe('it recasts binary expression using brackets where needed', () => { | ||||
|   it('when there are two minus in a row', () => { | ||||
|   it('when there are two minus in a row', async () => { | ||||
|     const code = `const part001 = 1 - (def - abc) | ||||
| ` | ||||
|     const recasted = recast(code2ast(code).ast) | ||||
|     const recasted = recast((await code2ast(code)).ast) | ||||
|     expect(recasted).toBe(code) | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| // helpers | ||||
|  | ||||
| function code2ast(code: string): { ast: Program } { | ||||
|   const ast = parse(code) | ||||
| async function code2ast(code: string): Promise<{ ast: Program }> { | ||||
|   const ast = await parse(code) | ||||
|   return { ast } | ||||
| } | ||||
|  | ||||
| @ -104,7 +104,7 @@ describe('testing changeSketchArguments', () => { | ||||
| ` | ||||
|     const code = genCode(lineToChange) | ||||
|     const expectedCode = genCode(lineAfterChange) | ||||
|     const ast = parse(code) | ||||
|     const ast = await parse(code) | ||||
|     const programMemory = await enginelessExecutor(ast) | ||||
|     const sourceStart = code.indexOf(lineToChange) | ||||
|     const { modifiedAst } = changeSketchArguments( | ||||
| @ -128,7 +128,7 @@ const mySketch001 = startSketchOn('XY') | ||||
|   // |> rx(45, %) | ||||
|   |> lineTo([-1.59, -1.54], %) | ||||
|   |> lineTo([0.46, -5.82], %)` | ||||
|     const ast = parse(code) | ||||
|     const ast = await parse(code) | ||||
|     const programMemory = await enginelessExecutor(ast) | ||||
|     const sourceStart = code.indexOf(lineToChange) | ||||
|     expect(sourceStart).toBe(95) | ||||
| @ -190,7 +190,7 @@ describe('testing addTagForSketchOnFace', () => { | ||||
|   |> lineTo([0.46, -5.82], %) | ||||
| ` | ||||
|     const code = genCode(originalLine) | ||||
|     const ast = parse(code) | ||||
|     const ast = await parse(code) | ||||
|     const programMemory = await enginelessExecutor(ast) | ||||
|     const sourceStart = code.indexOf(originalLine) | ||||
|     const sourceRange: [number, number] = [ | ||||
|  | ||||
| @ -28,7 +28,7 @@ async function testingSwapSketchFnCall({ | ||||
|     type: 'default', | ||||
|     range: [startIndex, startIndex + callToSwap.length], | ||||
|   } | ||||
|   const ast = parse(inputCode) | ||||
|   const ast = await parse(inputCode) | ||||
|   const programMemory = await enginelessExecutor(ast) | ||||
|   const selections = { | ||||
|     codeBasedSelections: [range], | ||||
| @ -351,7 +351,7 @@ const part001 = startSketchOn('XY') | ||||
|   |> line([2.14, 1.35], %) // normal-segment | ||||
|   |> xLine(3.54, %)` | ||||
|   it('normal case works', async () => { | ||||
|     const programMemory = await enginelessExecutor(parse(code)) | ||||
|     const programMemory = await enginelessExecutor(await parse(code)) | ||||
|     const index = code.indexOf('// normal-segment') - 7 | ||||
|     const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange( | ||||
|       programMemory.root['part001'] as SketchGroup, | ||||
| @ -365,7 +365,7 @@ const part001 = startSketchOn('XY') | ||||
|     }) | ||||
|   }) | ||||
|   it('verify it works when the segment is in the `start` property', async () => { | ||||
|     const programMemory = await enginelessExecutor(parse(code)) | ||||
|     const programMemory = await enginelessExecutor(await parse(code)) | ||||
|     const index = code.indexOf('// segment-in-start') - 7 | ||||
|     const { __geoMeta, ...segment } = getSketchSegmentFromSourceRange( | ||||
|       programMemory.root['part001'] as SketchGroup, | ||||
|  | ||||
| @ -15,53 +15,53 @@ beforeAll(() => initPromise) | ||||
|  | ||||
| describe('testing getConstraintType', () => { | ||||
|   const helper = getConstraintTypeFromSourceHelper | ||||
|   it('testing line', () => { | ||||
|     expect(helper(`line([5, myVar], %)`)).toBe('yRelative') | ||||
|     expect(helper(`line([myVar, 5], %)`)).toBe('xRelative') | ||||
|   it('testing line', async () => { | ||||
|     expect(await helper(`line([5, myVar], %)`)).toBe('yRelative') | ||||
|     expect(await helper(`line([myVar, 5], %)`)).toBe('xRelative') | ||||
|   }) | ||||
|   it('testing lineTo', () => { | ||||
|     expect(helper(`lineTo([5, myVar], %)`)).toBe('yAbsolute') | ||||
|     expect(helper(`lineTo([myVar, 5], %)`)).toBe('xAbsolute') | ||||
|   it('testing lineTo', async () => { | ||||
|     expect(await helper(`lineTo([5, myVar], %)`)).toBe('yAbsolute') | ||||
|     expect(await helper(`lineTo([myVar, 5], %)`)).toBe('xAbsolute') | ||||
|   }) | ||||
|   it('testing angledLine', () => { | ||||
|     expect(helper(`angledLine([5, myVar], %)`)).toBe('length') | ||||
|     expect(helper(`angledLine([myVar, 5], %)`)).toBe('angle') | ||||
|   it('testing angledLine', async () => { | ||||
|     expect(await helper(`angledLine([5, myVar], %)`)).toBe('length') | ||||
|     expect(await helper(`angledLine([myVar, 5], %)`)).toBe('angle') | ||||
|   }) | ||||
|   it('testing angledLineOfXLength', () => { | ||||
|     expect(helper(`angledLineOfXLength([5, myVar], %)`)).toBe('xRelative') | ||||
|     expect(helper(`angledLineOfXLength([myVar, 5], %)`)).toBe('angle') | ||||
|   it('testing angledLineOfXLength', async () => { | ||||
|     expect(await helper(`angledLineOfXLength([5, myVar], %)`)).toBe('xRelative') | ||||
|     expect(await helper(`angledLineOfXLength([myVar, 5], %)`)).toBe('angle') | ||||
|   }) | ||||
|   it('testing angledLineToX', () => { | ||||
|     expect(helper(`angledLineToX([5, myVar], %)`)).toBe('xAbsolute') | ||||
|     expect(helper(`angledLineToX([myVar, 5], %)`)).toBe('angle') | ||||
|   it('testing angledLineToX', async () => { | ||||
|     expect(await helper(`angledLineToX([5, myVar], %)`)).toBe('xAbsolute') | ||||
|     expect(await helper(`angledLineToX([myVar, 5], %)`)).toBe('angle') | ||||
|   }) | ||||
|   it('testing angledLineOfYLength', () => { | ||||
|     expect(helper(`angledLineOfYLength([5, myVar], %)`)).toBe('yRelative') | ||||
|     expect(helper(`angledLineOfYLength([myVar, 5], %)`)).toBe('angle') | ||||
|   it('testing angledLineOfYLength', async () => { | ||||
|     expect(await helper(`angledLineOfYLength([5, myVar], %)`)).toBe('yRelative') | ||||
|     expect(await helper(`angledLineOfYLength([myVar, 5], %)`)).toBe('angle') | ||||
|   }) | ||||
|   it('testing angledLineToY', () => { | ||||
|     expect(helper(`angledLineToY([5, myVar], %)`)).toBe('yAbsolute') | ||||
|     expect(helper(`angledLineToY([myVar, 5], %)`)).toBe('angle') | ||||
|   it('testing angledLineToY', async () => { | ||||
|     expect(await helper(`angledLineToY([5, myVar], %)`)).toBe('yAbsolute') | ||||
|     expect(await helper(`angledLineToY([myVar, 5], %)`)).toBe('angle') | ||||
|   }) | ||||
|   const helper2 = getConstraintTypeFromSourceHelper2 | ||||
|   it('testing xLine', () => { | ||||
|     expect(helper2(`xLine(5, %)`)).toBe('yRelative') | ||||
|   it('testing xLine', async () => { | ||||
|     expect(await helper2(`xLine(5, %)`)).toBe('yRelative') | ||||
|   }) | ||||
|   it('testing yLine', () => { | ||||
|     expect(helper2(`yLine(5, %)`)).toBe('xRelative') | ||||
|   it('testing yLine', async () => { | ||||
|     expect(await helper2(`yLine(5, %)`)).toBe('xRelative') | ||||
|   }) | ||||
|   it('testing xLineTo', () => { | ||||
|     expect(helper2(`xLineTo(5, %)`)).toBe('yAbsolute') | ||||
|   it('testing xLineTo', async () => { | ||||
|     expect(await helper2(`xLineTo(5, %)`)).toBe('yAbsolute') | ||||
|   }) | ||||
|   it('testing yLineTo', () => { | ||||
|     expect(helper2(`yLineTo(5, %)`)).toBe('xAbsolute') | ||||
|   it('testing yLineTo', async () => { | ||||
|     expect(await helper2(`yLineTo(5, %)`)).toBe('xAbsolute') | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| function getConstraintTypeFromSourceHelper( | ||||
| async function getConstraintTypeFromSourceHelper( | ||||
|   code: string | ||||
| ): ReturnType<typeof getConstraintType> { | ||||
|   const ast = parse(code) | ||||
| ): Promise<ReturnType<typeof getConstraintType>> { | ||||
|   const ast = await parse(code) | ||||
|   const args = (ast.body[0] as any).expression.arguments[0].elements as [ | ||||
|     Value, | ||||
|     Value | ||||
| @ -69,10 +69,10 @@ function getConstraintTypeFromSourceHelper( | ||||
|   const fnName = (ast.body[0] as any).expression.callee.name as ToolTip | ||||
|   return getConstraintType(args, fnName) | ||||
| } | ||||
| function getConstraintTypeFromSourceHelper2( | ||||
| async function getConstraintTypeFromSourceHelper2( | ||||
|   code: string | ||||
| ): ReturnType<typeof getConstraintType> { | ||||
|   const ast = parse(code) | ||||
| ): Promise<ReturnType<typeof getConstraintType>> { | ||||
|   const ast = await parse(code) | ||||
|   const arg = (ast.body[0] as any).expression.arguments[0] as Value | ||||
|   const fnName = (ast.body[0] as any).expression.callee.name as ToolTip | ||||
|   return getConstraintType(arg, fnName) | ||||
| @ -197,7 +197,7 @@ const part001 = startSketchOn('XY') | ||||
|   |> yLine(segLen('seg01', %), %) // ln-yLineTo-free should convert to yLine | ||||
| ` | ||||
|   it('should transform the ast', async () => { | ||||
|     const ast = parse(inputScript) | ||||
|     const ast = await parse(inputScript) | ||||
|     const selectionRanges: Selections['codeBasedSelections'] = inputScript | ||||
|       .split('\n') | ||||
|       .filter((ln) => ln.includes('//')) | ||||
| @ -284,7 +284,7 @@ const part001 = startSketchOn('XY') | ||||
|   |> xLineTo(myVar3, %) // select for horizontal constraint 10 | ||||
|   |> angledLineToY([301, myVar], %) // select for vertical constraint 10 | ||||
| ` | ||||
|     const ast = parse(inputScript) | ||||
|     const ast = await parse(inputScript) | ||||
|     const selectionRanges: Selections['codeBasedSelections'] = inputScript | ||||
|       .split('\n') | ||||
|       .filter((ln) => ln.includes('// select for horizontal constraint')) | ||||
| @ -342,7 +342,7 @@ const part001 = startSketchOn('XY') | ||||
|   |> angledLineToX([333, myVar3], %) // select for horizontal constraint 10 | ||||
|   |> yLineTo(myVar, %) // select for vertical constraint 10 | ||||
| ` | ||||
|     const ast = parse(inputScript) | ||||
|     const ast = await parse(inputScript) | ||||
|     const selectionRanges: Selections['codeBasedSelections'] = inputScript | ||||
|       .split('\n') | ||||
|       .filter((ln) => ln.includes('// select for vertical constraint')) | ||||
| @ -433,7 +433,7 @@ async function helperThing( | ||||
|   linesOfInterest: string[], | ||||
|   constraint: ConstraintType | ||||
| ): Promise<string> { | ||||
|   const ast = parse(inputScript) | ||||
|   const ast = await parse(inputScript) | ||||
|   const selectionRanges: Selections['codeBasedSelections'] = inputScript | ||||
|     .split('\n') | ||||
|     .filter((ln) => | ||||
| @ -465,7 +465,7 @@ async function helperThing( | ||||
| } | ||||
|  | ||||
| describe('testing getConstraintLevelFromSourceRange', () => { | ||||
|   it('should divide up lines into free, partial and fully contrained', () => { | ||||
|   it('should divide up lines into free, partial and fully contrained', async () => { | ||||
|     const code = `const baseLength = 3 | ||||
| const baseThick = 1 | ||||
| const armThick = 0.5 | ||||
| @ -495,7 +495,7 @@ const part001 = startSketchOn('XY') | ||||
|   |> line([-1.49, 1.06], %) // free | ||||
|   |> xLine(-3.43 + 0, %) // full | ||||
|   |> angledLineOfXLength([243 + 0, 1.2 + 0], %) // full` | ||||
|     const ast = parse(code) | ||||
|     const ast = await parse(code) | ||||
|     const constraintLevels: ReturnType< | ||||
|       typeof getConstraintLevelFromSourceRange | ||||
|     >['level'][] = ['full', 'partial', 'free'] | ||||
|  | ||||
| @ -15,9 +15,9 @@ describe('testing angledLineThatIntersects', () => { | ||||
|   offset: ${offset}, | ||||
| }, %, "yo2") | ||||
| const intersect = segEndX('yo2', part001)` | ||||
|     const { root } = await enginelessExecutor(parse(code('-1'))) | ||||
|     const { root } = await enginelessExecutor(await parse(code('-1'))) | ||||
|     expect(root.intersect.value).toBe(1 + Math.sqrt(2)) | ||||
|     const { root: noOffset } = await enginelessExecutor(parse(code('0'))) | ||||
|     const { root: noOffset } = await enginelessExecutor(await parse(code('0'))) | ||||
|     expect(noOffset.intersect.value).toBeCloseTo(1) | ||||
|   }) | ||||
| }) | ||||
|  | ||||
| @ -25,6 +25,8 @@ import { AppInfo } from 'wasm-lib/kcl/bindings/AppInfo' | ||||
| import { CoreDumpManager } from 'lib/coredump' | ||||
| import openWindow from 'lib/openWindow' | ||||
| import { DefaultPlanes } from 'wasm-lib/kcl/bindings/DefaultPlanes' | ||||
| import { rangeTypeFix } from './workers/types' | ||||
| import { parser } from 'lib/singletons' | ||||
|  | ||||
| export type { Program } from '../wasm-lib/kcl/bindings/Program' | ||||
| export type { Value } from '../wasm-lib/kcl/bindings/Value' | ||||
| @ -103,10 +105,18 @@ const initialise = async () => { | ||||
|  | ||||
| export const initPromise = initialise() | ||||
|  | ||||
| export const rangeTypeFix = (ranges: number[][]): [number, number][] => | ||||
|   ranges.map(([start, end]) => [start, end]) | ||||
| export type PathToNode = [string | number, string][] | ||||
|  | ||||
| export const parse = (code: string): Program => { | ||||
| interface Memory { | ||||
|   [key: string]: MemoryItem | ||||
| } | ||||
|  | ||||
| export interface ProgramMemory { | ||||
|   root: Memory | ||||
|   return: ProgramReturn | null | ||||
| } | ||||
|  | ||||
| /*export const parse = (code: string): Program => { | ||||
|   try { | ||||
|     const program: Program = parse_wasm(code) | ||||
|     return program | ||||
| @ -121,17 +131,10 @@ export const parse = (code: string): Program => { | ||||
|     console.log(kclError) | ||||
|     throw kclError | ||||
|   } | ||||
| } | ||||
| }*/ | ||||
|  | ||||
| export type PathToNode = [string | number, string][] | ||||
|  | ||||
| interface Memory { | ||||
|   [key: string]: MemoryItem | ||||
| } | ||||
|  | ||||
| export interface ProgramMemory { | ||||
|   root: Memory | ||||
|   return: ProgramReturn | null | ||||
| export const parse = async (code: string): Promise<Program> => { | ||||
|   return parser.parse(code) | ||||
| } | ||||
|  | ||||
| export const executor = async ( | ||||
|  | ||||
							
								
								
									
										60
									
								
								src/lang/workers/parser.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/lang/workers/parser.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,60 @@ | ||||
| import init, { parse_wasm } from 'wasm-lib/pkg/wasm_lib' | ||||
| import { Program } from 'lang/wasm' | ||||
| import { KclError as RustKclError } from 'wasm-lib/kcl/bindings/KclError' | ||||
| import { KCLError } from 'lang/errors' | ||||
| import { | ||||
|   WasmWorkerEventType, | ||||
|   WasmWorkerEvent, | ||||
|   WasmWorkerOptions, | ||||
|   ParserWorkerCall, | ||||
|   rangeTypeFix, | ||||
| } from 'lang/workers/types' | ||||
|  | ||||
| // Initialise the wasm module. | ||||
| const initialise = async (wasmUrl: string) => { | ||||
|   const input = await fetch(wasmUrl) | ||||
|   const buffer = await input.arrayBuffer() | ||||
|   return init(buffer) | ||||
| } | ||||
|  | ||||
| onmessage = function (event) { | ||||
|   const { worker, eventType, eventData }: WasmWorkerEvent = event.data | ||||
|  | ||||
|   switch (eventType) { | ||||
|     case WasmWorkerEventType.Init: | ||||
|       let { wasmUrl }: WasmWorkerOptions = eventData as WasmWorkerOptions | ||||
|       initialise(wasmUrl) | ||||
|         .then((instantiatedModule) => { | ||||
|           console.log('Worker: WASM module loaded', worker, instantiatedModule) | ||||
|           postMessage({ | ||||
|             eventType: WasmWorkerEventType.Init, | ||||
|             response: { worker: worker, initialized: true }, | ||||
|           }) | ||||
|         }) | ||||
|         .catch((error) => { | ||||
|           console.error('Worker: Error loading wasm module', worker, error) | ||||
|         }) | ||||
|       break | ||||
|     case WasmWorkerEventType.Call: | ||||
|       const data = eventData as ParserWorkerCall | ||||
|       try { | ||||
|         const program: Program = parse_wasm(data.code) | ||||
|         postMessage({ uuid: data.uuid, response: program }) | ||||
|       } catch (e: any) { | ||||
|         const parsed: RustKclError = JSON.parse(e.toString()) | ||||
|         const kclError = new KCLError( | ||||
|           parsed.kind, | ||||
|           parsed.msg, | ||||
|           rangeTypeFix(parsed.sourceRanges) | ||||
|         ) | ||||
|  | ||||
|         postMessage({ | ||||
|           eventType: WasmWorkerEventType.Call, | ||||
|           response: { uuid: data.uuid, response: kclError }, | ||||
|         }) | ||||
|       } | ||||
|       break | ||||
|     default: | ||||
|       console.error('Worker: Unknown message type', worker, eventType) | ||||
|   } | ||||
| } | ||||
							
								
								
									
										43
									
								
								src/lang/workers/types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								src/lang/workers/types.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,43 @@ | ||||
| import { KCLError } from 'lang/errors' | ||||
| import { Program } from 'lang/wasm' | ||||
|  | ||||
| export enum WasmWorker { | ||||
|   Parser = 'parser', | ||||
| } | ||||
| export enum WasmWorkerEventType { | ||||
|   Init = 'init', | ||||
|   Call = 'call', | ||||
| } | ||||
|  | ||||
| export interface WasmWorkerOptions { | ||||
|   wasmUrl: string | ||||
| } | ||||
|  | ||||
| export interface ParserWorkerCall { | ||||
|   uuid: string | ||||
|   code: string | ||||
| } | ||||
|  | ||||
| export interface ParserWorkerInitResponse { | ||||
|   worker: WasmWorker | ||||
|   initialized: boolean | ||||
| } | ||||
|  | ||||
| export interface ParserWorkerCallResponse { | ||||
|   uuid: string | ||||
|   response: Program | KCLError | ||||
| } | ||||
|  | ||||
| export interface ParserWorkerResponse { | ||||
|   eventType: WasmWorkerEventType | ||||
|   response: ParserWorkerInitResponse | ParserWorkerCallResponse | ||||
| } | ||||
|  | ||||
| export interface WasmWorkerEvent { | ||||
|   eventType: WasmWorkerEventType | ||||
|   eventData: Uint8Array | WasmWorkerOptions | ParserWorkerCall | ||||
|   worker: WasmWorker | ||||
| } | ||||
|  | ||||
| export const rangeTypeFix = (ranges: number[][]): [number, number][] => | ||||
|   ranges.map(([start, end]) => [start, end]) | ||||
| @ -119,9 +119,9 @@ export async function getEventForSelectWithPoint( | ||||
|   } | ||||
| } | ||||
|  | ||||
| export function getEventForSegmentSelection( | ||||
| export async function getEventForSegmentSelection( | ||||
|   obj: Object3D<Object3DEventMap> | ||||
| ): ModelingMachineEvent | null { | ||||
| ): Promise<ModelingMachineEvent | null> { | ||||
|   const group = getParentGroup(obj, [ | ||||
|     STRAIGHT_SEGMENT, | ||||
|     TANGENTIAL_ARC_TO_SEGMENT, | ||||
| @ -143,7 +143,7 @@ export function getEventForSegmentSelection( | ||||
|   // previous drags don't update ast for efficiency reasons | ||||
|   // So we want to make sure we have and updated ast with | ||||
|   // accurate source ranges | ||||
|   const updatedAst = parse(codeManager.code) | ||||
|   const updatedAst = await parse(codeManager.code) | ||||
|   const node = getNodeFromPath<CallExpression>( | ||||
|     updatedAst, | ||||
|     pathToNode, | ||||
| @ -254,10 +254,10 @@ export function processCodeMirrorRanges({ | ||||
|   } | ||||
| } | ||||
|  | ||||
| function updateSceneObjectColors(codeBasedSelections: Selection[]) { | ||||
| async function updateSceneObjectColors(codeBasedSelections: Selection[]) { | ||||
|   let updated: Program | ||||
|   try { | ||||
|     updated = parse(recast(kclManager.ast)) | ||||
|     updated = await parse(recast(kclManager.ast)) | ||||
|   } catch (e) { | ||||
|     console.error('error parsing code in processCodeMirrorRanges', e) | ||||
|     return | ||||
|  | ||||
| @ -2,8 +2,10 @@ import { SceneEntities } from 'clientSideScene/sceneEntities' | ||||
| import { SceneInfra } from 'clientSideScene/sceneInfra' | ||||
| import { KclManager } from 'lang/KclSingleton' | ||||
| import CodeManager from 'lang/codeManager' | ||||
| import Parser from 'lang/parser' | ||||
| import { EngineCommandManager } from 'lang/std/engineConnection' | ||||
|  | ||||
| export const parser = new Parser() | ||||
| export const codeManager = new CodeManager() | ||||
|  | ||||
| export const engineCommandManager = new EngineCommandManager() | ||||
|  | ||||
| @ -82,7 +82,7 @@ export function useCalculateKclExpression({ | ||||
|   useEffect(() => { | ||||
|     const execAstAndSetResult = async () => { | ||||
|       const code = `const __result__ = ${value}` | ||||
|       const ast = parse(code) | ||||
|       const ast = await parse(code) | ||||
|       const _programMem: any = { root: {}, return: null } | ||||
|       availableVarInfo.variables.forEach(({ key, value }) => { | ||||
|         _programMem.root[key] = { type: 'userVal', value, __meta: [] } | ||||
|  | ||||
		Reference in New Issue
	
	Block a user
	