Improved math expressions (#6)
* Improved math expressions Things are in a better state, + - / * work now for basic const var = 5 <operator> 1 Though the current method I'm using to make the ast isn't really going to work for dealing with precedence rules so some refactoring is needed going forward * get complex math expressions working with precedence including parans Node that identifiers are working, call expressions are not, that's a TODO / * % + - are working both other things like exponent and logical operators are also not working. Recasting is the most important thing to implement next * get recasting working for nested binary expressions * clean up
This commit is contained in:
@ -2,6 +2,7 @@ import {
|
||||
abstractSyntaxTree,
|
||||
findClosingBrace,
|
||||
hasPipeOperator,
|
||||
findEndOfBinaryExpression,
|
||||
} from './abstractSyntaxTree'
|
||||
import { lexer } from './tokeniser'
|
||||
|
||||
@ -1521,3 +1522,334 @@ describe('testing pipe operator special', () => {
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
describe('nests binary expressions correctly', () => {
|
||||
it('it works with the simple case', () => {
|
||||
const code = `const yo = 1 + 2`
|
||||
const { body } = abstractSyntaxTree(lexer(code))
|
||||
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',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
it('it should nest according to precedence with multiply first', () => {
|
||||
// should be binExp { binExp { lit-1 * lit-2 } + lit}
|
||||
const code = `const yo = 1 * 2 + 3`
|
||||
const { body } = abstractSyntaxTree(lexer(code))
|
||||
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',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
it('it should nest according to precedence with sum first', () => {
|
||||
// should be binExp { lit-1 + binExp { lit-2 * lit-3 } }
|
||||
const code = `const yo = 1 + 2 * 3`
|
||||
const { body } = abstractSyntaxTree(lexer(code))
|
||||
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',
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
})
|
||||
})
|
||||
it('it should nest properly with two opperators of equal precedence', () => {
|
||||
const code = `const yo = 1 + 2 - 3`
|
||||
const { body } = abstractSyntaxTree(lexer(code))
|
||||
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',
|
||||
},
|
||||
})
|
||||
})
|
||||
it('it should nest properly with two opperators of equal (but higher) precedence', () => {
|
||||
const code = `const yo = 1 * 2 / 3`
|
||||
const { body } = abstractSyntaxTree(lexer(code))
|
||||
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',
|
||||
},
|
||||
})
|
||||
})
|
||||
it('it should nest properly with longer example', () => {
|
||||
const code = `const yo = 1 + 2 * (3 - 4) / 5 + 6`
|
||||
const { body } = abstractSyntaxTree(lexer(code))
|
||||
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 },
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('testing findEndofBinaryExpression', () => {
|
||||
it('1 + 2 * 3', () => {
|
||||
const code = `1 + 2 * 3\nconst yo = 5`
|
||||
const tokens = lexer(code)
|
||||
const end = findEndOfBinaryExpression(tokens, 0)
|
||||
expect(end).toBe(8)
|
||||
})
|
||||
it('(1 + 2) / 5 - 3', () => {
|
||||
const code = `(1 + 25) / 5 - 3\nconst yo = 5`
|
||||
const tokens = lexer(code)
|
||||
const end = findEndOfBinaryExpression(tokens, 0)
|
||||
expect(end).toBe(14)
|
||||
|
||||
// expect to have the same end if started later in the string at a legitimate place
|
||||
const indexOf5 = code.indexOf('5')
|
||||
const endStartingAtThe5 = findEndOfBinaryExpression(tokens, indexOf5)
|
||||
expect(endStartingAtThe5).toBe(end)
|
||||
})
|
||||
it('whole thing wraped: ((1 + 2) / 5 - 3)', () => {
|
||||
const code = '((1 + 2) / 5 - 3)\nconst yo = 5'
|
||||
const tokens = lexer(code)
|
||||
const end = findEndOfBinaryExpression(tokens, 0)
|
||||
expect(end).toBe(code.indexOf('3)') + 1)
|
||||
})
|
||||
it('whole thing wraped but given index after the first brace: ((1 + 2) / 5 - 3)', () => {
|
||||
const code = '((1 + 2) / 5 - 3)\nconst yo = 5'
|
||||
const tokens = lexer(code)
|
||||
const end = findEndOfBinaryExpression(tokens, 1)
|
||||
expect(end).toBe(code.indexOf('3'))
|
||||
})
|
||||
it('given the index of a small wrapped section i.e. `1 + 2` in ((1 + 2) / 5 - 3)', () => {
|
||||
const code = '((1 + 2) / 5 - 3)\nconst yo = 5'
|
||||
const tokens = lexer(code)
|
||||
const end = findEndOfBinaryExpression(tokens, 2)
|
||||
expect(end).toBe(code.indexOf('2'))
|
||||
})
|
||||
it('lots of silly nesting: (1 + 2) / (5 - (3))', () => {
|
||||
const code = '(1 + 2) / (5 - (3))\nconst yo = 5'
|
||||
const tokens = lexer(code)
|
||||
const end = findEndOfBinaryExpression(tokens, 0)
|
||||
expect(end).toBe(code.indexOf('))') + 1)
|
||||
})
|
||||
it('with pipe operator at the end', () => {
|
||||
const code = '(1 + 2) / (5 - (3))\n |> fn(%)'
|
||||
const tokens = lexer(code)
|
||||
const end = findEndOfBinaryExpression(tokens, 0)
|
||||
expect(end).toBe(code.indexOf('))') + 1)
|
||||
})
|
||||
})
|
||||
|
Reference in New Issue
Block a user