From 4d570ec9fd9acda8757e53ac41b919b45ba602b5 Mon Sep 17 00:00:00 2001 From: Aditya Anchuri Date: Sun, 29 Jan 2023 00:11:05 -0800 Subject: [PATCH 1/5] Basic array assignment and access --- .../visitor/arrayAccessExpression.ts | 30 ++++++++++++++++ .../src/components/visitor/arrayExpression.ts | 18 ++++++++++ .../src/module/interpreterModule.ts | 5 +++ .../test/integration/positiveTestsProvider.ts | 21 ++++++++++- .../test/integration/testRunner.test.ts | 13 ++++++- .../expression/arrayAccessExpression.ts | 35 +++++++++++++++++++ .../statement/expression/arrayExpression.ts | 30 ++++++++++++++++ .../expression/assignmentExpression.ts | 1 + .../parser/statement/expression/index.ts | 6 ++++ .../statement/expression/primaryExpression.ts | 6 +++- packages/parser/src/constants/bhaiLangSpec.ts | 6 ++++ packages/parser/src/constants/constants.ts | 2 ++ packages/parser/src/module/bhaiLangModule.ts | 26 ++++++++++++++ 13 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 packages/interpreter/src/components/visitor/arrayAccessExpression.ts create mode 100644 packages/interpreter/src/components/visitor/arrayExpression.ts create mode 100644 packages/parser/src/components/parser/statement/expression/arrayAccessExpression.ts create mode 100644 packages/parser/src/components/parser/statement/expression/arrayExpression.ts diff --git a/packages/interpreter/src/components/visitor/arrayAccessExpression.ts b/packages/interpreter/src/components/visitor/arrayAccessExpression.ts new file mode 100644 index 00000000..ce7d00c8 --- /dev/null +++ b/packages/interpreter/src/components/visitor/arrayAccessExpression.ts @@ -0,0 +1,30 @@ +import Visitor from "."; +import { ASTNode } from "bhai-lang-parser"; + +import RuntimeException from "../../exceptions/runtimeException"; +import InterpreterModule from "../../module/interpreterModule"; + +export default class ArrayAccessExpression implements Visitor { + visitNode(node: ASTNode) { + const identifier = node.expressions?.[0]!; + const index = node.expressions?.[1]!; + + const array = InterpreterModule.getVisitor(identifier.type).visitNode(identifier); + const indexValue = InterpreterModule.getVisitor(index.type).visitNode(index); + if (typeof indexValue !== 'number') { + throw new RuntimeException( + `Bhai, kuch phat gaya: ${identifier.name} mein dekhne ke liye number mangta hai, ${indexValue} nahi.` + ); + } + + if (!Array.isArray(array)) { + throw new RuntimeException( + `Bhai, kuch phat gaya: ${identifier.name} toh array hi nahi hai.` + ); + } + + return array[indexValue]; + } +} + + diff --git a/packages/interpreter/src/components/visitor/arrayExpression.ts b/packages/interpreter/src/components/visitor/arrayExpression.ts new file mode 100644 index 00000000..b3e74643 --- /dev/null +++ b/packages/interpreter/src/components/visitor/arrayExpression.ts @@ -0,0 +1,18 @@ +import Visitor from "."; +import { ASTNode } from "bhai-lang-parser"; + +import InterpreterModule from "../../module/interpreterModule"; + +export default class ArrayExpression implements Visitor { + visitNode(node: ASTNode) { + let values: any[] = []; + + node.expressions?.forEach(expr => { + const value = InterpreterModule.getVisitor(expr.type).visitNode(expr); + values.push(value); + }); + + return values; + } +} + diff --git a/packages/interpreter/src/module/interpreterModule.ts b/packages/interpreter/src/module/interpreterModule.ts index adef304b..38942406 100644 --- a/packages/interpreter/src/module/interpreterModule.ts +++ b/packages/interpreter/src/module/interpreterModule.ts @@ -3,6 +3,8 @@ import parser, { NodeType } from "bhai-lang-parser"; import Interpreter from "../components/interpreter"; import Scope from "../components/scope"; import Visitor from "../components/visitor"; +import ArrayExpression from "../components/visitor/arrayExpression"; +import ArrayAccessExpression from "../components/visitor/arrayAccessExpression"; import AssignmentExpression from "../components/visitor/assignmentExpression"; import BinaryExpression from "../components/visitor/binaryExpression"; import BlockStatement from "../components/visitor/blockStatement"; @@ -35,6 +37,9 @@ export default class InterpreterModule { [NodeType.VariableStatement]: new VariableStatement(), [NodeType.IdentifierExpression]: new IdentifierExpression(), [NodeType.VariableDeclaration]: new VariableDeclaration(), + [NodeType.ArrayExpression]: new ArrayExpression(), + [NodeType.ArrayAccessExpression]: new ArrayAccessExpression(), + [NodeType.AssignmentExpression]: new AssignmentExpression(), [NodeType.AssignmentExpression]: new AssignmentExpression(), [NodeType.ExpressionStatement]: new ExpressionStatement(), [NodeType.BinaryExpression]: new BinaryExpression(), diff --git a/packages/interpreter/test/integration/positiveTestsProvider.ts b/packages/interpreter/test/integration/positiveTestsProvider.ts index b70c8dd0..052a2c57 100644 --- a/packages/interpreter/test/integration/positiveTestsProvider.ts +++ b/packages/interpreter/test/integration/positiveTestsProvider.ts @@ -996,4 +996,23 @@ export const WithOutputPositiveTests = [ `, output: "1", }, -]; \ No newline at end of file +]; + +export const WithMultilineOutputPositiveTests = [ + { + name: "basic arrays test", + input: ` + hi bhai + bhai ye hai b = 2; + bhai ye hai a = [1, b + 2, 3]; + bol bhai a; + bol bhai a[0]; + bol bhai a[1]; + bol bhai a[2]; + bol bhai a[3]; + bye bhai + `, + outputs: [`1,4,3`, `1`, `4`, `3`], + } +]; + diff --git a/packages/interpreter/test/integration/testRunner.test.ts b/packages/interpreter/test/integration/testRunner.test.ts index 57b86863..dcff2950 100644 --- a/packages/interpreter/test/integration/testRunner.test.ts +++ b/packages/interpreter/test/integration/testRunner.test.ts @@ -5,7 +5,8 @@ import InterpreterModule from "../../src/module/interpreterModule"; import { NegativeTestCases } from "./negativeTestsProvider"; import { NoOutputPositiveTests, - WithOutputPositiveTests + WithOutputPositiveTests, + WithMultilineOutputPositiveTests } from "./positiveTestsProvider"; @@ -31,6 +32,16 @@ WithOutputPositiveTests.forEach((testCase) => { }); }); +WithMultilineOutputPositiveTests.forEach((testCase) => { + test(testCase.name, () => { + expect(() => interpreter.interpret(testCase.input)).not.toThrowError(); + + testCase.outputs.forEach(output => { + expect(console.log).toHaveBeenCalledWith(output); + }); + }); +}); + NegativeTestCases.forEach((testCase) => { test(testCase.name, () => { expect(() => interpreter.interpret(testCase.input)).toThrowError( diff --git a/packages/parser/src/components/parser/statement/expression/arrayAccessExpression.ts b/packages/parser/src/components/parser/statement/expression/arrayAccessExpression.ts new file mode 100644 index 00000000..ad47e511 --- /dev/null +++ b/packages/parser/src/components/parser/statement/expression/arrayAccessExpression.ts @@ -0,0 +1,35 @@ +import Expression from "."; + +import { TokenTypes } from "../../../../constants/bhaiLangSpec"; +import { NodeType } from "../../../../constants/constants"; +import { ASTNode } from "../../types/nodeTypes"; + + +export default class ArrayAccessExpression extends Expression { + getExpression(): ASTNode { + const expressions = []; + + // name of identifier (could be array name) + const identifierExpression = Expression.getExpressionImpl(NodeType.IdentifierExpression).getExpression(); + + // this is an actual array access expression, otherwise we default to an identifier expression + if (this._tokenExecutor.getLookahead()?.type === TokenTypes.OPEN_BRACKET_TYPE) { + expressions.push(identifierExpression); + + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.OPEN_BRACKET_TYPE); + + // index (additive expression) + expressions.push(Expression.getExpressionImpl(NodeType.AdditiveExpression).getExpression()); + + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.CLOSED_BRACKET_TYPE); + + return { + type: NodeType.ArrayAccessExpression, + expressions, + }; + } + + return identifierExpression; + } +} + diff --git a/packages/parser/src/components/parser/statement/expression/arrayExpression.ts b/packages/parser/src/components/parser/statement/expression/arrayExpression.ts new file mode 100644 index 00000000..1b01c367 --- /dev/null +++ b/packages/parser/src/components/parser/statement/expression/arrayExpression.ts @@ -0,0 +1,30 @@ +import Expression from "."; + +import { TokenTypes } from "../../../../constants/bhaiLangSpec"; +import { NodeType } from "../../../../constants/constants"; +import { ASTNode } from "../../types/nodeTypes"; + + +export default class ArrayExpression extends Expression { + getExpression(): ASTNode { + this._tokenExecutor.eatTokenAndForwardLookahead( + TokenTypes.OPEN_BRACKET_TYPE + ); + + const expressions = []; + while (this._tokenExecutor.getLookahead()?.type != TokenTypes.CLOSED_BRACKET_TYPE) { + expressions.push(Expression.getExpressionImpl(NodeType.LogicalORExpression).getExpression()); + if (this._tokenExecutor.getLookahead()?.type == TokenTypes.COMMA_TYPE) { + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.COMMA_TYPE); + } + } + + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.CLOSED_BRACKET_TYPE); + + return { + type: NodeType.ArrayExpression, + expressions, + }; + } +} + diff --git a/packages/parser/src/components/parser/statement/expression/assignmentExpression.ts b/packages/parser/src/components/parser/statement/expression/assignmentExpression.ts index d7ab232e..9da142b1 100644 --- a/packages/parser/src/components/parser/statement/expression/assignmentExpression.ts +++ b/packages/parser/src/components/parser/statement/expression/assignmentExpression.ts @@ -57,6 +57,7 @@ export default class AssignmentExpression extends Expression { */ private _checkValidAssignmentTarget(node: any) { if (node.type === NodeType.IdentifierExpression) return node; + if (node.type === NodeType.ArrayAccessExpression) return node; throw new SyntaxError("Invalid left hand side in assignment expression"); } diff --git a/packages/parser/src/components/parser/statement/expression/index.ts b/packages/parser/src/components/parser/statement/expression/index.ts index 28849f74..5eea537b 100644 --- a/packages/parser/src/components/parser/statement/expression/index.ts +++ b/packages/parser/src/components/parser/statement/expression/index.ts @@ -27,6 +27,12 @@ export default abstract class Expression { case NodeType.ParanthesizedExpression: return BhaiLangModule.getParanthesizedExpression(); + case NodeType.ArrayExpression: + return BhaiLangModule.getArrayExpression(); + + case NodeType.ArrayAccessExpression: + return BhaiLangModule.getArrayAccessExpression(); + case NodeType.AssignmentExpression: return BhaiLangModule.getAssignmentExpression(); diff --git a/packages/parser/src/components/parser/statement/expression/primaryExpression.ts b/packages/parser/src/components/parser/statement/expression/primaryExpression.ts index 2e40c91e..74826851 100644 --- a/packages/parser/src/components/parser/statement/expression/primaryExpression.ts +++ b/packages/parser/src/components/parser/statement/expression/primaryExpression.ts @@ -15,6 +15,10 @@ export default class PrimaryExpression extends Expression { return Expression.getExpressionImpl( NodeType.ParanthesizedExpression ).getExpression(); + case TokenTypes.OPEN_BRACKET_TYPE: + return Expression.getExpressionImpl( + NodeType.ArrayExpression + ).getExpression(); case TokenTypes.STRING_TYPE: case TokenTypes.NUMBER_TYPE: case TokenTypes.BOOLEAN_TYPE: @@ -33,7 +37,7 @@ export default class PrimaryExpression extends Expression { private _getLeftHandSideExpression() { return Expression.getExpressionImpl( - NodeType.IdentifierExpression + NodeType.ArrayAccessExpression ).getExpression(); } } diff --git a/packages/parser/src/constants/bhaiLangSpec.ts b/packages/parser/src/constants/bhaiLangSpec.ts index 76537271..84455ab5 100644 --- a/packages/parser/src/constants/bhaiLangSpec.ts +++ b/packages/parser/src/constants/bhaiLangSpec.ts @@ -33,6 +33,10 @@ export const TokenTypes = { CLOSED_PARENTHESIS_TYPE: ")", + OPEN_BRACKET_TYPE: "[", + + CLOSED_BRACKET_TYPE: "]", + COMMA_TYPE: ",", NUMBER_TYPE: "NUMBER", @@ -76,6 +80,8 @@ export const SPEC = [ { regex: /^\}/, tokenType: TokenTypes.CLOSED_CURLY_BRACE_TYPE }, { regex: /^\(/, tokenType: TokenTypes.OPEN_PARENTHESIS_TYPE }, { regex: /^\)/, tokenType: TokenTypes.CLOSED_PARENTHESIS_TYPE }, + { regex: /^\[/, tokenType: TokenTypes.OPEN_BRACKET_TYPE }, + { regex: /^\]/, tokenType: TokenTypes.CLOSED_BRACKET_TYPE }, { regex: /^,/, tokenType: TokenTypes.COMMA_TYPE }, //Keywords diff --git a/packages/parser/src/constants/constants.ts b/packages/parser/src/constants/constants.ts index f547d517..a14d7ee6 100644 --- a/packages/parser/src/constants/constants.ts +++ b/packages/parser/src/constants/constants.ts @@ -1,5 +1,7 @@ export const NodeType = { AdditiveExpression: "AdditiveExpression", + ArrayExpression: "ArrayExpression", + ArrayAccessExpression: "ArrayAccessExpression", MultiplicativeExpression: "MultiplicativeExpression", PrimaryExpression: "PrimaryExpression", ParanthesizedExpression: "ParanthesizedExpression", diff --git a/packages/parser/src/module/bhaiLangModule.ts b/packages/parser/src/module/bhaiLangModule.ts index 6ddf3199..4d14b811 100644 --- a/packages/parser/src/module/bhaiLangModule.ts +++ b/packages/parser/src/module/bhaiLangModule.ts @@ -7,6 +7,10 @@ import ContinueStatement import EmptyStatement from "../components/parser/statement/emptyStatement"; import AdditiveExpression from "../components/parser/statement/expression/addititveExpression"; +import ArrayExpression + from "../components/parser/statement/expression/arrayExpression"; +import ArrayAccessExpression + from "../components/parser/statement/expression/arrayAccessExpression"; import AssignmentExpression from "../components/parser/statement/expression/assignmentExpression"; import EqualityExpression @@ -62,6 +66,8 @@ export default class BhaiLangModule { private static _additiveExpression?: AdditiveExpression; private static _multiplicativeExpression?: MultiplicativeExpression; private static _primaryExpression?: PrimaryExpression; + private static _arrayExpression?: ArrayExpression; + private static _arrayAccessExpression?: ArrayAccessExpression; private static _paranthesizedExpression?: ParanthesizedExpression; private static _numericLiteral?: NumericLiteral; private static _stringLiteral?: StringLiteral; @@ -226,6 +232,26 @@ export default class BhaiLangModule { return this._paranthesizedExpression; } + static getArrayAccessExpression() { + if (!this._arrayAccessExpression) { + this._arrayAccessExpression = new ArrayAccessExpression( + this.getTokenExecutor() + ); + } + + return this._arrayAccessExpression; + } + + static getArrayExpression() { + if (!this._arrayExpression) { + this._arrayExpression = new ArrayExpression( + this.getTokenExecutor() + ); + } + + return this._arrayExpression; + } + static getIndentifierExpression() { if (!this._idetifierExpression) this._idetifierExpression = new IdentifierExpression( From eb5584c444a6b312b32a8381b41fd077fe22449a Mon Sep 17 00:00:00 2001 From: Aditya Anchuri Date: Sun, 29 Jan 2023 04:31:14 -0800 Subject: [PATCH 2/5] Add array-index assignment --- packages/interpreter/src/components/scope.ts | 17 +++++ .../visitor/arrayAccessExpression.ts | 4 +- .../visitor/assignmentExpression.ts | 64 +++++++++++++++---- 3 files changed, 69 insertions(+), 16 deletions(-) diff --git a/packages/interpreter/src/components/scope.ts b/packages/interpreter/src/components/scope.ts index af224345..0029786c 100644 --- a/packages/interpreter/src/components/scope.ts +++ b/packages/interpreter/src/components/scope.ts @@ -64,6 +64,23 @@ export default class Scope { ); } + assignArray(identifier: string, index: number, value: unknown) { + if (this._variables.has(identifier)) { + let arr: any[] = this._variables.get(identifier) as any[]; + arr[index] = value; + return; + } + + if (this._parentScope !== null) { + this._parentScope.assignArray(identifier, index, value); + return; + } + + throw new RuntimeException( + `Variable "${identifier}" bana to le pehle fir assign karna.` + ); + } + declare(identifier: string, value: unknown) { if (this._variables.has(identifier)) { throw new RuntimeException( diff --git a/packages/interpreter/src/components/visitor/arrayAccessExpression.ts b/packages/interpreter/src/components/visitor/arrayAccessExpression.ts index ce7d00c8..e9ba0ddd 100644 --- a/packages/interpreter/src/components/visitor/arrayAccessExpression.ts +++ b/packages/interpreter/src/components/visitor/arrayAccessExpression.ts @@ -13,13 +13,13 @@ export default class ArrayAccessExpression implements Visitor { const indexValue = InterpreterModule.getVisitor(index.type).visitNode(index); if (typeof indexValue !== 'number') { throw new RuntimeException( - `Bhai, kuch phat gaya: ${identifier.name} mein dekhne ke liye number mangta hai, ${indexValue} nahi.` + `Bhai, kuch phat gaya: \`${identifier.name}\` array mein dekhne ke liye number mangta hai, "${indexValue}" nahi.` ); } if (!Array.isArray(array)) { throw new RuntimeException( - `Bhai, kuch phat gaya: ${identifier.name} toh array hi nahi hai.` + `Bhai, kuch phat gaya: \`${identifier.name}\` toh array hi nahi hai.` ); } diff --git a/packages/interpreter/src/components/visitor/assignmentExpression.ts b/packages/interpreter/src/components/visitor/assignmentExpression.ts index 8fc09cf7..8dbf5a99 100644 --- a/packages/interpreter/src/components/visitor/assignmentExpression.ts +++ b/packages/interpreter/src/components/visitor/assignmentExpression.ts @@ -15,8 +15,26 @@ export default class AssignmentExpression implements Visitor { `left node not present while executing: ${node.type}` ); - let identifier = node.left.name; + let identifier: string | undefined; + let index: number | undefined; let value: unknown; + + if (node.left.expressions) { + // must be an array access expression on LHS + identifier = node.left.expressions?.[0]!.name; + const indexNode = node.left.expressions?.[1]!; + const indexVal = InterpreterModule.getVisitor(indexNode.type).visitNode(indexNode); + if (typeof indexVal !== 'number') { + throw new RuntimeException( + `Bhai, kuch phat gaya: \`${identifier}\` array mein dekhne ke liye number mangta hai, "${indexVal}" nahi.` + ); + } + index = indexVal as number; + } else { + // regular identifier LHS + identifier = node.left.name; + } + const currentScope = InterpreterModule.getCurrentScope(); if (node.right) { @@ -26,25 +44,43 @@ export default class AssignmentExpression implements Visitor { } if (identifier && node.operator) { - const left = currentScope.get(identifier); + if (index) { + // assign array + const array: any[] = currentScope.get(identifier) as any[]; + if (!Array.isArray(array)) { + throw new RuntimeException( + `Bhai, kuch phat gaya: \`${identifier}\` toh array hi nahi hai.` + ); + } - if (left === null && node.operator !== "=") - throw new NallaPointerException( - `Nalla operand ni jamta "${node.operator}" ke sath` - ); + const newValue = this._getNewValue(array[index], value, node.operator); + currentScope.assignArray(identifier, index, newValue); - if ((left === true || left === false) && node.operator !== "=") - throw new RuntimeException( - `Boolean operand ni jamta "${node.operator}" ke sath` - ); + const updatedArray = currentScope.get(identifier) as any[]; + return updatedArray[index]; + } - const newValue = getOperationValue( - { left: currentScope.get(identifier), right: value }, - node.operator - ); + const newValue = this._getNewValue(currentScope.get(identifier), value, node.operator); currentScope.assign(identifier, newValue); return currentScope.get(identifier); } } + + _getNewValue(left: unknown, value: unknown, operator: string): unknown { + if (left === null && operator !== "=") + throw new NallaPointerException( + `Nalla operand ni jamta "${operator}" ke sath` + ); + + if ((left === true || left === false) && operator !== "=") + throw new RuntimeException( + `Boolean operand ni jamta "${operator}" ke sath` + ); + + return getOperationValue( + { left: left, right: value }, + operator + ); + } } From da8e83ca37923211d2398419f9f923f5a13188d6 Mon Sep 17 00:00:00 2001 From: Aditya Anchuri Date: Sun, 29 Jan 2023 14:59:18 -0800 Subject: [PATCH 3/5] Add array length expression --- .../visitor/arrayAccessExpression.ts | 2 -- .../visitor/arrayLengthExpression.ts | 21 ++++++++++++++++ .../visitor/assignmentExpression.ts | 2 +- .../src/module/interpreterModule.ts | 2 ++ .../test/integration/negativeTestsProvider.ts | 13 ++++++++++ .../test/integration/positiveTestsProvider.ts | 22 +++++++++++++++- .../expression/arrayLengthExpression.ts | 25 +++++++++++++++++++ .../expression/assignmentExpression.ts | 2 +- .../parser/statement/expression/index.ts | 3 +++ .../statement/expression/primaryExpression.ts | 2 +- packages/parser/src/constants/bhaiLangSpec.ts | 3 +++ packages/parser/src/constants/constants.ts | 1 + packages/parser/src/module/bhaiLangModule.ts | 13 ++++++++++ 13 files changed, 105 insertions(+), 6 deletions(-) create mode 100644 packages/interpreter/src/components/visitor/arrayLengthExpression.ts create mode 100644 packages/parser/src/components/parser/statement/expression/arrayLengthExpression.ts diff --git a/packages/interpreter/src/components/visitor/arrayAccessExpression.ts b/packages/interpreter/src/components/visitor/arrayAccessExpression.ts index e9ba0ddd..5e02d65d 100644 --- a/packages/interpreter/src/components/visitor/arrayAccessExpression.ts +++ b/packages/interpreter/src/components/visitor/arrayAccessExpression.ts @@ -26,5 +26,3 @@ export default class ArrayAccessExpression implements Visitor { return array[indexValue]; } } - - diff --git a/packages/interpreter/src/components/visitor/arrayLengthExpression.ts b/packages/interpreter/src/components/visitor/arrayLengthExpression.ts new file mode 100644 index 00000000..1a66b0c7 --- /dev/null +++ b/packages/interpreter/src/components/visitor/arrayLengthExpression.ts @@ -0,0 +1,21 @@ +import Visitor from "."; +import { ASTNode } from "bhai-lang-parser"; + +import RuntimeException from "../../exceptions/runtimeException"; +import InterpreterModule from "../../module/interpreterModule"; + +export default class ArrayLengthExpression implements Visitor { + visitNode(node: ASTNode) { + const identifier = node.expressions?.[0]!; + + const array = InterpreterModule.getVisitor(identifier.type).visitNode(identifier); + if (!Array.isArray(array)) { + throw new RuntimeException( + `Bhai, kuch phat gaya: \`${identifier.name}\` toh array hi nahi hai.` + ); + } + + return array.length; + } +} + diff --git a/packages/interpreter/src/components/visitor/assignmentExpression.ts b/packages/interpreter/src/components/visitor/assignmentExpression.ts index 8dbf5a99..1103b52a 100644 --- a/packages/interpreter/src/components/visitor/assignmentExpression.ts +++ b/packages/interpreter/src/components/visitor/assignmentExpression.ts @@ -44,7 +44,7 @@ export default class AssignmentExpression implements Visitor { } if (identifier && node.operator) { - if (index) { + if (index !== undefined) { // assign array const array: any[] = currentScope.get(identifier) as any[]; if (!Array.isArray(array)) { diff --git a/packages/interpreter/src/module/interpreterModule.ts b/packages/interpreter/src/module/interpreterModule.ts index 38942406..635087a6 100644 --- a/packages/interpreter/src/module/interpreterModule.ts +++ b/packages/interpreter/src/module/interpreterModule.ts @@ -5,6 +5,7 @@ import Scope from "../components/scope"; import Visitor from "../components/visitor"; import ArrayExpression from "../components/visitor/arrayExpression"; import ArrayAccessExpression from "../components/visitor/arrayAccessExpression"; +import ArrayLengthExpression from "../components/visitor/arrayLengthExpression"; import AssignmentExpression from "../components/visitor/assignmentExpression"; import BinaryExpression from "../components/visitor/binaryExpression"; import BlockStatement from "../components/visitor/blockStatement"; @@ -39,6 +40,7 @@ export default class InterpreterModule { [NodeType.VariableDeclaration]: new VariableDeclaration(), [NodeType.ArrayExpression]: new ArrayExpression(), [NodeType.ArrayAccessExpression]: new ArrayAccessExpression(), + [NodeType.ArrayLengthExpression]: new ArrayLengthExpression(), [NodeType.AssignmentExpression]: new AssignmentExpression(), [NodeType.AssignmentExpression]: new AssignmentExpression(), [NodeType.ExpressionStatement]: new ExpressionStatement(), diff --git a/packages/interpreter/test/integration/negativeTestsProvider.ts b/packages/interpreter/test/integration/negativeTestsProvider.ts index 2c138ebe..b99a11e1 100644 --- a/packages/interpreter/test/integration/negativeTestsProvider.ts +++ b/packages/interpreter/test/integration/negativeTestsProvider.ts @@ -528,4 +528,17 @@ export const NegativeTestCases = [ `, output: RuntimeException, }, + { + name: "array invalid index", + input: ` + hi bhai + bhai ye hai a = [-1, 0, 3, 5]; + bhai ye hai k = "invalid"; + a[k] = -5; + + bol bhai a; + bye bhai + `, + output: RuntimeException + } ]; diff --git a/packages/interpreter/test/integration/positiveTestsProvider.ts b/packages/interpreter/test/integration/positiveTestsProvider.ts index 052a2c57..8d5a7971 100644 --- a/packages/interpreter/test/integration/positiveTestsProvider.ts +++ b/packages/interpreter/test/integration/positiveTestsProvider.ts @@ -1012,7 +1012,27 @@ export const WithMultilineOutputPositiveTests = [ bol bhai a[3]; bye bhai `, - outputs: [`1,4,3`, `1`, `4`, `3`], + outputs: [`1,4,3`, `1`, `4`, `3`] + }, + { + name: "array access and length loop test", + input: ` + hi bhai + bhai ye hai a = [-1, 0, 3, 5]; + a[3] = -5; + + bol bhai a; + bol bhai a[1]; + + bhai ye hai i = 0; + jab tak bhai (i < a ka lambai) { + a[i] += 2; + i += 1; + } + bol bhai a; + bye bhai + `, + outputs: [`-1,0,3,-5`,`0`,`1,2,5,-3`] } ]; diff --git a/packages/parser/src/components/parser/statement/expression/arrayLengthExpression.ts b/packages/parser/src/components/parser/statement/expression/arrayLengthExpression.ts new file mode 100644 index 00000000..be1cdefb --- /dev/null +++ b/packages/parser/src/components/parser/statement/expression/arrayLengthExpression.ts @@ -0,0 +1,25 @@ +import Expression from "."; + +import { TokenTypes } from "../../../../constants/bhaiLangSpec"; +import { NodeType } from "../../../../constants/constants"; +import { ASTNode } from "../../types/nodeTypes"; + + +export default class ArrayLengthExpression extends Expression { + getExpression(): ASTNode { + // name of identifier (could be array access itself) + const identifierExpression = Expression.getExpressionImpl(NodeType.ArrayAccessExpression).getExpression(); + + // this is an actual array length expression, otherwise we default to an array access expression + if (this._tokenExecutor.getLookahead()?.type === TokenTypes.KA_LAMBAI) { + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.KA_LAMBAI); + + return { + type: NodeType.ArrayLengthExpression, + expressions: [identifierExpression] + }; + } + + return identifierExpression; + } +} diff --git a/packages/parser/src/components/parser/statement/expression/assignmentExpression.ts b/packages/parser/src/components/parser/statement/expression/assignmentExpression.ts index 9da142b1..8253b058 100644 --- a/packages/parser/src/components/parser/statement/expression/assignmentExpression.ts +++ b/packages/parser/src/components/parser/statement/expression/assignmentExpression.ts @@ -59,6 +59,6 @@ export default class AssignmentExpression extends Expression { if (node.type === NodeType.IdentifierExpression) return node; if (node.type === NodeType.ArrayAccessExpression) return node; - throw new SyntaxError("Invalid left hand side in assignment expression"); + throw new SyntaxError(`bhai kya kar raha hai tu?? Left side mein ${node.type} ho hi nahi sakta`); } } diff --git a/packages/parser/src/components/parser/statement/expression/index.ts b/packages/parser/src/components/parser/statement/expression/index.ts index 5eea537b..8bab98b7 100644 --- a/packages/parser/src/components/parser/statement/expression/index.ts +++ b/packages/parser/src/components/parser/statement/expression/index.ts @@ -33,6 +33,9 @@ export default abstract class Expression { case NodeType.ArrayAccessExpression: return BhaiLangModule.getArrayAccessExpression(); + case NodeType.ArrayLengthExpression: + return BhaiLangModule.getArrayLengthExpression(); + case NodeType.AssignmentExpression: return BhaiLangModule.getAssignmentExpression(); diff --git a/packages/parser/src/components/parser/statement/expression/primaryExpression.ts b/packages/parser/src/components/parser/statement/expression/primaryExpression.ts index 74826851..6a8e4ac5 100644 --- a/packages/parser/src/components/parser/statement/expression/primaryExpression.ts +++ b/packages/parser/src/components/parser/statement/expression/primaryExpression.ts @@ -37,7 +37,7 @@ export default class PrimaryExpression extends Expression { private _getLeftHandSideExpression() { return Expression.getExpressionImpl( - NodeType.ArrayAccessExpression + NodeType.ArrayLengthExpression ).getExpression(); } } diff --git a/packages/parser/src/constants/bhaiLangSpec.ts b/packages/parser/src/constants/bhaiLangSpec.ts index 84455ab5..a1363947 100644 --- a/packages/parser/src/constants/bhaiLangSpec.ts +++ b/packages/parser/src/constants/bhaiLangSpec.ts @@ -21,6 +21,8 @@ export const TokenTypes = { AGLA_DEKH_BHAI: "agla dekh bhai", + KA_LAMBAI: "ka lambai", + NALLA_TYPE: "NALLA", SEMI_COLON_TYPE: ";", @@ -96,6 +98,7 @@ export const SPEC = [ { regex: /^\bjab tak bhai\b/, tokenType: TokenTypes.JAB_TAK_BHAI }, { regex: /^\bbas kar bhai\b/, tokenType: TokenTypes.BAS_KAR_BHAI }, { regex: /^\bagla dekh bhai\b/, tokenType: TokenTypes.AGLA_DEKH_BHAI }, + { regex: /^\bka lambai\b/, tokenType: TokenTypes.KA_LAMBAI }, // Number { regex: /^[+-]?([\d]*[.])?[\d]+/, tokenType: TokenTypes.NUMBER_TYPE }, diff --git a/packages/parser/src/constants/constants.ts b/packages/parser/src/constants/constants.ts index a14d7ee6..e637f5ba 100644 --- a/packages/parser/src/constants/constants.ts +++ b/packages/parser/src/constants/constants.ts @@ -2,6 +2,7 @@ export const NodeType = { AdditiveExpression: "AdditiveExpression", ArrayExpression: "ArrayExpression", ArrayAccessExpression: "ArrayAccessExpression", + ArrayLengthExpression: "ArrayLengthExpression", MultiplicativeExpression: "MultiplicativeExpression", PrimaryExpression: "PrimaryExpression", ParanthesizedExpression: "ParanthesizedExpression", diff --git a/packages/parser/src/module/bhaiLangModule.ts b/packages/parser/src/module/bhaiLangModule.ts index 4d14b811..e805f039 100644 --- a/packages/parser/src/module/bhaiLangModule.ts +++ b/packages/parser/src/module/bhaiLangModule.ts @@ -11,6 +11,8 @@ import ArrayExpression from "../components/parser/statement/expression/arrayExpression"; import ArrayAccessExpression from "../components/parser/statement/expression/arrayAccessExpression"; +import ArrayLengthExpression + from "../components/parser/statement/expression/arrayLengthExpression"; import AssignmentExpression from "../components/parser/statement/expression/assignmentExpression"; import EqualityExpression @@ -68,6 +70,7 @@ export default class BhaiLangModule { private static _primaryExpression?: PrimaryExpression; private static _arrayExpression?: ArrayExpression; private static _arrayAccessExpression?: ArrayAccessExpression; + private static _arrayLengthExpression?: ArrayLengthExpression; private static _paranthesizedExpression?: ParanthesizedExpression; private static _numericLiteral?: NumericLiteral; private static _stringLiteral?: StringLiteral; @@ -242,6 +245,16 @@ export default class BhaiLangModule { return this._arrayAccessExpression; } + static getArrayLengthExpression() { + if (!this._arrayLengthExpression) { + this._arrayLengthExpression = new ArrayLengthExpression( + this.getTokenExecutor() + ); + } + + return this._arrayLengthExpression; + } + static getArrayExpression() { if (!this._arrayExpression) { this._arrayExpression = new ArrayExpression( From ad77b882a0137186e326cfdfd901be25601642fe Mon Sep 17 00:00:00 2001 From: Aditya Anchuri Date: Sun, 29 Jan 2023 15:32:48 -0800 Subject: [PATCH 4/5] Clean up printing --- .../src/components/visitor/arrayExpression.ts | 1 - .../src/components/visitor/printStatement.ts | 15 +++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/packages/interpreter/src/components/visitor/arrayExpression.ts b/packages/interpreter/src/components/visitor/arrayExpression.ts index b3e74643..1b875f26 100644 --- a/packages/interpreter/src/components/visitor/arrayExpression.ts +++ b/packages/interpreter/src/components/visitor/arrayExpression.ts @@ -15,4 +15,3 @@ export default class ArrayExpression implements Visitor { return values; } } - diff --git a/packages/interpreter/src/components/visitor/printStatement.ts b/packages/interpreter/src/components/visitor/printStatement.ts index 2f8a94de..d0c4c1db 100644 --- a/packages/interpreter/src/components/visitor/printStatement.ts +++ b/packages/interpreter/src/components/visitor/printStatement.ts @@ -19,10 +19,25 @@ export default class PrintStatement implements Visitor { currentNodeOutput = "sahi"; else if (currentNodeOutput === false) currentNodeOutput = "galat"; + else if (Array.isArray(currentNodeOutput)) { + currentNodeOutput = this._getArrayOutput(currentNodeOutput); + } return currentNodeOutput; } ) .join(" "); console.log(value); } + + _getArrayOutput(values: any[]): string { + let output = values.map((value) => { + if (Array.isArray(value)) { + return this._getArrayOutput(value); + } else { + return value; + } + }); + + return "[" + output.join(", ") + "]"; + } } From 0270adbc7c543c3c2cad600d285c154160f68f0a Mon Sep 17 00:00:00 2001 From: Aditya Anchuri Date: Sun, 29 Jan 2023 15:33:00 -0800 Subject: [PATCH 5/5] Ensure support for inner arrays --- .../test/integration/positiveTestsProvider.ts | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/packages/interpreter/test/integration/positiveTestsProvider.ts b/packages/interpreter/test/integration/positiveTestsProvider.ts index 8d5a7971..44eb5a91 100644 --- a/packages/interpreter/test/integration/positiveTestsProvider.ts +++ b/packages/interpreter/test/integration/positiveTestsProvider.ts @@ -1012,7 +1012,7 @@ export const WithMultilineOutputPositiveTests = [ bol bhai a[3]; bye bhai `, - outputs: [`1,4,3`, `1`, `4`, `3`] + outputs: [`[1, 4, 3]`, `1`, `4`, `3`] }, { name: "array access and length loop test", @@ -1032,7 +1032,20 @@ export const WithMultilineOutputPositiveTests = [ bol bhai a; bye bhai `, - outputs: [`-1,0,3,-5`,`0`,`1,2,5,-3`] + outputs: [`[-1, 0, 3, -5]`,`0`,`[1, 2, 5, -3]`] + }, + { + name: "inner arrays test", + input: ` + hi bhai + bhai ye hai a = [-1, 0, 3, [1, 2]]; + bhai ye hai k = a[3]; + k[0] = -5; + + bol bhai a; + bye bhai + `, + outputs: [`[-1, 0, 3, [-5, 2]]`] } ];