import { recast } from './recast' import { parser_wasm } from './abstractSyntaxTree' import { Program } from './abstractSyntaxTreeTypes' import fs from 'node:fs' import { initPromise } from './rust' beforeAll(() => initPromise) describe('recast', () => { it('recasts a simple program', () => { const code = '1 + 2' const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('variable declaration', () => { const code = 'const myVar = 5' const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it("variable declaration that's binary with string", () => { const code = "const myVar = 5 + 'yo'" const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) const codeWithOtherQuotes = 'const myVar = 5 + "yo"' const { ast: ast2 } = code2ast(codeWithOtherQuotes) expect(recast(ast2)).toBe(codeWithOtherQuotes) }) it('test assigning two variables, the second summing with the first', () => { const code = `const myVar = 5 const newVar = myVar + 1` const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('test assigning a var by cont concatenating two strings string', () => { const code = fs.readFileSync( './src/lang/testExamples/variableDeclaration.cado', 'utf-8' ) const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.trim()) }) it('test with function call', () => { const code = ` const myVar = "hello" log(5, myVar)` const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('function declaration with call', () => { const code = [ 'fn funcN = (a, b) => {', ' return a + b', '}', 'const theVar = 60', 'const magicNum = funcN(9, theVar)', ].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('recast sketch declaration', () => { let code = `const mySketch = startSketchAt([0, 0]) |> lineTo({ to: [0, 1], tag: "myPath" }, %) |> lineTo([1, 1], %) |> lineTo({ to: [1, 0], tag: "rightPath" }, %) |> close(%) show(mySketch) ` const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.trim()) }) it('sketch piped into callExpression', () => { const code = [ 'const mySk1 = startSketchAt([0, 0])', ' |> lineTo([1, 1], %)', ' |> lineTo({ to: [0, 1], tag: "myTag" }, %)', ' |> lineTo([1, 1], %)', ' |> rx(90, %)', ].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.trim()) }) it('recast BinaryExpression piped into CallExpression', () => { const code = [ 'fn myFn = (a) => {', ' return a + 1', '}', 'const myVar = 5 + 1', ' |> myFn(%)', ].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('recast nested binary expression', () => { const code = ['const myVar = 1 + 2 * 5'].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.trim()) }) it('recast nested binary expression with parans', () => { const code = ['const myVar = 1 + (1 + 2) * 5'].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.trim()) }) it('unnecessary paran wrap will be remove', () => { const code = ['const myVar = 1 + (2 * 5)'].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.replace('(', '').replace(')', '')) }) it('complex nested binary expression', () => { const code = ['1 * ((2 + 3) / 4 + 5)'].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.trim()) }) it('multiplied paren expressions', () => { const code = ['3 + (1 + 2) * (3 + 4)'].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.trim()) }) it('recast array declaration', () => { const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join( '\n' ) const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.trim()) }) it('recast long array declaration', () => { const code = [ 'const three = 3', 'const yo = [', ' 1,', " '2',", ' three,', ' 4 + 5,', " 'hey oooooo really long long long'", ']', ].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.trim()) }) it('recast long object exectution', () => { const code = `const three = 3 const yo = { aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5 }` const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.trim()) }) it('recast short object exectution', () => { const code = `const yo = { key: 'val' }` const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code.trim()) }) it('recast object execution with member expression', () => { 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 recasted = recast(ast) expect(recasted).toBe(code.trim()) }) }) describe('testing recasting with comments and whitespace', () => { it('code with comments', () => { const code = ` const yo = { a: { b: { c: '123' } } } // this is a comment const key = 'c'` const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('code with comment and extra lines', () => { const code = ` const yo = 'c' /* this is a comment */ const yo = 'bing'` const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('comments at the start and end', () => { 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 recasted = recast(ast) expect(recasted).toBe(code) }) it('comments in a fn block', () => { const code = ` const myFn = () => { // this is a comment const yo = { a: { b: { c: '123' } } } /* block comment */ const key = 'c' // this is also a comment }` const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('comments in a pipe expression', () => { const code = [ 'const mySk1 = startSketchAt([0, 0])', ' |> lineTo([1, 1], %)', ' |> lineTo({ to: [0, 1], tag: "myTag" }, %)', ' |> lineTo([1, 1], %)', ' // a comment', ' |> rx(90, %)', ].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('comments sprinkled in all over the place', () => { const code = ` /* comment at start */ const mySk1 = startSketchAt([0, 0]) |> lineTo([1, 1], %) // comment here |> lineTo({ to: [0, 1], tag: 'myTag' }, %) |> lineTo([1, 1], %) /* and here */ // a comment between pipe expression statements |> rx(90, %) // and another with just white space between others below |> ry(45, %) |> rx(45, %) /* one more for good measure */` const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) }) describe('testing call Expressions in BinaryExpressions and UnaryExpressions', () => { it('nested callExpression in binaryExpression', () => { const code = 'const myVar = 2 + min(100, legLen(5, 3))' const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('nested callExpression in unaryExpression', () => { const code = 'const myVar = -min(100, legLen(5, 3))' const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('with unaryExpression in callExpression', () => { const code = 'const myVar = min(5, -legLen(5, 4))' const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('with unaryExpression in sketch situation', () => { const code = [ 'const part001 = startSketchAt([0, 0])', '|> line([-2.21, -legLen(5, min(3, 999))], %)', ].join('\n') const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) }) describe('it recasts wrapped object expressions in pipe bodies with correct indentation', () => { it('with a single line', () => { const code = `const part001 = startSketchAt([-0.01, -0.08]) |> line({ to: [0.62, 4.15], tag: 'seg01' }, %) |> line([2.77, -1.24], %) |> angledLineThatIntersects({ angle: 201, offset: -1.35, intersectTag: 'seg01' }, %) |> line([-0.42, -1.72], %) show(part001)` const { ast } = code2ast(code) const recasted = recast(ast) expect(recasted).toBe(code) }) it('recasts wrapped object expressions NOT in pipe body correctly', () => { const code = `angledLineThatIntersects({ angle: 201, offset: -1.35, intersectTag: 'seg01' }, %)` const { ast } = 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', () => { const code = `const part001 = 1 - (def - abc)` const recasted = recast(code2ast(code).ast) expect(recasted).toBe(code) }) }) // helpers function code2ast(code: string): { ast: Program } { const ast = parser_wasm(code) return { ast } }