diff --git a/package/src/__test__/test.test.ts b/package/src/__test__/test.test.ts index 51ef2c1..e48fa14 100644 --- a/package/src/__test__/test.test.ts +++ b/package/src/__test__/test.test.ts @@ -1,3 +1,5 @@ +import { transpiler } from '@parser' +import { Expression } from '@parser/nodes/expressions/index' import { Program } from '@parser/program' import { parserNode } from '@parser/test' @@ -37,8 +39,11 @@ describe('Test every thing', () => { in ra (a) a++ }` - const test = ` test = 2 + i -1` + const test = `khai báo a = { + a: 1 + } + in ra(a.a) + ` const result = parserNode.parse(test, Program) - console.log(JSON.stringify(result, null, 2)) }) }) diff --git a/package/src/constants/keyword.ts b/package/src/constants/keyword.ts index de331dd..a1f75e9 100644 --- a/package/src/constants/keyword.ts +++ b/package/src/constants/keyword.ts @@ -63,5 +63,8 @@ export enum Keyword { INSTANCEOF = 'Instanceof', WITH = 'With', DEBUGGER = 'Debugger', - IDENTIFIER = 'Identifier' + IDENTIFIER = 'Identifier', + RELATIONAL_OPERATOR = 'RelationalOperator', + ADDITIVE_OPERATOR = 'AdditiveOperator', + MULTIPLICATIVE_OPERATOR = 'MultiplicativeOperator' } diff --git a/package/src/constants/specs.ts b/package/src/constants/specs.ts index c83d67f..e251fb0 100644 --- a/package/src/constants/specs.ts +++ b/package/src/constants/specs.ts @@ -16,6 +16,14 @@ export const Specs: Array = [ // Skip multi-line comments: [/^\/\*[\S\s]*?\*\//, null], + [/^\+\+/, '++'], // PlusPlus + + // -------------------------------------- + // Math operators: +, -, *, / + [/^[+\-]/, Keyword.ADDITIVE_OPERATOR], + [/^[*\/\%]/, Keyword.MULTIPLICATIVE_OPERATOR], + // Relational operators: >, >=, <, <= + [/^(<=|>=|<|>|==)/, Keyword.RELATIONAL_OPERATOR], // -------------------------------------- // Symbols and delimiters: [/^\[/, '['], // OpenBracket @@ -36,7 +44,6 @@ export const Specs: Array = [ [/^:/, ':'], // Colon [/^\.{3}/, '...'], // Ellipsis [/^\./, '.'], // Dot - [/^\+\+/, '++'], // PlusPlus [/^\+/, '+'], // Plus [/^--/, '--'], // MinusMinus [/^-/, '-'], // Minus diff --git a/package/src/nodes/expressions/__test__/assignment.test.ts b/package/src/nodes/expressions/__test__/assignment.test.ts index ad54c89..2872857 100644 --- a/package/src/nodes/expressions/__test__/assignment.test.ts +++ b/package/src/nodes/expressions/__test__/assignment.test.ts @@ -5,7 +5,7 @@ import { AssignmentExpression } from '../assignment' describe('expression-assignment.test', () => { it('should parse the syntax normally', () => { const result = parserNode.parse('a = 12;', AssignmentExpression) - expect(toPlainObject(result)).toStrictEqual({ + expect(toPlainObject(result)).toEqual({ type: 'AssignmentExpression', left: { type: 'Identifier', @@ -13,24 +13,20 @@ describe('expression-assignment.test', () => { }, operator: '=', right: { - type: 'BinaryExpression', - left: { - type: 'NumericLiteral', - start: 4, - end: 6, - value: 12, - extra: { - rawValue: 12, - raw: '12' - } - }, - right: {} + type: 'NumericLiteral', + start: 4, + end: 6, + value: 12, + extra: { + rawValue: 12, + raw: '12' + } } }) }) it('Should parse the syntax with a complex expression', () => { const result = parserNode.parse('a = 12 + 2;', AssignmentExpression) - expect(toPlainObject(result)).toStrictEqual({ + expect(toPlainObject(result)).toEqual({ type: 'AssignmentExpression', left: { type: 'Identifier', @@ -39,6 +35,7 @@ describe('expression-assignment.test', () => { operator: '=', right: { type: 'BinaryExpression', + operator: '+', left: { type: 'NumericLiteral', start: 4, @@ -49,7 +46,6 @@ describe('expression-assignment.test', () => { raw: '12' } }, - operator: '+', right: { type: 'NumericLiteral', start: 9, diff --git a/package/src/nodes/expressions/__test__/binary.test.ts b/package/src/nodes/expressions/__test__/binary.test.ts index 99297bd..e7c5e97 100644 --- a/package/src/nodes/expressions/__test__/binary.test.ts +++ b/package/src/nodes/expressions/__test__/binary.test.ts @@ -1,26 +1,77 @@ import { parserNode } from '@parser/test' import toPlainObject from '@parser/utils/toPlainObject' -import { BinaryExpression } from '../binary' import { Expression } from '../index' +import { BinaryExpression } from '../binary/index' describe('expression-binary.test', () => { it('should parse the syntax normally', () => { - const result = parserNode.parse('xin chào === hello', Expression) - - expect(toPlainObject(result)).toStrictEqual({ - type: 'BinaryExpression', - operator: '===', + const res = parserNode.parse('a = 1 + 2 ', Expression) + expect(toPlainObject(res)).toEqual({ + type: 'AssignmentExpression', left: { type: 'Identifier', - name: 'xin_ch_224o' + name: 'a' }, + operator: '=', right: { - type: 'Identifier', - name: 'hello' + type: 'BinaryExpression', + operator: '+', + left: { + type: 'NumericLiteral', + start: 4, + end: 5, + value: 1, + extra: { + rawValue: 1, + raw: '1' + } + }, + right: { + type: 'NumericLiteral', + start: 8, + end: 9, + value: 2, + extra: { + rawValue: 2, + raw: '2' + } + } } - } as BinaryExpression) + }) }) it('should parse the syntax normally', () => { - const res = parserNode.parse('a = 1 + 2 ', Expression) + const res = parserNode.parse('a - 1 * 2 ', BinaryExpression) + expect(toPlainObject(res)).toEqual({ + type: 'BinaryExpression', + left: { + type: 'Identifier', + name: 'a' + }, + operator: '-', + right: { + type: 'BinaryExpression', + operator: '*', + left: { + type: 'NumericLiteral', + start: 4, + end: 5, + value: 1, + extra: { + rawValue: 1, + raw: '1' + } + }, + right: { + type: 'NumericLiteral', + start: 8, + end: 9, + value: 2, + extra: { + rawValue: 2, + raw: '2' + } + } + } + }) }) }) diff --git a/package/src/nodes/expressions/__test__/unary.test.ts b/package/src/nodes/expressions/__test__/unary.test.ts deleted file mode 100644 index 9a6c21b..0000000 --- a/package/src/nodes/expressions/__test__/unary.test.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { parserNode } from '@parser/test' -import { UnaryExpression } from '../unary' -import toPlainObject from '@parser/utils/toPlainObject' - -describe('expression-unary.test', () => { - it('should parse the syntax normally', () => { - const result = parserNode.parse('+"12"', UnaryExpression) - expect(toPlainObject(result)).toStrictEqual({ - type: 'UnaryExpression', - operator: '+', - prefix: true, - argument: { - type: 'StringLiteral', - value: '12', - extra: { - rawValue: '12', - raw: '"12"' - }, - start: 1, - end: 5 - } - }) - }) -}) diff --git a/package/src/nodes/expressions/assignment.ts b/package/src/nodes/expressions/assignment.ts index 2912d74..2e6ab66 100644 --- a/package/src/nodes/expressions/assignment.ts +++ b/package/src/nodes/expressions/assignment.ts @@ -3,6 +3,7 @@ import { Keyword } from '@parser/constants/keyword' import { Identifier } from '../identifier/index' import { Expression } from './index' import { BinaryExpression } from './binary/index' +import { BinaryExpressionBuilder } from './binary/builder' export class AssignmentExpression { type = 'AssignmentExpression' @@ -16,11 +17,10 @@ export class AssignmentExpression { constructor(parser: Parser, identifier?: Identifier | Expression) { this.left = identifier ?? (parser.nextToken?.type === Keyword.IDENTIFIER ? new Identifier(parser) : new Expression(parser)) - while (parser.nextToken?.type === '=') { - this.operator = String(parser.validate('=').value) - this.right = new BinaryExpression(parser) - return + this.operator = String(parser.validate(parser.nextToken?.type as any).value) + const binaryBuilder = new BinaryExpressionBuilder(parser, identifier as Identifier) + this.right = binaryBuilder.RelationalExpression() } } } diff --git a/package/src/nodes/expressions/binary/builder.ts b/package/src/nodes/expressions/binary/builder.ts index f6ba75c..02dd9a4 100644 --- a/package/src/nodes/expressions/binary/builder.ts +++ b/package/src/nodes/expressions/binary/builder.ts @@ -1,17 +1,20 @@ -import { Identifier } from '@parser/nodes/identifier/index' -import { Expression } from '../index' +import { Keyword } from '@parser/constants/keyword' import { Parser } from '@parser/parser' import { Token } from '@parser/types/token' -import { Operator } from '@parser/types/operator' +import { UnaryExpression } from '../unary' +import { Literal } from '@parser/nodes/literal/index' +import { Expression } from '../index' +import { Identifier } from '@parser/nodes/identifier/index' export class BinaryExpressionBuilder { parser: Parser - constructor(_parser: Parser) { + identifier?: Identifier + constructor(_parser: Parser, _identifier?: Identifier) { this.parser = _parser + this.identifier = _identifier } - BuilderExpression(builderName: string, operatorToken: Operator) { + BuilderExpression(builderName: string, operatorToken: Token['type']) { let left = this[builderName]?.() - while (this.parser.nextToken?.type === operatorToken) { const operator = this.parser.validate(operatorToken).value const right = this[builderName]() @@ -22,17 +25,36 @@ export class BinaryExpressionBuilder { right } } - return left } AdditiveExpression() { - console.log('vao dau') - return { - type: 'BinaryExpression', - operator: '+', - left: new Identifier(this.parser), - right: new Expression(this.parser) + const res = this.BuilderExpression('MultiplicativeExpression', Keyword.ADDITIVE_OPERATOR) + return res + } + MultiplicativeExpression() { + const res = this.BuilderExpression('PrimaryExpression', Keyword.MULTIPLICATIVE_OPERATOR) + return res + } + RelationalExpression() { + const res = this.BuilderExpression('AdditiveExpression', Keyword.RELATIONAL_OPERATOR) + return res + } + PrimaryExpression() { + switch (this.parser.nextToken?.type) { + case '(': + return this.ParenthesizedExpression() + case Keyword.IDENTIFIER: + return new Identifier(this.parser) + default: + return new Literal(this.parser) } } + + ParenthesizedExpression() { + this.parser.validate('(') + const expression = new Expression(this.parser) + this.parser.validate(')') + return expression + } } diff --git a/package/src/nodes/expressions/binary/index.ts b/package/src/nodes/expressions/binary/index.ts index 95d4aac..553aefc 100644 --- a/package/src/nodes/expressions/binary/index.ts +++ b/package/src/nodes/expressions/binary/index.ts @@ -3,46 +3,29 @@ import { Identifier } from '@parser/nodes/identifier/index' import { Parser } from '@parser/parser' import { Expression } from '../index' import { BinaryExpressionBuilder } from './builder' -import { Operator } from '@parser/types/operator' -import { CLIENT_RENEG_LIMIT } from 'tls' export class BinaryExpression { type = 'BinaryExpression' left: Identifier | Expression - operator: string | undefined + operator?: string - right: Identifier | Expression + right?: Identifier | Expression constructor(parser: Parser, identifier?: Identifier) { - const binaryBuilder = new BinaryExpressionBuilder(parser) this.left = - identifier ?? - (parser.nextToken?.type === Keyword.IDENTIFIER ? new Identifier(parser) : binaryBuilder.AdditiveExpression()) - switch (parser.nextToken?.type) { - case '+': - case '-': - case '*': - case '/': - case '%': - case '**': - case '^': - case '>': - case '>>': - case '>>>': - case '<': - case '<<': - case '<<<': - case '>=': - case '<=': - case '==': - case '===': { - this.operator = String(parser.validate(parser.nextToken?.type).value) - break - } - } + identifier ?? (parser.nextToken?.type === Keyword.IDENTIFIER ? new Identifier(parser) : new Expression(parser)) + + while ( + [Keyword.RELATIONAL_OPERATOR, Keyword.ADDITIVE_OPERATOR, Keyword.MULTIPLICATIVE_OPERATOR].includes( + parser.nextToken?.type as Keyword + ) + ) { + this.operator = String(parser.validate(parser.nextToken?.type as any).value) + const binaryBuilder = new BinaryExpressionBuilder(parser, identifier) - this.right = new Expression(parser) + this.right = binaryBuilder.RelationalExpression() + } } } diff --git a/package/src/nodes/expressions/index.ts b/package/src/nodes/expressions/index.ts index 38935e5..dd1b627 100644 --- a/package/src/nodes/expressions/index.ts +++ b/package/src/nodes/expressions/index.ts @@ -67,7 +67,10 @@ export class Expression { case '>=': case '<=': case '==': - case '===': { + case '===': + case Keyword.ADDITIVE_OPERATOR: + case Keyword.MULTIPLICATIVE_OPERATOR: + case Keyword.RELATIONAL_OPERATOR: { Object.assign(this, new BinaryExpression(parser, identifier)) break } diff --git a/package/src/nodes/expressions/parenthesized.ts b/package/src/nodes/expressions/parenthesized.ts new file mode 100644 index 0000000..a26cb6d --- /dev/null +++ b/package/src/nodes/expressions/parenthesized.ts @@ -0,0 +1,14 @@ +import { Parser } from '@parser/parser' +import { ObjectLiteral } from '../literal/object' +import { Expression } from './index' + +export class ParenthesizedExpression { + type = 'ParenthesizedExpression' + expression: Expression + + constructor(parser: Parser) { + parser.validate('(') + this.expression = new Expression(parser) + parser.validate(')') + } +} diff --git a/package/src/nodes/identifier/index.ts b/package/src/nodes/identifier/index.ts index fa4a10e..6c5f19a 100644 --- a/package/src/nodes/identifier/index.ts +++ b/package/src/nodes/identifier/index.ts @@ -7,10 +7,11 @@ export class Identifier { name: string constructor(parser: Parser) { - this.name = String(parser.validate(Keyword.IDENTIFIER)?.value) + const res = parser.validate(Keyword.IDENTIFIER)?.value + this.name = String(res) .replace(/[^\sA-Za-z]/g, (match) => { // Find vietnamese character and replace - return `_${match.codePointAt(0)}` + return `_${match?.codePointAt(0)}` }) .replace(/\s/g, '_') } diff --git a/package/src/nodes/statements/breakable/__test__/for.test.ts b/package/src/nodes/statements/breakable/__test__/for.test.ts index d671b6b..9212fa2 100644 --- a/package/src/nodes/statements/breakable/__test__/for.test.ts +++ b/package/src/nodes/statements/breakable/__test__/for.test.ts @@ -4,8 +4,7 @@ import { ForStatement } from '../iteration/for' describe('For statement test', () => { it('Should parse for statement correctly', () => { - const result = parserNode.parse('for(let a = 0; a < 10; a = a + 1){return 1;}', ForStatement) - + const result = parserNode.parse('for(let a = 0; a < 10; a++){return 1;}', ForStatement) expect(toPlainObject(result)).toStrictEqual({ type: 'ForStatement', init: { @@ -15,13 +14,13 @@ describe('For statement test', () => { type: 'VariableDeclarator', init: { type: 'NumericLiteral', + start: 12, + end: 13, value: 0, extra: { rawValue: 0, raw: '0' - }, - start: 12, - end: 13 + } }, id: { type: 'Identifier', @@ -40,40 +39,23 @@ describe('For statement test', () => { operator: '<', right: { type: 'NumericLiteral', + start: 19, + end: 21, value: 10, extra: { rawValue: 10, raw: '10' - }, - start: 19, - end: 21 + } } }, update: { - type: 'AssignmentExpression', - left: { + type: 'UpdateExpression', + argument: { type: 'Identifier', name: 'a' }, - operator: '=', - right: { - type: 'BinaryExpression', - left: { - type: 'Identifier', - name: 'a' - }, - operator: '+', - right: { - type: 'NumericLiteral', - value: 1, - extra: { - rawValue: 1, - raw: '1' - }, - start: 31, - end: 32 - } - } + prefix: false, + operator: '++' }, body: { type: 'BlockStatement', @@ -82,13 +64,13 @@ describe('For statement test', () => { type: 'ReturnStatement', argument: { type: 'NumericLiteral', + start: 35, + end: 36, value: 1, extra: { rawValue: 1, raw: '1' - }, - start: 41, - end: 42 + } } } ] @@ -97,7 +79,7 @@ describe('For statement test', () => { }) it('Should parse for statement correctly', () => { - const result = parserNode.parse('lặp(khai báo a = 0; a < 10; a++){return 1;}', ForStatement) + const result = parserNode.parse('lặp(khai báo a = 0; a < 10 - 2 * 2; a++){return 1;}', ForStatement) expect(toPlainObject(result)).toStrictEqual({ type: 'ForStatement', init: { @@ -107,13 +89,13 @@ describe('For statement test', () => { type: 'VariableDeclarator', init: { type: 'NumericLiteral', + start: 17, + end: 18, value: 0, extra: { rawValue: 0, raw: '0' - }, - start: 17, - end: 18 + } }, id: { type: 'Identifier', @@ -131,24 +113,52 @@ describe('For statement test', () => { }, operator: '<', right: { - type: 'NumericLiteral', - value: 10, - extra: { - rawValue: 10, - raw: '10' + type: 'BinaryExpression', + operator: '-', + left: { + type: 'NumericLiteral', + start: 24, + end: 26, + value: 10, + extra: { + rawValue: 10, + raw: '10' + } }, - start: 24, - end: 26 + right: { + type: 'BinaryExpression', + operator: '*', + left: { + type: 'NumericLiteral', + start: 29, + end: 30, + value: 2, + extra: { + rawValue: 2, + raw: '2' + } + }, + right: { + type: 'NumericLiteral', + start: 33, + end: 34, + value: 2, + extra: { + rawValue: 2, + raw: '2' + } + } + } } }, update: { type: 'UpdateExpression', - operator: '++', argument: { type: 'Identifier', name: 'a' }, - prefix: false + prefix: false, + operator: '++' }, body: { type: 'BlockStatement', @@ -157,13 +167,13 @@ describe('For statement test', () => { type: 'ReturnStatement', argument: { type: 'NumericLiteral', + start: 48, + end: 49, value: 1, extra: { rawValue: 1, raw: '1' - }, - start: 40, - end: 41 + } } } ] diff --git a/package/src/nodes/statements/breakable/iteration/for.ts b/package/src/nodes/statements/breakable/iteration/for.ts index a07cb6f..09cccd9 100644 --- a/package/src/nodes/statements/breakable/iteration/for.ts +++ b/package/src/nodes/statements/breakable/iteration/for.ts @@ -49,7 +49,6 @@ export class ForStatement { } parser.validate(';') - this.test = new Expression(parser) if (parser.nextToken?.type === ';') { parser.validate(';') diff --git a/package/src/nodes/statements/breakable/iteration/while.ts b/package/src/nodes/statements/breakable/iteration/while.ts index ae5171b..4397f50 100644 --- a/package/src/nodes/statements/breakable/iteration/while.ts +++ b/package/src/nodes/statements/breakable/iteration/while.ts @@ -1,7 +1,7 @@ import { Parser } from '@parser/parser' import { BlockStatement } from '../../block' -import { Expression } from '@parser/nodes/expressions' import { Keyword } from '@parser/constants/keyword' +import { Expression } from '@parser/nodes/expressions/index' export class WhileStatement { type = 'WhileStatement' diff --git a/package/src/nodes/statements/item.ts b/package/src/nodes/statements/item.ts index ead4b9e..cf129c8 100644 --- a/package/src/nodes/statements/item.ts +++ b/package/src/nodes/statements/item.ts @@ -5,7 +5,6 @@ import { Statement } from './index' export class StatementItem { constructor(parser: Parser) { - console.log(parser.nextToken?.type) switch (parser.nextToken?.type) { case Keyword.LET: case Keyword.CONST: