2023-09-13 07:23:14 -07:00
|
|
|
import { KCLError } from './errors'
|
2023-09-29 11:11:01 -07:00
|
|
|
import { initPromise, parse } from './wasm'
|
2023-02-21 09:42:41 +11:00
|
|
|
|
|
|
|
beforeAll(() => initPromise)
|
2022-11-13 11:14:30 +11:00
|
|
|
|
2022-11-26 08:34:23 +11:00
|
|
|
describe('testing AST', () => {
|
2023-07-20 19:25:04 -04:00
|
|
|
test('5 + 6', () => {
|
2023-09-29 11:11:01 -07:00
|
|
|
const result = parse('5 +6')
|
2023-02-01 07:30:55 +11:00
|
|
|
delete (result as any).nonCodeMeta
|
2023-08-18 19:37:52 +10:00
|
|
|
expect(result.body).toEqual([
|
|
|
|
{
|
|
|
|
type: 'ExpressionStatement',
|
|
|
|
start: 0,
|
|
|
|
end: 4,
|
|
|
|
expression: {
|
|
|
|
type: 'BinaryExpression',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 0,
|
|
|
|
end: 4,
|
2023-08-18 19:37:52 +10:00
|
|
|
left: {
|
|
|
|
type: 'Literal',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 0,
|
2023-08-18 19:37:52 +10:00
|
|
|
end: 1,
|
|
|
|
value: 5,
|
|
|
|
raw: '5',
|
|
|
|
},
|
|
|
|
operator: '+',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 3,
|
2022-11-13 11:14:30 +11:00
|
|
|
end: 4,
|
2023-08-18 19:37:52 +10:00
|
|
|
value: 6,
|
|
|
|
raw: '6',
|
2022-11-13 11:14:30 +11:00
|
|
|
},
|
|
|
|
},
|
2023-08-18 19:37:52 +10:00
|
|
|
},
|
|
|
|
])
|
2022-11-26 08:34:23 +11:00
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
test('const myVar = 5', () => {
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse('const myVar = 5')
|
2022-11-13 11:14:30 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclaration',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 0,
|
|
|
|
end: 15,
|
2022-11-26 08:34:23 +11:00
|
|
|
kind: 'const',
|
2022-11-13 11:14:30 +11:00
|
|
|
declarations: [
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclarator',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 6,
|
|
|
|
end: 15,
|
|
|
|
id: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 6,
|
|
|
|
end: 11,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'myVar',
|
2022-11-13 11:14:30 +11:00
|
|
|
},
|
|
|
|
init: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Literal',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 14,
|
|
|
|
end: 15,
|
|
|
|
value: 5,
|
2022-11-26 08:34:23 +11:00
|
|
|
raw: '5',
|
2022-11-13 11:14:30 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
2022-11-26 08:34:23 +11:00
|
|
|
])
|
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
test('multi-line', () => {
|
2022-11-13 11:14:30 +11:00
|
|
|
const code = `const myVar = 5
|
|
|
|
const newVar = myVar + 1
|
2022-11-26 08:34:23 +11:00
|
|
|
`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2022-11-13 11:14:30 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclaration',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 0,
|
|
|
|
end: 15,
|
2022-11-26 08:34:23 +11:00
|
|
|
kind: 'const',
|
2022-11-13 11:14:30 +11:00
|
|
|
declarations: [
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclarator',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 6,
|
|
|
|
end: 15,
|
|
|
|
id: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 6,
|
|
|
|
end: 11,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'myVar',
|
2022-11-13 11:14:30 +11:00
|
|
|
},
|
|
|
|
init: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Literal',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 14,
|
|
|
|
end: 15,
|
|
|
|
value: 5,
|
2022-11-26 08:34:23 +11:00
|
|
|
raw: '5',
|
2022-11-13 11:14:30 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclaration',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 16,
|
|
|
|
end: 40,
|
2022-11-26 08:34:23 +11:00
|
|
|
kind: 'const',
|
2022-11-13 11:14:30 +11:00
|
|
|
declarations: [
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclarator',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 22,
|
|
|
|
end: 40,
|
|
|
|
id: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 22,
|
|
|
|
end: 28,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'newVar',
|
2022-11-13 11:14:30 +11:00
|
|
|
},
|
|
|
|
init: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'BinaryExpression',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 31,
|
|
|
|
end: 40,
|
|
|
|
left: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 31,
|
|
|
|
end: 36,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'myVar',
|
2022-11-13 11:14:30 +11:00
|
|
|
},
|
2022-11-26 08:34:23 +11:00
|
|
|
operator: '+',
|
2022-11-13 11:14:30 +11:00
|
|
|
right: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Literal',
|
2022-11-13 11:14:30 +11:00
|
|
|
start: 39,
|
|
|
|
end: 40,
|
|
|
|
value: 1,
|
2022-11-26 08:34:23 +11:00
|
|
|
raw: '1',
|
2022-11-13 11:14:30 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
2022-11-26 08:34:23 +11:00
|
|
|
])
|
|
|
|
})
|
|
|
|
})
|
2022-11-17 20:17:00 +11:00
|
|
|
|
2022-11-26 08:34:23 +11:00
|
|
|
describe('testing function declaration', () => {
|
|
|
|
test('fn funcN = (a, b) => {return a + b}', () => {
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(
|
2022-11-26 08:34:23 +11:00
|
|
|
['fn funcN = (a, b) => {', ' return a + b', '}'].join('\n')
|
|
|
|
)
|
2023-02-01 07:30:55 +11:00
|
|
|
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
2022-11-18 08:20:18 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclaration',
|
2022-11-18 08:20:18 +11:00
|
|
|
start: 0,
|
|
|
|
end: 39,
|
2022-11-26 08:34:23 +11:00
|
|
|
kind: 'fn',
|
2022-11-18 08:20:18 +11:00
|
|
|
declarations: [
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclarator',
|
2022-11-18 08:20:18 +11:00
|
|
|
start: 3,
|
|
|
|
end: 39,
|
|
|
|
id: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-18 08:20:18 +11:00
|
|
|
start: 3,
|
|
|
|
end: 8,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'funcN',
|
2022-11-18 08:20:18 +11:00
|
|
|
},
|
|
|
|
init: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'FunctionExpression',
|
2022-11-18 08:20:18 +11:00
|
|
|
start: 11,
|
|
|
|
end: 39,
|
|
|
|
params: [
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-18 08:20:18 +11:00
|
|
|
start: 12,
|
|
|
|
end: 13,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'a',
|
2022-11-18 08:20:18 +11:00
|
|
|
},
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-18 08:20:18 +11:00
|
|
|
start: 15,
|
|
|
|
end: 16,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'b',
|
2022-11-18 08:20:18 +11:00
|
|
|
},
|
|
|
|
],
|
|
|
|
body: {
|
|
|
|
start: 21,
|
|
|
|
end: 39,
|
|
|
|
body: [
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'ReturnStatement',
|
2022-11-18 08:20:18 +11:00
|
|
|
start: 25,
|
|
|
|
end: 37,
|
|
|
|
argument: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'BinaryExpression',
|
2022-11-18 08:20:18 +11:00
|
|
|
start: 32,
|
|
|
|
end: 37,
|
|
|
|
left: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-18 08:20:18 +11:00
|
|
|
start: 32,
|
|
|
|
end: 33,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'a',
|
2022-11-18 08:20:18 +11:00
|
|
|
},
|
2022-11-26 08:34:23 +11:00
|
|
|
operator: '+',
|
2022-11-18 08:20:18 +11:00
|
|
|
right: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-18 08:20:18 +11:00
|
|
|
start: 36,
|
|
|
|
end: 37,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'b',
|
2022-11-18 08:20:18 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
2022-11-26 08:34:23 +11:00
|
|
|
])
|
|
|
|
})
|
|
|
|
test('call expression assignment', () => {
|
2023-08-18 19:37:52 +10:00
|
|
|
const code = `fn funcN = (a, b) => { return a + b }
|
2022-11-20 17:43:21 +11:00
|
|
|
const myVar = funcN(1, 2)`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-02-01 07:30:55 +11:00
|
|
|
delete (body[0] as any).declarations[0].init.body.nonCodeMeta
|
2022-11-20 09:41:21 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclaration',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 0,
|
|
|
|
end: 37,
|
2022-11-26 08:34:23 +11:00
|
|
|
kind: 'fn',
|
2022-11-20 17:43:21 +11:00
|
|
|
declarations: [
|
2022-11-20 09:41:21 +11:00
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclarator',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 3,
|
|
|
|
end: 37,
|
|
|
|
id: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 3,
|
|
|
|
end: 8,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'funcN',
|
2022-11-20 09:41:21 +11:00
|
|
|
},
|
2022-11-20 17:43:21 +11:00
|
|
|
init: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'FunctionExpression',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 11,
|
|
|
|
end: 37,
|
|
|
|
params: [
|
2022-11-20 09:41:21 +11:00
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 12,
|
|
|
|
end: 13,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'a',
|
2022-11-20 09:41:21 +11:00
|
|
|
},
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 15,
|
|
|
|
end: 16,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'b',
|
2022-11-20 17:43:21 +11:00
|
|
|
},
|
2022-11-20 09:41:21 +11:00
|
|
|
],
|
2022-11-20 17:43:21 +11:00
|
|
|
body: {
|
|
|
|
start: 21,
|
|
|
|
end: 37,
|
|
|
|
body: [
|
2022-11-20 09:41:21 +11:00
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'ReturnStatement',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 23,
|
|
|
|
end: 35,
|
|
|
|
argument: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'BinaryExpression',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 30,
|
|
|
|
end: 35,
|
|
|
|
left: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 30,
|
|
|
|
end: 31,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'a',
|
2022-11-20 09:41:21 +11:00
|
|
|
},
|
2022-11-26 08:34:23 +11:00
|
|
|
operator: '+',
|
2022-11-20 17:43:21 +11:00
|
|
|
right: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 34,
|
|
|
|
end: 35,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'b',
|
2022-11-20 17:43:21 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
2022-11-20 09:41:21 +11:00
|
|
|
},
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclaration',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 38,
|
|
|
|
end: 63,
|
2022-11-26 08:34:23 +11:00
|
|
|
kind: 'const',
|
2022-11-20 17:43:21 +11:00
|
|
|
declarations: [
|
2022-11-20 09:41:21 +11:00
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'VariableDeclarator',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 44,
|
|
|
|
end: 63,
|
|
|
|
id: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 44,
|
|
|
|
end: 49,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'myVar',
|
2022-11-20 09:41:21 +11:00
|
|
|
},
|
2022-11-20 17:43:21 +11:00
|
|
|
init: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'CallExpression',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 52,
|
|
|
|
end: 63,
|
|
|
|
callee: {
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Identifier',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 52,
|
|
|
|
end: 57,
|
2022-11-26 08:34:23 +11:00
|
|
|
name: 'funcN',
|
2022-11-20 09:41:21 +11:00
|
|
|
},
|
2022-11-20 17:43:21 +11:00
|
|
|
arguments: [
|
2022-11-20 09:41:21 +11:00
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Literal',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 58,
|
|
|
|
end: 59,
|
|
|
|
value: 1,
|
2022-11-26 08:34:23 +11:00
|
|
|
raw: '1',
|
2022-11-20 09:41:21 +11:00
|
|
|
},
|
|
|
|
{
|
2022-11-26 08:34:23 +11:00
|
|
|
type: 'Literal',
|
2022-11-20 17:43:21 +11:00
|
|
|
start: 61,
|
|
|
|
end: 62,
|
|
|
|
value: 2,
|
2022-11-26 08:34:23 +11:00
|
|
|
raw: '2',
|
2022-11-20 17:43:21 +11:00
|
|
|
},
|
2022-11-20 09:41:21 +11:00
|
|
|
],
|
2023-09-05 16:02:27 -07:00
|
|
|
function: {
|
|
|
|
type: 'InMemory',
|
|
|
|
},
|
2022-11-20 17:43:21 +11:00
|
|
|
optional: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
2022-11-26 08:34:23 +11:00
|
|
|
])
|
|
|
|
})
|
|
|
|
})
|
2022-11-20 17:43:21 +11:00
|
|
|
|
2022-12-03 22:50:46 +11:00
|
|
|
describe('testing pipe operator special', () => {
|
2022-12-02 21:00:57 +11:00
|
|
|
test('pipe operator with sketch', () => {
|
2023-02-12 10:56:45 +11:00
|
|
|
let code = `const mySketch = startSketchAt([0, 0])
|
|
|
|
|> lineTo([2, 3], %)
|
|
|
|
|> lineTo({ to: [0, 1], tag: "myPath" }, %)
|
|
|
|
|> lineTo([1, 1], %)
|
2023-08-18 19:37:52 +10:00
|
|
|
|> rx(45, %)
|
2022-12-02 21:00:57 +11:00
|
|
|
`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-02-01 07:30:55 +11:00
|
|
|
delete (body[0] as any).declarations[0].init.nonCodeMeta
|
2022-12-02 21:00:57 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
2023-02-12 10:56:45 +11:00
|
|
|
end: 145,
|
|
|
|
kind: 'const',
|
2022-12-03 22:50:46 +11:00
|
|
|
declarations: [
|
2022-12-02 21:00:57 +11:00
|
|
|
{
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'VariableDeclarator',
|
2023-02-12 10:56:45 +11:00
|
|
|
start: 6,
|
|
|
|
end: 145,
|
|
|
|
id: { type: 'Identifier', start: 6, end: 14, name: 'mySketch' },
|
2022-12-03 22:50:46 +11:00
|
|
|
init: {
|
|
|
|
type: 'PipeExpression',
|
2023-09-29 22:19:20 -04:00
|
|
|
start: 17,
|
2023-02-12 10:56:45 +11:00
|
|
|
end: 145,
|
2022-12-03 22:50:46 +11:00
|
|
|
body: [
|
2022-12-02 21:00:57 +11:00
|
|
|
{
|
2023-02-12 10:56:45 +11:00
|
|
|
type: 'CallExpression',
|
|
|
|
start: 17,
|
|
|
|
end: 38,
|
|
|
|
callee: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 17,
|
|
|
|
end: 30,
|
|
|
|
name: 'startSketchAt',
|
|
|
|
},
|
|
|
|
arguments: [
|
|
|
|
{
|
|
|
|
type: 'ArrayExpression',
|
|
|
|
start: 31,
|
|
|
|
end: 37,
|
|
|
|
elements: [
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 32,
|
|
|
|
end: 33,
|
|
|
|
value: 0,
|
|
|
|
raw: '0',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 35,
|
|
|
|
end: 36,
|
|
|
|
value: 0,
|
|
|
|
raw: '0',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
],
|
2023-09-05 16:02:27 -07:00
|
|
|
function: expect.any(Object),
|
2023-02-12 10:56:45 +11:00
|
|
|
optional: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'CallExpression',
|
|
|
|
start: 44,
|
|
|
|
end: 61,
|
|
|
|
callee: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 44,
|
|
|
|
end: 50,
|
|
|
|
name: 'lineTo',
|
|
|
|
},
|
|
|
|
arguments: [
|
|
|
|
{
|
|
|
|
type: 'ArrayExpression',
|
|
|
|
start: 51,
|
|
|
|
end: 57,
|
|
|
|
elements: [
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 52,
|
|
|
|
end: 53,
|
|
|
|
value: 2,
|
|
|
|
raw: '2',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 55,
|
|
|
|
end: 56,
|
|
|
|
value: 3,
|
|
|
|
raw: '3',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{ type: 'PipeSubstitution', start: 59, end: 60 },
|
|
|
|
],
|
2023-09-05 16:02:27 -07:00
|
|
|
function: expect.any(Object),
|
2023-02-12 10:56:45 +11:00
|
|
|
optional: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'CallExpression',
|
|
|
|
start: 67,
|
|
|
|
end: 107,
|
|
|
|
callee: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 67,
|
|
|
|
end: 73,
|
|
|
|
name: 'lineTo',
|
|
|
|
},
|
|
|
|
arguments: [
|
|
|
|
{
|
|
|
|
type: 'ObjectExpression',
|
|
|
|
start: 74,
|
|
|
|
end: 103,
|
|
|
|
properties: [
|
|
|
|
{
|
|
|
|
type: 'ObjectProperty',
|
|
|
|
start: 76,
|
|
|
|
end: 86,
|
|
|
|
key: {
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'Identifier',
|
2023-02-12 10:56:45 +11:00
|
|
|
start: 76,
|
|
|
|
end: 78,
|
|
|
|
name: 'to',
|
2022-12-03 22:50:46 +11:00
|
|
|
},
|
2023-02-12 10:56:45 +11:00
|
|
|
value: {
|
|
|
|
type: 'ArrayExpression',
|
|
|
|
start: 80,
|
|
|
|
end: 86,
|
|
|
|
elements: [
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 81,
|
|
|
|
end: 82,
|
|
|
|
value: 0,
|
|
|
|
raw: '0',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 84,
|
|
|
|
end: 85,
|
|
|
|
value: 1,
|
|
|
|
raw: '1',
|
2022-12-03 22:50:46 +11:00
|
|
|
},
|
2023-02-12 10:56:45 +11:00
|
|
|
],
|
2022-12-03 22:50:46 +11:00
|
|
|
},
|
2023-02-12 10:56:45 +11:00
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'ObjectProperty',
|
|
|
|
start: 88,
|
|
|
|
end: 101,
|
|
|
|
key: {
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'Identifier',
|
2023-02-12 10:56:45 +11:00
|
|
|
start: 88,
|
|
|
|
end: 91,
|
|
|
|
name: 'tag',
|
|
|
|
},
|
|
|
|
value: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 93,
|
|
|
|
end: 101,
|
|
|
|
value: 'myPath',
|
|
|
|
raw: '"myPath"',
|
2022-12-02 21:00:57 +11:00
|
|
|
},
|
2022-12-03 22:50:46 +11:00
|
|
|
},
|
2023-02-12 10:56:45 +11:00
|
|
|
],
|
|
|
|
},
|
|
|
|
{ type: 'PipeSubstitution', start: 105, end: 106 },
|
|
|
|
],
|
2023-09-05 16:02:27 -07:00
|
|
|
function: expect.any(Object),
|
2023-02-12 10:56:45 +11:00
|
|
|
optional: false,
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'CallExpression',
|
|
|
|
start: 113,
|
|
|
|
end: 130,
|
|
|
|
callee: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 113,
|
|
|
|
end: 119,
|
|
|
|
name: 'lineTo',
|
2022-12-03 22:50:46 +11:00
|
|
|
},
|
2023-02-12 10:56:45 +11:00
|
|
|
arguments: [
|
|
|
|
{
|
|
|
|
type: 'ArrayExpression',
|
|
|
|
start: 120,
|
|
|
|
end: 126,
|
|
|
|
elements: [
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 121,
|
|
|
|
end: 122,
|
|
|
|
value: 1,
|
|
|
|
raw: '1',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 124,
|
|
|
|
end: 125,
|
|
|
|
value: 1,
|
|
|
|
raw: '1',
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{ type: 'PipeSubstitution', start: 128, end: 129 },
|
|
|
|
],
|
2023-09-05 16:02:27 -07:00
|
|
|
function: expect.any(Object),
|
2023-02-12 10:56:45 +11:00
|
|
|
optional: false,
|
2022-12-02 21:00:57 +11:00
|
|
|
},
|
|
|
|
{
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'CallExpression',
|
2023-02-12 10:56:45 +11:00
|
|
|
start: 136,
|
|
|
|
end: 145,
|
2022-12-03 22:50:46 +11:00
|
|
|
callee: {
|
|
|
|
type: 'Identifier',
|
2023-02-12 10:56:45 +11:00
|
|
|
start: 136,
|
|
|
|
end: 138,
|
2022-12-03 22:50:46 +11:00
|
|
|
name: 'rx',
|
2022-12-02 21:00:57 +11:00
|
|
|
},
|
2022-12-03 22:50:46 +11:00
|
|
|
arguments: [
|
2022-12-02 21:00:57 +11:00
|
|
|
{
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'Literal',
|
2023-02-12 10:56:45 +11:00
|
|
|
start: 139,
|
|
|
|
end: 141,
|
2022-12-03 22:50:46 +11:00
|
|
|
value: 45,
|
|
|
|
raw: '45',
|
2022-12-02 21:00:57 +11:00
|
|
|
},
|
2023-02-12 10:56:45 +11:00
|
|
|
{ type: 'PipeSubstitution', start: 143, end: 144 },
|
2022-12-02 21:00:57 +11:00
|
|
|
],
|
2023-09-05 16:02:27 -07:00
|
|
|
function: {
|
|
|
|
type: 'InMemory',
|
|
|
|
},
|
2022-12-03 22:50:46 +11:00
|
|
|
optional: false,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
2022-12-02 21:00:57 +11:00
|
|
|
])
|
|
|
|
})
|
|
|
|
test('pipe operator with binary expression', () => {
|
|
|
|
let code = `const myVar = 5 + 6 |> myFunc(45, %)`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-02-01 07:30:55 +11:00
|
|
|
delete (body as any)[0].declarations[0].init.nonCodeMeta
|
2022-12-02 21:00:57 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
|
|
|
end: 36,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
2022-12-02 21:00:57 +11:00
|
|
|
{
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 6,
|
|
|
|
end: 36,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 6,
|
|
|
|
end: 11,
|
|
|
|
name: 'myVar',
|
2022-12-02 21:00:57 +11:00
|
|
|
},
|
2022-12-03 22:50:46 +11:00
|
|
|
init: {
|
|
|
|
type: 'PipeExpression',
|
2023-09-29 22:19:20 -04:00
|
|
|
start: 14,
|
2022-12-03 22:50:46 +11:00
|
|
|
end: 36,
|
|
|
|
body: [
|
2022-12-02 21:00:57 +11:00
|
|
|
{
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 14,
|
|
|
|
end: 19,
|
|
|
|
left: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 14,
|
|
|
|
end: 15,
|
|
|
|
value: 5,
|
|
|
|
raw: '5',
|
|
|
|
},
|
|
|
|
operator: '+',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 18,
|
|
|
|
end: 19,
|
|
|
|
value: 6,
|
|
|
|
raw: '6',
|
2022-12-02 21:00:57 +11:00
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'CallExpression',
|
|
|
|
start: 23,
|
|
|
|
end: 36,
|
|
|
|
callee: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 23,
|
|
|
|
end: 29,
|
|
|
|
name: 'myFunc',
|
2022-12-02 21:00:57 +11:00
|
|
|
},
|
2022-12-03 22:50:46 +11:00
|
|
|
arguments: [
|
2022-12-02 21:00:57 +11:00
|
|
|
{
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'Literal',
|
|
|
|
start: 30,
|
|
|
|
end: 32,
|
|
|
|
value: 45,
|
|
|
|
raw: '45',
|
2022-12-02 21:00:57 +11:00
|
|
|
},
|
|
|
|
{
|
2022-12-03 22:50:46 +11:00
|
|
|
type: 'PipeSubstitution',
|
|
|
|
start: 34,
|
|
|
|
end: 35,
|
|
|
|
},
|
2022-12-02 21:00:57 +11:00
|
|
|
],
|
2023-09-05 16:02:27 -07:00
|
|
|
function: {
|
|
|
|
type: 'InMemory',
|
|
|
|
},
|
2022-12-03 22:50:46 +11:00
|
|
|
optional: false,
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
2022-12-02 21:00:57 +11:00
|
|
|
])
|
|
|
|
})
|
2022-12-30 21:53:50 +11:00
|
|
|
test('array expression', () => {
|
|
|
|
let code = `const yo = [1, '2', three, 4 + 5]`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2022-12-30 21:53:50 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
|
|
|
end: 33,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 6,
|
|
|
|
end: 33,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 6,
|
|
|
|
end: 8,
|
|
|
|
name: 'yo',
|
|
|
|
},
|
|
|
|
init: {
|
|
|
|
type: 'ArrayExpression',
|
|
|
|
start: 11,
|
|
|
|
end: 33,
|
|
|
|
elements: [
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 12,
|
|
|
|
end: 13,
|
|
|
|
value: 1,
|
|
|
|
raw: '1',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 15,
|
|
|
|
end: 18,
|
|
|
|
value: '2',
|
|
|
|
raw: "'2'",
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 20,
|
|
|
|
end: 25,
|
|
|
|
name: 'three',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 27,
|
|
|
|
end: 32,
|
|
|
|
left: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 27,
|
|
|
|
end: 28,
|
|
|
|
value: 4,
|
|
|
|
raw: '4',
|
|
|
|
},
|
|
|
|
operator: '+',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 31,
|
|
|
|
end: 32,
|
|
|
|
value: 5,
|
|
|
|
raw: '5',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
])
|
|
|
|
})
|
2023-01-01 21:48:30 +11:00
|
|
|
test('object expression ast', () => {
|
|
|
|
const code = [
|
|
|
|
'const three = 3',
|
|
|
|
"const yo = {aStr: 'str', anum: 2, identifier: three, binExp: 4 + 5}",
|
|
|
|
].join('\n')
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-01 21:48:30 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
|
|
|
end: 15,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 6,
|
|
|
|
end: 15,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 6,
|
|
|
|
end: 11,
|
|
|
|
name: 'three',
|
|
|
|
},
|
|
|
|
init: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 14,
|
|
|
|
end: 15,
|
|
|
|
value: 3,
|
|
|
|
raw: '3',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 16,
|
|
|
|
end: 83,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 22,
|
|
|
|
end: 83,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 22,
|
|
|
|
end: 24,
|
|
|
|
name: 'yo',
|
|
|
|
},
|
|
|
|
init: {
|
|
|
|
type: 'ObjectExpression',
|
|
|
|
start: 27,
|
|
|
|
end: 83,
|
|
|
|
properties: [
|
|
|
|
{
|
|
|
|
type: 'ObjectProperty',
|
|
|
|
start: 28,
|
|
|
|
end: 39,
|
|
|
|
key: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 28,
|
|
|
|
end: 32,
|
|
|
|
name: 'aStr',
|
|
|
|
},
|
|
|
|
value: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 34,
|
|
|
|
end: 39,
|
|
|
|
value: 'str',
|
|
|
|
raw: "'str'",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'ObjectProperty',
|
|
|
|
start: 41,
|
|
|
|
end: 48,
|
|
|
|
key: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 41,
|
|
|
|
end: 45,
|
|
|
|
name: 'anum',
|
|
|
|
},
|
|
|
|
value: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 47,
|
|
|
|
end: 48,
|
|
|
|
value: 2,
|
|
|
|
raw: '2',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'ObjectProperty',
|
|
|
|
start: 50,
|
|
|
|
end: 67,
|
|
|
|
key: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 50,
|
|
|
|
end: 60,
|
|
|
|
name: 'identifier',
|
|
|
|
},
|
|
|
|
value: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 62,
|
|
|
|
end: 67,
|
|
|
|
name: 'three',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'ObjectProperty',
|
|
|
|
start: 69,
|
|
|
|
end: 82,
|
|
|
|
key: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 69,
|
|
|
|
end: 75,
|
|
|
|
name: 'binExp',
|
|
|
|
},
|
|
|
|
value: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 77,
|
|
|
|
end: 82,
|
|
|
|
left: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 77,
|
|
|
|
end: 78,
|
|
|
|
value: 4,
|
|
|
|
raw: '4',
|
|
|
|
},
|
|
|
|
operator: '+',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 81,
|
|
|
|
end: 82,
|
|
|
|
value: 5,
|
|
|
|
raw: '5',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
])
|
|
|
|
})
|
2023-01-02 12:18:54 +11:00
|
|
|
test('nested object expression ast', () => {
|
|
|
|
const code = `const yo = {key: {
|
|
|
|
key2: 'value'
|
|
|
|
}}`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-02 12:18:54 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
|
|
|
end: 37,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 6,
|
|
|
|
end: 37,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 6,
|
|
|
|
end: 8,
|
|
|
|
name: 'yo',
|
|
|
|
},
|
|
|
|
init: {
|
|
|
|
type: 'ObjectExpression',
|
|
|
|
start: 11,
|
|
|
|
end: 37,
|
|
|
|
properties: [
|
|
|
|
{
|
|
|
|
type: 'ObjectProperty',
|
|
|
|
start: 12,
|
|
|
|
end: 36,
|
|
|
|
key: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 12,
|
|
|
|
end: 15,
|
|
|
|
name: 'key',
|
|
|
|
},
|
|
|
|
value: {
|
|
|
|
type: 'ObjectExpression',
|
|
|
|
start: 17,
|
|
|
|
end: 36,
|
|
|
|
properties: [
|
|
|
|
{
|
|
|
|
type: 'ObjectProperty',
|
|
|
|
start: 21,
|
|
|
|
end: 34,
|
|
|
|
key: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 21,
|
|
|
|
end: 25,
|
|
|
|
name: 'key2',
|
|
|
|
},
|
|
|
|
value: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 27,
|
|
|
|
end: 34,
|
|
|
|
value: 'value',
|
|
|
|
raw: "'value'",
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
])
|
|
|
|
})
|
|
|
|
test('object expression with array ast', () => {
|
|
|
|
const code = `const yo = {key: [1, '2']}`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-02 12:18:54 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
|
|
|
end: 26,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 6,
|
|
|
|
end: 26,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 6,
|
|
|
|
end: 8,
|
|
|
|
name: 'yo',
|
|
|
|
},
|
|
|
|
init: {
|
|
|
|
type: 'ObjectExpression',
|
|
|
|
start: 11,
|
|
|
|
end: 26,
|
|
|
|
properties: [
|
|
|
|
{
|
|
|
|
type: 'ObjectProperty',
|
|
|
|
start: 12,
|
|
|
|
end: 25,
|
|
|
|
key: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 12,
|
|
|
|
end: 15,
|
|
|
|
name: 'key',
|
|
|
|
},
|
|
|
|
value: {
|
|
|
|
type: 'ArrayExpression',
|
|
|
|
start: 17,
|
|
|
|
end: 25,
|
|
|
|
elements: [
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 18,
|
|
|
|
end: 19,
|
|
|
|
value: 1,
|
|
|
|
raw: '1',
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 21,
|
|
|
|
end: 24,
|
|
|
|
value: '2',
|
|
|
|
raw: "'2'",
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
])
|
|
|
|
})
|
2023-01-03 19:41:27 +11:00
|
|
|
test('object memberExpression simple', () => {
|
|
|
|
const code = `const prop = yo.one.two`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-03 19:41:27 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
|
|
|
end: 23,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 6,
|
|
|
|
end: 23,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 6,
|
|
|
|
end: 10,
|
|
|
|
name: 'prop',
|
|
|
|
},
|
|
|
|
init: {
|
|
|
|
type: 'MemberExpression',
|
|
|
|
start: 13,
|
|
|
|
end: 23,
|
|
|
|
computed: false,
|
|
|
|
object: {
|
|
|
|
type: 'MemberExpression',
|
|
|
|
start: 13,
|
|
|
|
end: 19,
|
|
|
|
computed: false,
|
|
|
|
object: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 13,
|
|
|
|
end: 15,
|
|
|
|
name: 'yo',
|
|
|
|
},
|
|
|
|
property: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 16,
|
|
|
|
end: 19,
|
|
|
|
name: 'one',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
property: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 20,
|
|
|
|
end: 23,
|
|
|
|
name: 'two',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
])
|
|
|
|
})
|
|
|
|
test('object memberExpression with square braces', () => {
|
|
|
|
const code = `const prop = yo.one["two"]`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-03 19:41:27 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
|
|
|
end: 26,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 6,
|
|
|
|
end: 26,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 6,
|
|
|
|
end: 10,
|
|
|
|
name: 'prop',
|
|
|
|
},
|
|
|
|
init: {
|
|
|
|
type: 'MemberExpression',
|
|
|
|
start: 13,
|
|
|
|
end: 26,
|
|
|
|
computed: false,
|
|
|
|
object: {
|
|
|
|
type: 'MemberExpression',
|
|
|
|
start: 13,
|
|
|
|
end: 19,
|
|
|
|
computed: false,
|
|
|
|
object: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 13,
|
|
|
|
end: 15,
|
|
|
|
name: 'yo',
|
|
|
|
},
|
|
|
|
property: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 16,
|
|
|
|
end: 19,
|
|
|
|
name: 'one',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
property: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 20,
|
|
|
|
end: 25,
|
|
|
|
value: 'two',
|
|
|
|
raw: '"two"',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
])
|
|
|
|
})
|
|
|
|
test('object memberExpression with two square braces literal and identifier', () => {
|
|
|
|
const code = `const prop = yo["one"][two]`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-03 19:41:27 +11:00
|
|
|
expect(body).toEqual([
|
|
|
|
{
|
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
|
|
|
end: 27,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 6,
|
|
|
|
end: 27,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 6,
|
|
|
|
end: 10,
|
|
|
|
name: 'prop',
|
|
|
|
},
|
|
|
|
init: {
|
|
|
|
type: 'MemberExpression',
|
|
|
|
start: 13,
|
|
|
|
end: 27,
|
|
|
|
computed: true,
|
|
|
|
object: {
|
|
|
|
type: 'MemberExpression',
|
|
|
|
start: 13,
|
|
|
|
end: 22,
|
|
|
|
computed: false,
|
|
|
|
object: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 13,
|
|
|
|
end: 15,
|
|
|
|
name: 'yo',
|
|
|
|
},
|
|
|
|
property: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 16,
|
|
|
|
end: 21,
|
|
|
|
value: 'one',
|
|
|
|
raw: '"one"',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
property: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 23,
|
|
|
|
end: 26,
|
|
|
|
name: 'two',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
},
|
|
|
|
])
|
|
|
|
})
|
2022-12-03 22:50:46 +11:00
|
|
|
})
|
2023-01-21 21:23:01 +11:00
|
|
|
|
|
|
|
describe('nests binary expressions correctly', () => {
|
2023-07-20 19:25:04 -04:00
|
|
|
it('works with the simple case', () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = `const yo = 1 + 2`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-21 21:23:01 +11:00
|
|
|
expect(body[0]).toEqual({
|
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
|
|
|
end: 16,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 6,
|
|
|
|
end: 16,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 6,
|
|
|
|
end: 8,
|
|
|
|
name: 'yo',
|
|
|
|
},
|
|
|
|
init: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 11,
|
|
|
|
end: 16,
|
|
|
|
left: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 11,
|
|
|
|
end: 12,
|
|
|
|
value: 1,
|
|
|
|
raw: '1',
|
|
|
|
},
|
|
|
|
operator: '+',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 15,
|
|
|
|
end: 16,
|
|
|
|
value: 2,
|
|
|
|
raw: '2',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
it('should nest according to precedence with multiply first', () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
// should be binExp { binExp { lit-1 * lit-2 } + lit}
|
|
|
|
const code = `const yo = 1 * 2 + 3`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-21 21:23:01 +11:00
|
|
|
expect(body[0]).toEqual({
|
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
|
|
|
end: 20,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 6,
|
|
|
|
end: 20,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 6,
|
|
|
|
end: 8,
|
|
|
|
name: 'yo',
|
|
|
|
},
|
|
|
|
init: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 11,
|
|
|
|
end: 20,
|
|
|
|
left: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 11,
|
|
|
|
end: 16,
|
|
|
|
left: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 11,
|
|
|
|
end: 12,
|
|
|
|
value: 1,
|
|
|
|
raw: '1',
|
|
|
|
},
|
|
|
|
operator: '*',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 15,
|
|
|
|
end: 16,
|
|
|
|
value: 2,
|
|
|
|
raw: '2',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
operator: '+',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 19,
|
|
|
|
end: 20,
|
|
|
|
value: 3,
|
|
|
|
raw: '3',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
it('should nest according to precedence with sum first', () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
// should be binExp { lit-1 + binExp { lit-2 * lit-3 } }
|
|
|
|
const code = `const yo = 1 + 2 * 3`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-21 21:23:01 +11:00
|
|
|
expect(body[0]).toEqual({
|
|
|
|
type: 'VariableDeclaration',
|
|
|
|
start: 0,
|
|
|
|
end: 20,
|
|
|
|
kind: 'const',
|
|
|
|
declarations: [
|
|
|
|
{
|
|
|
|
type: 'VariableDeclarator',
|
|
|
|
start: 6,
|
|
|
|
end: 20,
|
|
|
|
id: {
|
|
|
|
type: 'Identifier',
|
|
|
|
start: 6,
|
|
|
|
end: 8,
|
|
|
|
name: 'yo',
|
|
|
|
},
|
|
|
|
init: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 11,
|
|
|
|
end: 20,
|
|
|
|
left: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 11,
|
|
|
|
end: 12,
|
|
|
|
value: 1,
|
|
|
|
raw: '1',
|
|
|
|
},
|
|
|
|
operator: '+',
|
|
|
|
right: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 15,
|
|
|
|
end: 20,
|
|
|
|
left: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 15,
|
|
|
|
end: 16,
|
|
|
|
value: 2,
|
|
|
|
raw: '2',
|
|
|
|
},
|
|
|
|
operator: '*',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 19,
|
|
|
|
end: 20,
|
|
|
|
value: 3,
|
|
|
|
raw: '3',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
|
|
|
})
|
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
it('should nest properly with two opperators of equal precedence', () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = `const yo = 1 + 2 - 3`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-21 21:23:01 +11:00
|
|
|
expect((body[0] as any).declarations[0].init).toEqual({
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 11,
|
|
|
|
end: 20,
|
|
|
|
left: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 11,
|
|
|
|
end: 16,
|
|
|
|
left: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 11,
|
|
|
|
end: 12,
|
|
|
|
value: 1,
|
|
|
|
raw: '1',
|
|
|
|
},
|
|
|
|
operator: '+',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 15,
|
|
|
|
end: 16,
|
|
|
|
value: 2,
|
|
|
|
raw: '2',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
operator: '-',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 19,
|
|
|
|
end: 20,
|
|
|
|
value: 3,
|
|
|
|
raw: '3',
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
it('should nest properly with two opperators of equal (but higher) precedence', () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = `const yo = 1 * 2 / 3`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-21 21:23:01 +11:00
|
|
|
expect((body[0] as any).declarations[0].init).toEqual({
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 11,
|
|
|
|
end: 20,
|
|
|
|
left: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
start: 11,
|
|
|
|
end: 16,
|
|
|
|
left: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 11,
|
|
|
|
end: 12,
|
|
|
|
value: 1,
|
|
|
|
raw: '1',
|
|
|
|
},
|
|
|
|
operator: '*',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 15,
|
|
|
|
end: 16,
|
|
|
|
value: 2,
|
|
|
|
raw: '2',
|
|
|
|
},
|
|
|
|
},
|
|
|
|
operator: '/',
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
start: 19,
|
|
|
|
end: 20,
|
|
|
|
value: 3,
|
|
|
|
raw: '3',
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
2023-07-20 19:25:04 -04:00
|
|
|
it('should nest properly with longer example', () => {
|
2023-01-21 21:23:01 +11:00
|
|
|
const code = `const yo = 1 + 2 * (3 - 4) / 5 + 6`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-01-21 21:23:01 +11:00
|
|
|
const init = (body[0] as any).declarations[0].init
|
|
|
|
expect(init).toEqual({
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
operator: '+',
|
|
|
|
start: 11,
|
|
|
|
end: 34,
|
|
|
|
left: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
operator: '+',
|
|
|
|
start: 11,
|
|
|
|
end: 30,
|
|
|
|
left: { type: 'Literal', value: 1, raw: '1', start: 11, end: 12 },
|
|
|
|
right: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
operator: '/',
|
|
|
|
start: 15,
|
|
|
|
end: 30,
|
|
|
|
left: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
operator: '*',
|
|
|
|
start: 15,
|
|
|
|
end: 26,
|
|
|
|
left: { type: 'Literal', value: 2, raw: '2', start: 15, end: 16 },
|
|
|
|
right: {
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
operator: '-',
|
|
|
|
start: 20,
|
|
|
|
end: 25,
|
|
|
|
left: { type: 'Literal', value: 3, raw: '3', start: 20, end: 21 },
|
|
|
|
right: {
|
|
|
|
type: 'Literal',
|
|
|
|
value: 4,
|
|
|
|
raw: '4',
|
|
|
|
start: 24,
|
|
|
|
end: 25,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
right: { type: 'Literal', value: 5, raw: '5', start: 29, end: 30 },
|
|
|
|
},
|
|
|
|
},
|
|
|
|
right: { type: 'Literal', value: 6, raw: '6', start: 33, end: 34 },
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-02-01 07:30:55 +11:00
|
|
|
describe('check nonCodeMeta data is attached to the AST correctly', () => {
|
|
|
|
it('comments between expressions', () => {
|
|
|
|
const code = `
|
|
|
|
const yo = { a: { b: { c: '123' } } }
|
|
|
|
// this is a comment
|
|
|
|
const key = 'c'`
|
|
|
|
const nonCodeMetaInstance = {
|
2023-09-18 17:14:12 -06:00
|
|
|
type: 'NonCodeNode',
|
2023-02-01 07:30:55 +11:00
|
|
|
start: code.indexOf('\n// this is a comment'),
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
end: code.indexOf('const key') - 1,
|
2023-09-05 16:02:27 -07:00
|
|
|
value: {
|
2023-09-06 10:36:03 -07:00
|
|
|
type: 'blockComment',
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
style: 'line',
|
2023-09-05 16:02:27 -07:00
|
|
|
value: 'this is a comment',
|
|
|
|
},
|
2023-02-01 07:30:55 +11:00
|
|
|
}
|
2023-09-29 11:11:01 -07:00
|
|
|
const { nonCodeMeta } = parse(code)
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
expect(nonCodeMeta.nonCodeNodes[0][0]).toEqual(nonCodeMetaInstance)
|
2023-02-01 07:30:55 +11:00
|
|
|
|
|
|
|
// 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
|
2023-09-29 11:11:01 -07:00
|
|
|
const { nonCodeMeta: nonCodeMeta2 } = parse(codeWithExtraStartWhitespace)
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
expect(nonCodeMeta2.nonCodeNodes[0][0].value).toStrictEqual(
|
2023-09-05 16:02:27 -07:00
|
|
|
nonCodeMetaInstance.value
|
|
|
|
)
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
expect(nonCodeMeta2.nonCodeNodes[0][0].start).not.toBe(
|
2023-07-20 12:38:05 +10:00
|
|
|
nonCodeMetaInstance.start
|
|
|
|
)
|
2023-02-01 07:30:55 +11:00
|
|
|
})
|
|
|
|
it('comments nested within a block statement', () => {
|
2023-02-12 10:56:45 +11:00
|
|
|
const code = `const mySketch = startSketchAt([0,0])
|
|
|
|
|> lineTo({ to: [0, 1], tag: 'myPath' }, %)
|
2023-08-24 15:34:51 -07:00
|
|
|
|> lineTo([1, 1], %) /* this is
|
|
|
|
a comment
|
2023-02-01 07:30:55 +11:00
|
|
|
spanning a few lines */
|
2023-02-12 10:56:45 +11:00
|
|
|
|> lineTo({ to: [1,0], tag: "rightPath" }, %)
|
|
|
|
|> close(%)
|
|
|
|
`
|
2023-02-01 07:30:55 +11:00
|
|
|
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-02-12 10:56:45 +11:00
|
|
|
const indexOfSecondLineToExpression = 2
|
|
|
|
const sketchNonCodeMeta = (body as any)[0].declarations[0].init.nonCodeMeta
|
2023-09-18 17:14:12 -06:00
|
|
|
.nonCodeNodes
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
expect(sketchNonCodeMeta[indexOfSecondLineToExpression][0]).toEqual({
|
2023-09-18 17:14:12 -06:00
|
|
|
type: 'NonCodeNode',
|
2023-02-12 10:56:45 +11:00
|
|
|
start: 106,
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
end: 163,
|
2023-09-05 16:02:27 -07:00
|
|
|
value: {
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
type: 'inlineComment',
|
|
|
|
style: 'block',
|
2023-09-05 16:02:27 -07:00
|
|
|
value: 'this is\n a comment\n spanning a few lines',
|
|
|
|
},
|
2023-02-01 07:30:55 +11:00
|
|
|
})
|
|
|
|
})
|
|
|
|
it('comments in a pipe expression', () => {
|
|
|
|
const code = [
|
2023-02-12 10:56:45 +11:00
|
|
|
'const mySk1 = startSketchAt([0, 0])',
|
|
|
|
' |> lineTo([1, 1], %)',
|
|
|
|
' |> lineTo({to: [0, 1], tag: "myPath"}, %)',
|
|
|
|
' |> lineTo([1, 1], %)',
|
2023-02-01 07:30:55 +11:00
|
|
|
'// a comment',
|
|
|
|
' |> rx(90, %)',
|
|
|
|
].join('\n')
|
|
|
|
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-02-01 07:30:55 +11:00
|
|
|
const sketchNonCodeMeta = (body[0] as any).declarations[0].init.nonCodeMeta
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
.nonCodeNodes[3][0]
|
|
|
|
expect(sketchNonCodeMeta).toEqual({
|
2023-09-18 17:14:12 -06:00
|
|
|
type: 'NonCodeNode',
|
2023-02-12 10:56:45 +11:00
|
|
|
start: 125,
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
end: 138,
|
2023-09-05 16:02:27 -07:00
|
|
|
value: {
|
2023-09-06 10:36:03 -07:00
|
|
|
type: 'blockComment',
|
2023-09-05 16:02:27 -07:00
|
|
|
value: 'a comment',
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
style: 'line',
|
2023-09-05 16:02:27 -07:00
|
|
|
},
|
2023-02-01 07:30:55 +11:00
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
2023-03-02 21:19:11 +11:00
|
|
|
describe('test UnaryExpression', () => {
|
|
|
|
it('should parse a unary expression in simple var dec situation', () => {
|
|
|
|
const code = `const myVar = -min(4, 100)`
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-03-02 21:19:11 +11:00
|
|
|
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
|
|
|
expect(myVarInit).toEqual({
|
|
|
|
type: 'UnaryExpression',
|
|
|
|
operator: '-',
|
|
|
|
start: 14,
|
|
|
|
end: 26,
|
|
|
|
argument: {
|
|
|
|
type: 'CallExpression',
|
|
|
|
start: 15,
|
|
|
|
end: 26,
|
|
|
|
callee: { type: 'Identifier', start: 15, end: 18, name: 'min' },
|
|
|
|
arguments: [
|
|
|
|
{ type: 'Literal', start: 19, end: 20, value: 4, raw: '4' },
|
|
|
|
{ type: 'Literal', start: 22, end: 25, value: 100, raw: '100' },
|
|
|
|
],
|
2023-09-05 16:02:27 -07:00
|
|
|
function: expect.any(Object),
|
2023-03-02 21:19:11 +11:00
|
|
|
optional: false,
|
|
|
|
},
|
|
|
|
})
|
|
|
|
})
|
|
|
|
})
|
|
|
|
|
|
|
|
describe('testing nested call expressions', () => {
|
|
|
|
it('callExp in a binExp in a callExp', () => {
|
|
|
|
const code = 'const myVar = min(100, 1 + legLen(5, 3))'
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-03-02 21:19:11 +11:00
|
|
|
const myVarInit = (body?.[0] as any).declarations[0]?.init
|
|
|
|
expect(myVarInit).toEqual({
|
|
|
|
type: 'CallExpression',
|
|
|
|
start: 14,
|
|
|
|
end: 40,
|
|
|
|
callee: { type: 'Identifier', start: 14, end: 17, name: 'min' },
|
|
|
|
arguments: [
|
|
|
|
{ type: 'Literal', start: 18, end: 21, value: 100, raw: '100' },
|
|
|
|
{
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
operator: '+',
|
|
|
|
start: 23,
|
|
|
|
end: 39,
|
|
|
|
left: { type: 'Literal', value: 1, raw: '1', start: 23, end: 24 },
|
|
|
|
right: {
|
|
|
|
type: 'CallExpression',
|
|
|
|
start: 27,
|
|
|
|
end: 39,
|
|
|
|
callee: { type: 'Identifier', start: 27, end: 33, name: 'legLen' },
|
|
|
|
arguments: [
|
|
|
|
{ type: 'Literal', start: 34, end: 35, value: 5, raw: '5' },
|
|
|
|
{ type: 'Literal', start: 37, end: 38, value: 3, raw: '3' },
|
|
|
|
],
|
2023-09-05 16:02:27 -07:00
|
|
|
function: expect.any(Object),
|
2023-03-02 21:19:11 +11:00
|
|
|
optional: false,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
],
|
2023-09-05 16:02:27 -07:00
|
|
|
function: expect.any(Object),
|
2023-03-02 21:19:11 +11:00
|
|
|
optional: false,
|
|
|
|
})
|
2023-01-21 21:23:01 +11:00
|
|
|
})
|
|
|
|
})
|
2023-03-10 14:55:16 +11:00
|
|
|
|
|
|
|
describe('should recognise callExpresions in binaryExpressions', () => {
|
|
|
|
const code = "xLineTo(segEndX('seg02', %) + 1, %)"
|
|
|
|
it('should recognise the callExp', () => {
|
2023-09-29 11:11:01 -07:00
|
|
|
const { body } = parse(code)
|
2023-03-10 14:55:16 +11:00
|
|
|
const callExpArgs = (body?.[0] as any).expression?.arguments
|
|
|
|
expect(callExpArgs).toEqual([
|
|
|
|
{
|
|
|
|
type: 'BinaryExpression',
|
|
|
|
operator: '+',
|
|
|
|
start: 8,
|
|
|
|
end: 31,
|
|
|
|
left: {
|
|
|
|
type: 'CallExpression',
|
|
|
|
start: 8,
|
|
|
|
end: 27,
|
|
|
|
callee: { type: 'Identifier', start: 8, end: 15, name: 'segEndX' },
|
|
|
|
arguments: [
|
|
|
|
{
|
|
|
|
type: 'Literal',
|
|
|
|
start: 16,
|
|
|
|
end: 23,
|
|
|
|
value: 'seg02',
|
|
|
|
raw: "'seg02'",
|
|
|
|
},
|
|
|
|
{ type: 'PipeSubstitution', start: 25, end: 26 },
|
|
|
|
],
|
2023-09-05 16:02:27 -07:00
|
|
|
function: expect.any(Object),
|
2023-03-10 14:55:16 +11:00
|
|
|
optional: false,
|
|
|
|
},
|
|
|
|
right: { type: 'Literal', value: 1, raw: '1', start: 30, end: 31 },
|
|
|
|
},
|
|
|
|
{ type: 'PipeSubstitution', start: 33, end: 34 },
|
|
|
|
])
|
|
|
|
})
|
|
|
|
})
|
2023-08-22 13:28:02 +10:00
|
|
|
|
|
|
|
describe('parsing errors', () => {
|
|
|
|
it('should return an error when there is a unexpected closed curly brace', async () => {
|
|
|
|
const code = `const myVar = startSketchAt([}], %)`
|
|
|
|
|
|
|
|
let _theError
|
|
|
|
try {
|
2023-09-29 11:11:01 -07:00
|
|
|
const result = expect(parse(code))
|
2023-08-22 13:28:02 +10:00
|
|
|
} catch (e) {
|
|
|
|
_theError = e
|
|
|
|
}
|
|
|
|
const theError = _theError as any
|
2023-09-13 07:23:14 -07:00
|
|
|
expect(theError).toEqual(
|
New parser built in Winnow (#731)
* New parser built with Winnow
This new parser uses [winnow](docs.rs/winnow) to replace the handwritten recursive parser.
## Differences
I think the Winnow parser is more readable than handwritten one, due to reusing standard combinators. If you have a parsre like `p` or `q` you can combine them with standard functions like `repeat(0..4, p)`, `opt(p)`, `alt((p, q))` and `separated1(p, ", ")`. This IMO makes it more readable once you know what those standard functions do.
It's also more accurate now -- e.g. the parser no longer swallows whitespace between comments, or inserts it where there was none before. It no longer changes // comments to /* comments depending on the surrounding whitespace.
Primary form of testing was running the same KCL program through both the old and new parsers and asserting that both parsers produce the same AST. See the test `parser::parser_impl::tests::check_parsers_work_the_same`. But occasionally the new and old parsers disagree. This is either:
- Innocuous (e.g. disagreeing on whether a comment starts at the preceding whitespace or at the //)
- Helpful (e.g. new parser recognizes comments more accurately, preserving the difference between // and /* comments)
- Acceptably bad (e.g. new parser sometimes outputs worse error messages, TODO in #784)
so those KCL programs have their own unit tests in `parser_impl.rs` demonstrating the behaviour.
If you'd like to review this PR, it's arguably more important to review changes to the existing unit tests rather than the new parser itself. Because changes to the unit tests show where my parser changes behaviour -- usually for the better, occasionally for the worse (e.g. a worse error message than before). I think overall the improvements are worth it that I'd like to merge it without spending another week fixing it up -- we can fix the error messages in a follow-up PR.
## Performance
| Benchmark | Old parser (this branch) | New parser (this branch) | Speedup |
| ------------- | ------------- | ------------- | ------------- |
| Pipes on pipes | 922 ms | 42 ms | 21x |
| Kitt SVG | 148 ms | 7 ms | 21x |
There's definitely still room to improve performance:
- https://github.com/KittyCAD/modeling-app/issues/839
- https://github.com/KittyCAD/modeling-app/issues/840
## Winnow
Y'all know I love [Nom](docs.rs/nom) and I've blogged about it a lot. But I'm very happy using Winnow, a fork. It's got some really nice usability improvements. While writing this PR I found some bugs or unclear docs in Winnow:
- https://github.com/winnow-rs/winnow/issues/339
- https://github.com/winnow-rs/winnow/issues/341
- https://github.com/winnow-rs/winnow/issues/342
- https://github.com/winnow-rs/winnow/issues/344
The maintainer was quick to close them and release new versions within a few hours, so I feel very confident building the parser on this library. It's a clear improvement over Nom and it's used in toml-edit (and therefore within Cargo) and Gitoxide, so it's becoming a staple of the Rust ecosystem, which adds confidence.
Closes #716
Closes #815
Closes #599
2023-10-12 09:42:37 -05:00
|
|
|
new KCLError('syntax', 'Unexpected token', [[27, 28]])
|
2023-09-13 07:23:14 -07:00
|
|
|
)
|
2023-08-22 13:28:02 +10:00
|
|
|
})
|
|
|
|
})
|