2022-11-26 08:34:23 +11:00
|
|
|
import fs from 'node:fs'
|
2022-11-14 14:04:23 +11:00
|
|
|
|
2022-11-26 08:34:23 +11:00
|
|
|
import { abstractSyntaxTree } from './abstractSyntaxTree'
|
|
|
|
import { lexer } from './tokeniser'
|
2023-01-08 16:37:31 +11:00
|
|
|
import { executor, ProgramMemory, Path, SketchGroup } from './executor'
|
2022-11-14 14:04:23 +11:00
|
|
|
|
2022-11-26 08:34:23 +11:00
|
|
|
describe('test', () => {
|
|
|
|
it('test assigning two variables, the second summing with the first', () => {
|
2022-11-14 14:04:23 +11:00
|
|
|
const code = `const myVar = 5
|
2022-11-26 08:34:23 +11:00
|
|
|
const newVar = myVar + 1`
|
|
|
|
const { root } = exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
expect(root.myVar.value).toBe(5)
|
|
|
|
expect(root.newVar.value).toBe(6)
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
|
|
|
it('test assigning a var with a string', () => {
|
|
|
|
const code = `const myVar = "a str"`
|
|
|
|
const { root } = exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
expect(root.myVar.value).toBe('a str')
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
2023-01-02 12:18:54 +11:00
|
|
|
it('test assigning a var by cont concatenating two strings string execute', () => {
|
2022-11-20 09:41:21 +11:00
|
|
|
const code = fs.readFileSync(
|
2022-11-26 08:34:23 +11:00
|
|
|
'./src/lang/testExamples/variableDeclaration.cado',
|
|
|
|
'utf-8'
|
|
|
|
)
|
|
|
|
const { root } = exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
expect(root.myVar.value).toBe('a str another str')
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
|
|
|
it('test with function call', () => {
|
2022-11-14 14:04:23 +11:00
|
|
|
const code = `
|
|
|
|
const myVar = "hello"
|
2022-11-26 08:34:23 +11:00
|
|
|
log(5, myVar)`
|
2023-01-08 16:37:31 +11:00
|
|
|
const programMemoryOverride: ProgramMemory['root'] = {
|
|
|
|
log: {
|
|
|
|
type: 'userVal',
|
|
|
|
value: jest.fn(),
|
|
|
|
__meta: [
|
|
|
|
{
|
|
|
|
sourceRange: [0, 0],
|
|
|
|
pathToNode: [],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
2022-11-26 08:34:23 +11:00
|
|
|
}
|
2022-11-21 08:57:44 +11:00
|
|
|
const { root } = executor(abstractSyntaxTree(lexer(code)), {
|
2022-11-20 09:41:21 +11:00
|
|
|
root: programMemoryOverride,
|
2022-11-21 08:57:44 +11:00
|
|
|
_sketch: [],
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
2023-01-08 16:37:31 +11:00
|
|
|
expect(root.myVar.value).toBe('hello')
|
|
|
|
expect(programMemoryOverride.log.value).toHaveBeenCalledWith(5, 'hello')
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
2023-01-02 12:18:54 +11:00
|
|
|
it('fn funcN = () => {} execute', () => {
|
2022-11-21 08:57:44 +11:00
|
|
|
const { root } = exe(
|
2022-11-20 09:41:21 +11:00
|
|
|
[
|
2022-11-26 08:34:23 +11:00
|
|
|
'fn funcN = (a, b) => {',
|
|
|
|
' return a + b',
|
|
|
|
'}',
|
|
|
|
'const theVar = 60',
|
|
|
|
'const magicNum = funcN(9, theVar)',
|
|
|
|
].join('\n')
|
|
|
|
)
|
2023-01-08 16:37:31 +11:00
|
|
|
expect(root.theVar.value).toBe(60)
|
|
|
|
expect(root.magicNum.value).toBe(69)
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
|
|
|
it('sketch declaration', () => {
|
2022-11-21 08:57:44 +11:00
|
|
|
let code = `sketch mySketch {
|
2023-01-08 16:37:31 +11:00
|
|
|
path myPath = lineTo(0,2)
|
|
|
|
lineTo(2,3)
|
|
|
|
path rightPath = lineTo(5,-1)
|
2022-11-21 08:57:44 +11:00
|
|
|
close()
|
|
|
|
}
|
2022-11-21 09:16:24 +11:00
|
|
|
show(mySketch)
|
2022-11-26 08:34:23 +11:00
|
|
|
`
|
|
|
|
const { root, return: _return } = exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
// geo is three js buffer geometry and is very bloated to have in tests
|
|
|
|
const minusGeo = removeGeoFromPaths(root.mySketch.value)
|
|
|
|
expect(minusGeo).toEqual([
|
|
|
|
{
|
|
|
|
type: 'toPoint',
|
|
|
|
to: [0, 2],
|
|
|
|
from: [5, -1],
|
|
|
|
__geoMeta: {
|
|
|
|
sourceRange: [25, 45],
|
|
|
|
pathToNode: [],
|
|
|
|
geos: ['line', 'lineEnd'],
|
|
|
|
},
|
|
|
|
name: 'myPath',
|
|
|
|
},
|
2022-11-23 21:28:38 +11:00
|
|
|
{
|
2023-01-08 16:37:31 +11:00
|
|
|
type: 'toPoint',
|
|
|
|
to: [2, 3],
|
|
|
|
from: [0, 2],
|
|
|
|
__geoMeta: {
|
|
|
|
sourceRange: [48, 59],
|
|
|
|
pathToNode: [],
|
|
|
|
geos: ['line', 'lineEnd'],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'toPoint',
|
|
|
|
to: [5, -1],
|
|
|
|
from: [2, 3],
|
|
|
|
__geoMeta: {
|
|
|
|
sourceRange: [67, 91],
|
|
|
|
pathToNode: [],
|
|
|
|
geos: ['line', 'lineEnd'],
|
|
|
|
},
|
|
|
|
name: 'rightPath',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'toPoint',
|
|
|
|
from: [5, -1],
|
|
|
|
to: [0, 2],
|
|
|
|
__geoMeta: {
|
|
|
|
sourceRange: [94, 101],
|
|
|
|
pathToNode: [],
|
|
|
|
geos: ['line', 'lineEnd'],
|
|
|
|
},
|
2022-11-23 21:28:38 +11:00
|
|
|
},
|
2022-11-26 08:34:23 +11:00
|
|
|
])
|
2022-12-30 14:09:07 +11:00
|
|
|
// expect(root.mySketch.sketch[0]).toEqual(root.mySketch.sketch[4].firstPath)
|
2022-11-21 09:16:24 +11:00
|
|
|
expect(_return).toEqual([
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2023-01-08 16:37:31 +11:00
|
|
|
start: 109,
|
|
|
|
end: 117,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'mySketch',
|
2022-11-21 09:16:24 +11:00
|
|
|
},
|
2022-11-26 08:34:23 +11:00
|
|
|
])
|
|
|
|
})
|
2022-12-03 22:50:46 +11:00
|
|
|
|
|
|
|
it('pipe binary expression into call expression', () => {
|
|
|
|
const code = [
|
|
|
|
'fn myFn = (a) => { return a + 1 }',
|
|
|
|
'const myVar = 5 + 1 |> myFn(%)',
|
|
|
|
].join('\n')
|
|
|
|
const { root } = exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
expect(root.myVar.value).toBe(7)
|
2022-12-03 22:50:46 +11:00
|
|
|
})
|
|
|
|
|
2022-12-04 08:16:04 +11:00
|
|
|
it('rotated sketch', () => {
|
|
|
|
const code = [
|
|
|
|
'sketch mySk1 {',
|
|
|
|
' lineTo(1,1)',
|
|
|
|
' path myPath = lineTo(0, 1)',
|
|
|
|
' lineTo(1,1)',
|
|
|
|
'}',
|
|
|
|
'const rotated = rx(90, mySk1)',
|
|
|
|
// 'show(mySk1)',
|
|
|
|
].join('\n')
|
|
|
|
const { root } = exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
expect(root.mySk1.value).toHaveLength(3)
|
|
|
|
expect(root?.rotated?.type).toBe('sketchGroup')
|
|
|
|
if (
|
|
|
|
root?.mySk1?.type !== 'sketchGroup' ||
|
|
|
|
root?.rotated?.type !== 'sketchGroup'
|
|
|
|
)
|
|
|
|
throw new Error('not a sketch group')
|
|
|
|
expect(root.mySk1.rotation).toEqual([0, 0, 0, 1])
|
|
|
|
expect(root.rotated.rotation.map((a) => a.toFixed(4))).toEqual([
|
|
|
|
'0.7071',
|
|
|
|
'0.0000',
|
|
|
|
'0.0000',
|
|
|
|
'0.7071',
|
|
|
|
])
|
2022-12-04 08:16:04 +11:00
|
|
|
})
|
|
|
|
|
|
|
|
it('execute pipe sketch into call expression', () => {
|
|
|
|
const code = [
|
|
|
|
'sketch mySk1 {',
|
|
|
|
' lineTo(1,1)',
|
|
|
|
' path myPath = lineTo(0, 1)',
|
|
|
|
' lineTo(1,1)',
|
|
|
|
'} |> rx(90, %)',
|
|
|
|
].join('\n')
|
|
|
|
const { root } = exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
const striptVersion = removeGeoFromSketch(root.mySk1 as SketchGroup)
|
2022-12-04 08:16:04 +11:00
|
|
|
expect(striptVersion).toEqual({
|
2023-01-08 16:37:31 +11:00
|
|
|
type: 'sketchGroup',
|
|
|
|
value: [
|
2022-12-04 08:16:04 +11:00
|
|
|
{
|
|
|
|
type: 'toPoint',
|
|
|
|
to: [1, 1],
|
2023-01-08 16:37:31 +11:00
|
|
|
from: [0, 0],
|
|
|
|
__geoMeta: {
|
|
|
|
sourceRange: [17, 28],
|
|
|
|
pathToNode: [],
|
|
|
|
geos: ['line', 'lineEnd'],
|
|
|
|
},
|
2022-12-04 08:16:04 +11:00
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'toPoint',
|
|
|
|
to: [0, 1],
|
2023-01-08 16:37:31 +11:00
|
|
|
from: [1, 1],
|
|
|
|
__geoMeta: {
|
|
|
|
sourceRange: [36, 57],
|
|
|
|
pathToNode: [],
|
|
|
|
geos: ['line', 'lineEnd'],
|
|
|
|
},
|
2022-12-04 08:16:04 +11:00
|
|
|
name: 'myPath',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'toPoint',
|
|
|
|
to: [1, 1],
|
2023-01-08 16:37:31 +11:00
|
|
|
from: [0, 1],
|
|
|
|
__geoMeta: {
|
|
|
|
sourceRange: [60, 71],
|
|
|
|
pathToNode: [],
|
|
|
|
geos: ['line', 'lineEnd'],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
position: [0, 0, 0],
|
|
|
|
rotation: [0.7071067811865475, 0, 0, 0.7071067811865476],
|
|
|
|
__meta: [
|
|
|
|
{
|
|
|
|
sourceRange: [13, 73],
|
|
|
|
pathToNode: ['body', 0, 'declarations', 0, 'init', 0],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
sourceRange: [77, 86],
|
|
|
|
pathToNode: [],
|
2022-12-04 08:16:04 +11:00
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
})
|
2022-12-30 21:53:50 +11:00
|
|
|
it('execute array expression', () => {
|
|
|
|
const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join(
|
|
|
|
'\n'
|
|
|
|
)
|
|
|
|
const { root } = exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
// TODO path to node is probably wrong here, zero indexes are not correct
|
2022-12-30 21:53:50 +11:00
|
|
|
expect(root).toEqual({
|
2023-01-08 16:37:31 +11:00
|
|
|
three: {
|
|
|
|
type: 'userVal',
|
|
|
|
value: 3,
|
|
|
|
__meta: [
|
|
|
|
{
|
|
|
|
pathToNode: ['body', 0, 'declarations', 0, 'init'],
|
|
|
|
sourceRange: [14, 15],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
yo: {
|
|
|
|
type: 'userVal',
|
|
|
|
value: [1, '2', 3, 9],
|
|
|
|
__meta: [
|
|
|
|
{
|
|
|
|
pathToNode: ['body', 1, 'declarations', 0, 'init'],
|
|
|
|
sourceRange: [27, 49],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
pathToNode: ['body', 0, 'declarations', 0, 'init'],
|
|
|
|
sourceRange: [14, 15],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
2022-12-30 21:53:50 +11:00
|
|
|
})
|
|
|
|
})
|
2023-01-01 21:48:30 +11:00
|
|
|
it('execute object expression', () => {
|
|
|
|
const code = [
|
|
|
|
'const three = 3',
|
|
|
|
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
|
|
|
].join('\n')
|
|
|
|
const { root } = exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
expect(root.yo).toEqual({
|
|
|
|
type: 'userVal',
|
|
|
|
value: {
|
2023-01-01 21:48:30 +11:00
|
|
|
aStr: 'str',
|
|
|
|
anum: 2,
|
|
|
|
identifier: 3,
|
|
|
|
binExp: 9,
|
|
|
|
},
|
2023-01-08 16:37:31 +11:00
|
|
|
__meta: [
|
|
|
|
{
|
|
|
|
pathToNode: ['body', 1, 'declarations', 0, 'init'],
|
|
|
|
sourceRange: [27, 83],
|
|
|
|
},
|
|
|
|
],
|
2023-01-01 21:48:30 +11:00
|
|
|
})
|
|
|
|
})
|
2023-01-03 19:41:27 +11:00
|
|
|
it('execute memberExpression', () => {
|
|
|
|
const code = ["const yo = {a: {b: '123'}}", "const myVar = yo.a['b']"].join(
|
|
|
|
'\n'
|
|
|
|
)
|
|
|
|
const { root } = exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
expect(root.myVar).toEqual({
|
|
|
|
type: 'userVal',
|
|
|
|
value: '123',
|
|
|
|
__meta: [
|
|
|
|
{
|
|
|
|
pathToNode: ['body', 1, 'declarations', 0, 'init'],
|
|
|
|
sourceRange: [41, 50],
|
|
|
|
},
|
|
|
|
],
|
2023-01-03 19:41:27 +11:00
|
|
|
})
|
|
|
|
})
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
2022-11-14 14:04:23 +11:00
|
|
|
|
|
|
|
// helpers
|
|
|
|
|
2022-11-20 09:41:21 +11:00
|
|
|
function exe(
|
|
|
|
code: string,
|
2022-11-21 08:57:44 +11:00
|
|
|
programMemory: ProgramMemory = { root: {}, _sketch: [] }
|
2022-11-20 09:41:21 +11:00
|
|
|
) {
|
2022-11-26 08:34:23 +11:00
|
|
|
const tokens = lexer(code)
|
|
|
|
const ast = abstractSyntaxTree(tokens)
|
|
|
|
return executor(ast, programMemory)
|
2022-11-20 09:41:21 +11:00
|
|
|
}
|
2022-12-04 08:16:04 +11:00
|
|
|
|
2023-01-08 16:37:31 +11:00
|
|
|
function removeGeoFromSketch(sketch: SketchGroup): any {
|
|
|
|
return {
|
|
|
|
...sketch,
|
|
|
|
value: removeGeoFromPaths(sketch.value),
|
2022-12-04 08:16:04 +11:00
|
|
|
}
|
2023-01-08 16:37:31 +11:00
|
|
|
}
|
|
|
|
|
|
|
|
function removeGeoFromPaths(paths: Path[]): any[] {
|
|
|
|
return paths.map((path: Path) => {
|
|
|
|
const newGeos = path?.__geoMeta?.geos.map((geo) => geo.type)
|
2022-12-30 14:09:07 +11:00
|
|
|
return {
|
2023-01-08 16:37:31 +11:00
|
|
|
...path,
|
|
|
|
__geoMeta: {
|
|
|
|
...path.__geoMeta,
|
|
|
|
geos: newGeos,
|
|
|
|
},
|
2022-12-30 14:09:07 +11:00
|
|
|
}
|
2023-01-08 16:37:31 +11:00
|
|
|
})
|
2022-12-04 08:16:04 +11:00
|
|
|
}
|