Compare commits
9 Commits
v1.0.0
...
parser-in-
Author | SHA1 | Date | |
---|---|---|---|
ae068b7ed6 | |||
5bdc899831 | |||
e0e6acf231 | |||
668bc863df | |||
59ce602d6e | |||
8ce819b28f | |||
ed5e276377 | |||
8549bd9486 | |||
56f9078812 |
@ -137,10 +137,10 @@ export function useCalc({
|
||||
setAvailableVarInfo(varInfo)
|
||||
}, [kclManager.ast, kclManager.programMemory, selectionRange])
|
||||
|
||||
useEffect(() => {
|
||||
useEffect(async () => {
|
||||
try {
|
||||
const code = `const __result__ = ${value}`
|
||||
const ast = parse(code)
|
||||
parse(code).then((ast) => {
|
||||
const _programMem: any = { root: {}, return: null }
|
||||
availableVarInfo.variables.forEach(({ key, value }) => {
|
||||
_programMem.root[key] = { type: 'userVal', value, __meta: [] }
|
||||
@ -165,6 +165,7 @@ export function useCalc({
|
||||
setCalcResult(typeof result === 'number' ? String(result) : 'NAN')
|
||||
init && setValueNode(init)
|
||||
})
|
||||
})
|
||||
} catch (e) {
|
||||
setCalcResult('NAN')
|
||||
setValueNode(null)
|
||||
|
@ -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