2022-11-26 08:34:23 +11:00
|
|
|
import fs from 'node:fs'
|
2022-11-14 14:04:23 +11:00
|
|
|
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
import {
|
|
|
|
parse,
|
|
|
|
ProgramMemory,
|
|
|
|
SketchGroup,
|
|
|
|
initPromise,
|
|
|
|
sketchGroupFromKclValue,
|
|
|
|
} from './wasm'
|
2023-07-10 15:15:07 +10:00
|
|
|
import { enginelessExecutor } from '../lib/testHelpers'
|
2023-08-24 15:34:51 -07:00
|
|
|
import { KCLError } from './errors'
|
2023-02-21 09:42:41 +11:00
|
|
|
|
2024-04-19 14:24:40 -07:00
|
|
|
beforeAll(async () => {
|
|
|
|
await initPromise
|
|
|
|
})
|
2022-11-14 14:04:23 +11:00
|
|
|
|
2023-06-22 16:43:33 +10:00
|
|
|
describe('test executor', () => {
|
|
|
|
it('test assigning two variables, the second summing with the first', async () => {
|
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`
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(5)
|
|
|
|
expect(mem.get('newVar')?.value).toBe(6)
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('test assigning a var with a string', async () => {
|
2022-11-26 08:34:23 +11:00
|
|
|
const code = `const myVar = "a str"`
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe('a str')
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('test assigning a var by cont concatenating two strings string execute', async () => {
|
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'
|
|
|
|
)
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe('a str another str')
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('fn funcN = () => {} execute', async () => {
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await 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')
|
|
|
|
)
|
2024-07-22 19:43:40 -04:00
|
|
|
expect(mem.get('theVar')?.value).toBe(60)
|
|
|
|
expect(mem.get('magicNum')?.value).toBe(69)
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('sketch declaration', async () => {
|
2023-10-05 14:27:48 -07:00
|
|
|
let code = `const mySketch = startSketchOn('XY')
|
|
|
|
|> startProfileAt([0,0], %)
|
2024-07-27 17:59:41 -07:00
|
|
|
|> lineTo([0,2], %, $myPath)
|
2023-02-12 10:56:45 +11:00
|
|
|
|> lineTo([2,3], %)
|
2024-07-27 17:59:41 -07:00
|
|
|
|> lineTo([5,-1], %, $rightPath)
|
2023-02-12 10:56:45 +11:00
|
|
|
// |> close(%)
|
2022-11-26 08:34:23 +11:00
|
|
|
`
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
// geo is three js buffer geometry and is very bloated to have in tests
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
const minusGeo = mem.get('mySketch')?.value?.value
|
2023-01-08 16:37:31 +11:00
|
|
|
expect(minusGeo).toEqual([
|
|
|
|
{
|
2024-02-11 12:59:00 +11:00
|
|
|
type: 'ToPoint',
|
2023-01-08 16:37:31 +11:00
|
|
|
to: [0, 2],
|
2023-02-12 10:56:45 +11:00
|
|
|
from: [0, 0],
|
2023-01-08 16:37:31 +11:00
|
|
|
__geoMeta: {
|
2024-07-27 17:59:41 -07:00
|
|
|
sourceRange: [72, 97],
|
2023-08-24 15:34:51 -07:00
|
|
|
id: expect.any(String),
|
2023-01-08 16:37:31 +11:00
|
|
|
},
|
2024-06-24 14:45:07 -07:00
|
|
|
tag: {
|
2024-07-27 17:59:41 -07:00
|
|
|
end: 96,
|
2024-06-24 14:45:07 -07:00
|
|
|
start: 89,
|
|
|
|
type: 'TagDeclarator',
|
|
|
|
value: 'myPath',
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2024-06-24 14:45:07 -07:00
|
|
|
},
|
2023-01-08 16:37:31 +11:00
|
|
|
},
|
2022-11-23 21:28:38 +11:00
|
|
|
{
|
2024-02-11 12:59:00 +11:00
|
|
|
type: 'ToPoint',
|
2023-01-08 16:37:31 +11:00
|
|
|
to: [2, 3],
|
|
|
|
from: [0, 2],
|
2024-06-24 14:45:07 -07:00
|
|
|
tag: null,
|
2023-01-08 16:37:31 +11:00
|
|
|
__geoMeta: {
|
2024-07-27 17:59:41 -07:00
|
|
|
sourceRange: [103, 119],
|
2023-08-24 15:34:51 -07:00
|
|
|
id: expect.any(String),
|
2023-01-08 16:37:31 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2024-02-11 12:59:00 +11:00
|
|
|
type: 'ToPoint',
|
2023-01-08 16:37:31 +11:00
|
|
|
to: [5, -1],
|
|
|
|
from: [2, 3],
|
|
|
|
__geoMeta: {
|
2024-07-27 17:59:41 -07:00
|
|
|
sourceRange: [125, 154],
|
2023-08-24 15:34:51 -07:00
|
|
|
id: expect.any(String),
|
2023-01-08 16:37:31 +11:00
|
|
|
},
|
2024-06-24 14:45:07 -07:00
|
|
|
tag: {
|
2024-07-27 17:59:41 -07:00
|
|
|
end: 153,
|
|
|
|
start: 143,
|
2024-06-24 14:45:07 -07:00
|
|
|
type: 'TagDeclarator',
|
|
|
|
value: 'rightPath',
|
2024-07-09 12:24:42 -04:00
|
|
|
digest: null,
|
2024-06-24 14:45:07 -07:00
|
|
|
},
|
2023-01-08 16:37:31 +11:00
|
|
|
},
|
2022-11-26 08:34:23 +11:00
|
|
|
])
|
|
|
|
})
|
2022-12-03 22:50:46 +11:00
|
|
|
|
2023-06-22 16:43:33 +10:00
|
|
|
it('pipe binary expression into call expression', async () => {
|
2022-12-03 22:50:46 +11:00
|
|
|
const code = [
|
|
|
|
'fn myFn = (a) => { return a + 1 }',
|
|
|
|
'const myVar = 5 + 1 |> myFn(%)',
|
|
|
|
].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(7)
|
2022-12-03 22:50:46 +11:00
|
|
|
})
|
|
|
|
|
2023-07-10 15:15:07 +10:00
|
|
|
// Enable rotations #152
|
|
|
|
// it('rotated sketch', async () => {
|
|
|
|
// const code = [
|
|
|
|
// 'const mySk1 = startSketchAt([0,0])',
|
|
|
|
// ' |> lineTo([1,1], %)',
|
2024-03-15 17:03:42 -04:00
|
|
|
// ' |> lineTo([0, 1], %, "myPath")',
|
2023-07-10 15:15:07 +10:00
|
|
|
// ' |> lineTo([1, 1], %)',
|
|
|
|
// 'const rotated = rx(90, mySk1)',
|
|
|
|
// ].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
// const mem = await exe(code)
|
|
|
|
// expect(mem.get('mySk1')?.value).toHaveLength(3)
|
|
|
|
// expect(mem.get('rotated')?.type).toBe('SketchGroup')
|
2023-07-10 15:15:07 +10:00
|
|
|
// if (
|
2024-07-22 19:43:40 -04:00
|
|
|
// mem.get('mySk1')?.type !== 'SketchGroup' ||
|
|
|
|
// mem.get('rotated')?.type !== 'SketchGroup'
|
2023-07-10 15:15:07 +10:00
|
|
|
// )
|
|
|
|
// throw new Error('not a sketch group')
|
2024-07-22 19:43:40 -04:00
|
|
|
// expect(mem.get('mySk1')?.rotation).toEqual([0, 0, 0, 1])
|
|
|
|
// expect(mem.get('rotated')?.rotation.map((a) => a.toFixed(4))).toEqual([
|
2023-07-10 15:15:07 +10:00
|
|
|
// '0.7071',
|
|
|
|
// '0.0000',
|
|
|
|
// '0.0000',
|
|
|
|
// '0.7071',
|
|
|
|
// ])
|
|
|
|
// })
|
2022-12-04 08:16:04 +11:00
|
|
|
|
2023-06-22 16:43:33 +10:00
|
|
|
it('execute pipe sketch into call expression', async () => {
|
2023-07-10 15:15:07 +10:00
|
|
|
// Enable rotations #152
|
2022-12-04 08:16:04 +11:00
|
|
|
const code = [
|
2023-10-05 14:27:48 -07:00
|
|
|
"const mySk1 = startSketchOn('XY')",
|
|
|
|
' |> startProfileAt([0,0], %)',
|
2023-02-12 10:56:45 +11:00
|
|
|
' |> lineTo([1,1], %)',
|
2024-07-27 17:59:41 -07:00
|
|
|
' |> lineTo([0, 1], %, $myPath)',
|
2023-02-12 10:56:45 +11:00
|
|
|
' |> lineTo([1,1], %)',
|
2023-07-10 15:15:07 +10:00
|
|
|
// ' |> rx(90, %)',
|
2022-12-04 08:16:04 +11:00
|
|
|
].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('mySk1')).toEqual({
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
type: 'UserVal',
|
|
|
|
value: {
|
|
|
|
type: 'SketchGroup',
|
|
|
|
on: expect.any(Object),
|
|
|
|
start: {
|
|
|
|
to: [0, 0],
|
2023-01-08 16:37:31 +11:00
|
|
|
from: [0, 0],
|
2024-06-24 14:45:07 -07:00
|
|
|
tag: null,
|
2023-01-08 16:37:31 +11:00
|
|
|
__geoMeta: {
|
2023-08-24 15:34:51 -07:00
|
|
|
id: expect.any(String),
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
sourceRange: [39, 63],
|
2023-01-08 16:37:31 +11:00
|
|
|
},
|
2022-12-04 08:16:04 +11:00
|
|
|
},
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
tags: {
|
|
|
|
myPath: {
|
|
|
|
__meta: [
|
|
|
|
{
|
|
|
|
sourceRange: [109, 116],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
type: 'TagIdentifier',
|
2024-06-24 14:45:07 -07:00
|
|
|
value: 'myPath',
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
info: expect.any(Object),
|
2024-06-24 14:45:07 -07:00
|
|
|
},
|
2022-12-04 08:16:04 +11:00
|
|
|
},
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
value: [
|
|
|
|
{
|
|
|
|
type: 'ToPoint',
|
|
|
|
to: [1, 1],
|
|
|
|
from: [0, 0],
|
|
|
|
tag: null,
|
|
|
|
__geoMeta: {
|
|
|
|
sourceRange: [69, 85],
|
|
|
|
id: expect.any(String),
|
|
|
|
},
|
2023-01-08 16:37:31 +11:00
|
|
|
},
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
{
|
|
|
|
type: 'ToPoint',
|
|
|
|
to: [0, 1],
|
|
|
|
from: [1, 1],
|
|
|
|
__geoMeta: {
|
|
|
|
sourceRange: [91, 117],
|
|
|
|
id: expect.any(String),
|
|
|
|
},
|
|
|
|
tag: {
|
|
|
|
end: 116,
|
|
|
|
start: 109,
|
|
|
|
type: 'TagDeclarator',
|
|
|
|
value: 'myPath',
|
|
|
|
digest: null,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'ToPoint',
|
|
|
|
to: [1, 1],
|
|
|
|
from: [0, 1],
|
|
|
|
tag: null,
|
|
|
|
__geoMeta: {
|
|
|
|
sourceRange: [123, 139],
|
|
|
|
id: expect.any(String),
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
id: expect.any(String),
|
|
|
|
__meta: [{ sourceRange: [39, 63] }],
|
|
|
|
},
|
2023-10-05 14:27:48 -07:00
|
|
|
__meta: [{ sourceRange: [39, 63] }],
|
2022-12-04 08:16:04 +11:00
|
|
|
})
|
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('execute array expression', async () => {
|
2022-12-30 21:53:50 +11:00
|
|
|
const code = ['const three = 3', "const yo = [1, '2', three, 4 + 5]"].join(
|
|
|
|
'\n'
|
|
|
|
)
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
2023-01-08 16:37:31 +11:00
|
|
|
// TODO path to node is probably wrong here, zero indexes are not correct
|
2024-07-22 19:43:40 -04:00
|
|
|
expect(mem.get('three')).toEqual({
|
|
|
|
type: 'UserVal',
|
|
|
|
value: 3,
|
|
|
|
__meta: [
|
|
|
|
{
|
|
|
|
sourceRange: [14, 15],
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
expect(mem.get('yo')).toEqual({
|
|
|
|
type: 'UserVal',
|
|
|
|
value: [1, '2', 3, 9],
|
|
|
|
__meta: [
|
|
|
|
{
|
|
|
|
sourceRange: [27, 49],
|
|
|
|
},
|
|
|
|
],
|
2022-12-30 21:53:50 +11:00
|
|
|
})
|
2024-07-22 19:43:40 -04:00
|
|
|
// Check that there are no other variables or environments.
|
|
|
|
expect(mem.numEnvironments()).toBe(1)
|
|
|
|
expect(mem.numVariables(0)).toBe(2)
|
2022-12-30 21:53:50 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('execute object expression', async () => {
|
2023-01-01 21:48:30 +11:00
|
|
|
const code = [
|
|
|
|
'const three = 3',
|
|
|
|
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
|
|
|
].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('yo')).toEqual({
|
2023-09-12 18:10:27 -07:00
|
|
|
type: 'UserVal',
|
2023-04-01 16:47:00 +11:00
|
|
|
value: { aStr: 'str', anum: 2, identifier: 3, binExp: 9 },
|
2023-01-08 16:37:31 +11:00
|
|
|
__meta: [
|
|
|
|
{
|
|
|
|
sourceRange: [27, 83],
|
|
|
|
},
|
|
|
|
],
|
2023-01-01 21:48:30 +11:00
|
|
|
})
|
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('execute memberExpression', async () => {
|
2023-01-03 19:41:27 +11:00
|
|
|
const code = ["const yo = {a: {b: '123'}}", "const myVar = yo.a['b']"].join(
|
|
|
|
'\n'
|
|
|
|
)
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')).toEqual({
|
2023-09-12 18:10:27 -07:00
|
|
|
type: 'UserVal',
|
2023-01-08 16:37:31 +11:00
|
|
|
value: '123',
|
|
|
|
__meta: [
|
|
|
|
{
|
|
|
|
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
|
|
|
|
2023-01-21 21:23:01 +11:00
|
|
|
describe('testing math operators', () => {
|
2023-07-20 19:25:04 -04:00
|
|
|
it('can sum', async () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = ['const myVar = 1 + 2'].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(3)
|
2023-01-21 21:23:01 +11:00
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
it('can subtract', async () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = ['const myVar = 1 - 2'].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(-1)
|
2023-01-21 21:23:01 +11:00
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
it('can multiply', async () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = ['const myVar = 1 * 2'].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(2)
|
2023-01-21 21:23:01 +11:00
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
it('can divide', async () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = ['const myVar = 1 / 2'].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(0.5)
|
2023-01-21 21:23:01 +11:00
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
it('can modulus', async () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = ['const myVar = 5 % 2'].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(1)
|
2023-01-21 21:23:01 +11:00
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
it('can do multiple operations', async () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = ['const myVar = 1 + 2 * 3'].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(7)
|
2023-01-21 21:23:01 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('big example with parans', async () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = ['const myVar = 1 + 2 * (3 - 4) / -5 + 6'].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(7.4)
|
2023-01-21 21:23:01 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('with identifier', async () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = ['const yo = 6', 'const myVar = yo / 2'].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(3)
|
2023-01-21 21:23:01 +11:00
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
it('with lots of testing', async () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = ['const myVar = 2 * ((2 + 3 ) / 4 + 5)'].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(12.5)
|
2023-01-21 21:23:01 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('with callExpression at start', async () => {
|
2023-03-02 21:19:11 +11:00
|
|
|
const code = 'const myVar = min(4, 100) + 2'
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(6)
|
2023-03-02 21:19:11 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('with callExpression at end', async () => {
|
2023-03-02 21:19:11 +11:00
|
|
|
const code = 'const myVar = 2 + min(4, 100)'
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(6)
|
2023-03-02 21:19:11 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('with nested callExpression', async () => {
|
2023-03-02 21:19:11 +11:00
|
|
|
const code = 'const myVar = 2 + min(100, legLen(5, 3))'
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(6)
|
2023-03-02 21:19:11 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('with unaryExpression', async () => {
|
2023-03-02 21:19:11 +11:00
|
|
|
const code = 'const myVar = -min(100, 3)'
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(-3)
|
2023-03-02 21:19:11 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('with unaryExpression in callExpression', async () => {
|
2023-03-02 21:19:11 +11:00
|
|
|
const code = 'const myVar = min(-legLen(5, 4), 5)'
|
|
|
|
const code2 = 'const myVar = min(5 , -legLen(5, 4))'
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
const mem2 = await exe(code2)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(-3)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(mem2.get('myVar')?.value)
|
2023-03-02 21:19:11 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('with unaryExpression in ArrayExpression', async () => {
|
2023-03-02 21:19:11 +11:00
|
|
|
const code = 'const myVar = [1,-legLen(5, 4)]'
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toEqual([1, -3])
|
2023-03-02 21:19:11 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('with unaryExpression in ArrayExpression in CallExpression, checking nothing funny happens when used in a sketch', async () => {
|
2023-03-02 21:19:11 +11:00
|
|
|
const code = [
|
2023-10-05 14:27:48 -07:00
|
|
|
"const part001 = startSketchOn('XY')",
|
|
|
|
' |> startProfileAt([0, 0], %)',
|
2023-03-02 21:19:11 +11:00
|
|
|
'|> line([-2.21, -legLen(5, min(3, 999))], %)',
|
|
|
|
].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
const sketch = sketchGroupFromKclValue(mem.get('part001'), 'part001')
|
2023-03-02 21:19:11 +11:00
|
|
|
// result of `-legLen(5, min(3, 999))` should be -4
|
2023-09-12 18:10:27 -07:00
|
|
|
const yVal = (sketch as SketchGroup).value?.[0]?.to?.[1]
|
2023-03-02 21:19:11 +11:00
|
|
|
expect(yVal).toBe(-4)
|
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('test that % substitution feeds down CallExp->ArrExp->UnaryExp->CallExp', async () => {
|
2023-03-02 21:19:11 +11:00
|
|
|
const code = [
|
|
|
|
`const myVar = 3`,
|
2023-10-05 14:27:48 -07:00
|
|
|
`const part001 = startSketchOn('XY')`,
|
|
|
|
` |> startProfileAt([0, 0], %)`,
|
2024-07-27 17:59:41 -07:00
|
|
|
` |> line([3, 4], %, $seg01)`,
|
2023-03-02 21:19:11 +11:00
|
|
|
` |> line([`,
|
2024-07-27 22:56:46 -07:00
|
|
|
` min(segLen(seg01), myVar),`,
|
|
|
|
` -legLen(segLen(seg01), myVar)`,
|
2023-03-02 21:19:11 +11:00
|
|
|
`], %)`,
|
|
|
|
``,
|
|
|
|
].join('\n')
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
const sketch = sketchGroupFromKclValue(mem.get('part001'), 'part001')
|
2024-07-27 22:56:46 -07:00
|
|
|
// expect -legLen(segLen('seg01'), myVar) to equal -4 setting the y value back to 0
|
2023-09-12 18:10:27 -07:00
|
|
|
expect((sketch as SketchGroup).value?.[1]?.from).toEqual([3, 4])
|
|
|
|
expect((sketch as SketchGroup).value?.[1]?.to).toEqual([6, 0])
|
2023-03-02 21:19:11 +11:00
|
|
|
const removedUnaryExp = code.replace(
|
2024-07-27 22:56:46 -07:00
|
|
|
`-legLen(segLen(seg01), myVar)`,
|
|
|
|
`legLen(segLen(seg01), myVar)`
|
2023-03-02 21:19:11 +11:00
|
|
|
)
|
2024-07-22 19:43:40 -04:00
|
|
|
const removedUnaryExpMem = await exe(removedUnaryExp)
|
Remove KclValue::SketchGroup variant (#3446)
We can store Rust types like `SketchGroup` as their own variant of `KclValue`, or as `KclValue::UserVal`. Sometimes we store in one and try to read from the other, which fails. This causes bugs, like #3338.
Instead, we should use either ::SketchGroup or ::UserVal, and stop using the other. If we stopped using ::UserVal, we'd need a new variant for every Rust type we wanted to build, including user-defined types. So I don't think that's practical.
Instead, we should store every KCL value by de/serializing it into UserVal. This is a first step along that path, removing just the SketchGroup variants. If it goes well, we can remove the other specialized variants too.
My only concern is there might be performance implications from how frequently we convert between serde_json::Value and Rust types via Serde. But I'm not too worried -- there's no parsing JSON strings, just traversing serde_json::Value trees. This isn't great for performance but I think it'll probably be miniscule in comparison to doing all the API calls.
2024-08-21 11:06:48 -05:00
|
|
|
const removedUnaryExpMemSketch = sketchGroupFromKclValue(
|
|
|
|
removedUnaryExpMem.get('part001'),
|
|
|
|
'part001'
|
|
|
|
)
|
2023-07-10 15:15:07 +10:00
|
|
|
|
2023-03-02 21:19:11 +11:00
|
|
|
// without the minus sign, the y value should be 8
|
2024-07-22 19:43:40 -04:00
|
|
|
expect((removedUnaryExpMemSketch as SketchGroup).value?.[1]?.to).toEqual([
|
2023-09-12 18:10:27 -07:00
|
|
|
6, 8,
|
|
|
|
])
|
2023-03-02 21:19:11 +11:00
|
|
|
})
|
2023-06-22 16:43:33 +10:00
|
|
|
it('with nested callExpression and binaryExpression', async () => {
|
2023-03-02 21:19:11 +11:00
|
|
|
const code = 'const myVar = 2 + min(100, -1 + legLen(5, 3))'
|
2024-07-22 19:43:40 -04:00
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myVar')?.value).toBe(5)
|
2023-03-02 21:19:11 +11:00
|
|
|
})
|
2024-09-19 19:31:41 +10:00
|
|
|
it('can do power of math', async () => {
|
|
|
|
const code = 'const myNeg2 = 4 ^ 2 - 3 ^ 2 * 2'
|
|
|
|
const mem = await exe(code)
|
|
|
|
expect(mem.get('myNeg2')?.value).toBe(-2)
|
|
|
|
})
|
2023-01-21 21:23:01 +11:00
|
|
|
})
|
|
|
|
|
2023-08-07 20:33:38 -05:00
|
|
|
describe('Testing Errors', () => {
|
|
|
|
it('should throw an error when a variable is not defined', async () => {
|
|
|
|
const code = `const myVar = 5
|
2023-10-05 14:27:48 -07:00
|
|
|
const theExtrude = startSketchOn('XY')
|
|
|
|
|> startProfileAt([0, 0], %)
|
2023-08-07 20:33:38 -05:00
|
|
|
|> line([-2.4, 5], %)
|
|
|
|
|> line([-0.76], myVarZ, %)
|
|
|
|
|> line([5,5], %)
|
|
|
|
|> close(%)
|
2024-03-01 17:16:18 -08:00
|
|
|
|> extrude(4, %)`
|
2023-08-07 20:33:38 -05:00
|
|
|
await expect(exe(code)).rejects.toEqual(
|
2023-08-24 15:34:51 -07:00
|
|
|
new KCLError(
|
|
|
|
'undefined_value',
|
|
|
|
'memory item key `myVarZ` is not defined',
|
2023-10-05 14:27:48 -07:00
|
|
|
[[129, 135]]
|
2023-08-24 15:34:51 -07:00
|
|
|
)
|
2023-08-07 20:33:38 -05:00
|
|
|
)
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2022-11-14 14:04:23 +11:00
|
|
|
// helpers
|
|
|
|
|
2023-09-12 18:10:27 -07:00
|
|
|
async function exe(
|
|
|
|
code: string,
|
2024-07-22 19:43:40 -04:00
|
|
|
programMemory: ProgramMemory = ProgramMemory.empty()
|
2023-09-12 18:10:27 -07:00
|
|
|
) {
|
2023-09-29 11:11:01 -07:00
|
|
|
const ast = parse(code)
|
2023-06-22 16:43:33 +10:00
|
|
|
|
2023-07-10 15:15:07 +10:00
|
|
|
const result = await enginelessExecutor(ast, programMemory)
|
2023-06-22 16:43:33 +10:00
|
|
|
return result
|
2022-11-20 09:41:21 +11:00
|
|
|
}
|