From c18ae59f3944883b16c256a2d4d0d6f48eca76a7 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Fri, 18 Mar 2022 00:44:32 +0530 Subject: [PATCH 01/22] added Callable Function --- .../expression/callableExpression.ts | 36 +++++++++++++++++++ .../parser/statement/expression/index.ts | 4 +++ .../statement/expression/primaryExpression.ts | 4 +++ .../src/components/parser/types/nodeTypes.ts | 2 +- packages/parser/src/constants/bhaiLangSpec.ts | 8 +++++ packages/parser/src/constants/constants.ts | 2 ++ packages/parser/src/module/bhaiLangModule.ts | 12 +++++++ 7 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 packages/parser/src/components/parser/statement/expression/callableExpression.ts diff --git a/packages/parser/src/components/parser/statement/expression/callableExpression.ts b/packages/parser/src/components/parser/statement/expression/callableExpression.ts new file mode 100644 index 00000000..806749e5 --- /dev/null +++ b/packages/parser/src/components/parser/statement/expression/callableExpression.ts @@ -0,0 +1,36 @@ +import Expression from "."; + +import { TokenTypes } from "../../../../constants/bhaiLangSpec"; +import { NodeType } from "../../../../constants/constants"; +import { ASTNode } from "../../types/nodeTypes"; + +export default class CallableExpression extends Expression { + getExpression(): ASTNode { + console.log("CallableExpression.getExpression()"); + + const name = this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.CALLABLE_TYPE).value; + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.OPEN_PARENTHESIS_TYPE); + + // read arguments + let args: any[] = []; + if(this._tokenExecutor.getLookahead()?.type !== TokenTypes.CLOSED_PARENTHESIS_TYPE) { + do { + args.push(this._getArgs()); + } while ( + this._tokenExecutor.getLookahead()?.type === TokenTypes.COMMA_TYPE && + this._tokenExecutor.getLookahead()?.type !== TokenTypes.CLOSED_PARENTHESIS_TYPE&& + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.COMMA_TYPE) + ); + } + + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.CLOSED_PARENTHESIS_TYPE); + return { + type: NodeType.CallableExpression, + name, + args, + }; + } + private _getArgs() { + return Expression.getExpressionImpl(NodeType.AssignmentExpression).getExpression(); + } +} diff --git a/packages/parser/src/components/parser/statement/expression/index.ts b/packages/parser/src/components/parser/statement/expression/index.ts index 28849f74..0ff6135d 100644 --- a/packages/parser/src/components/parser/statement/expression/index.ts +++ b/packages/parser/src/components/parser/statement/expression/index.ts @@ -41,6 +41,10 @@ export default abstract class Expression { case NodeType.RelationalExpression: return BhaiLangModule.getRelationalExpression(); + + case NodeType.CallableExpression: + return BhaiLangModule.getCallableExpression(); + default: return BhaiLangModule.getIndentifierExpression(); diff --git a/packages/parser/src/components/parser/statement/expression/primaryExpression.ts b/packages/parser/src/components/parser/statement/expression/primaryExpression.ts index 2e40c91e..c54cd550 100644 --- a/packages/parser/src/components/parser/statement/expression/primaryExpression.ts +++ b/packages/parser/src/components/parser/statement/expression/primaryExpression.ts @@ -21,6 +21,10 @@ export default class PrimaryExpression extends Expression { return Literal.getLiteralImpl(token.type).getLiteral(); case TokenTypes.NALLA_TYPE: return this._getNallaLiteral(); + case TokenTypes.CALLABLE_TYPE: + return Expression.getExpressionImpl( + NodeType.CallableExpression + ).getExpression(); default: return this._getLeftHandSideExpression(); } diff --git a/packages/parser/src/components/parser/types/nodeTypes.ts b/packages/parser/src/components/parser/types/nodeTypes.ts index 1acb2722..a3397ff1 100644 --- a/packages/parser/src/components/parser/types/nodeTypes.ts +++ b/packages/parser/src/components/parser/types/nodeTypes.ts @@ -13,5 +13,5 @@ export type ASTNode = { declarations?: ASTNode[]; test?: ASTNode; consequent?: ASTNode; - alternate?: ASTNode; + args?: ASTNode[]; }; diff --git a/packages/parser/src/constants/bhaiLangSpec.ts b/packages/parser/src/constants/bhaiLangSpec.ts index b169e96b..bf4a5032 100644 --- a/packages/parser/src/constants/bhaiLangSpec.ts +++ b/packages/parser/src/constants/bhaiLangSpec.ts @@ -19,6 +19,8 @@ export const TokenTypes = { AGLA_DEKH_BHAI: "agla dekh bhai", + FUNDA: "funda", //functional programming + NALLA_TYPE: "NALLA", SEMI_COLON_TYPE: ";", @@ -37,6 +39,8 @@ export const TokenTypes = { IDENTIFIER_TYPE: "IDENTIFIER", + CALLABLE_TYPE: "CALLABLE", + SIMPLE_ASSIGN_TYPE: "SIMPLE_ASSIGN", COMPLEX_ASSIGN_TYPE: "COMPLEX_ASSIGN", @@ -88,6 +92,10 @@ export const SPEC = [ { regex: /^\bbas kar bhai\b/, tokenType: TokenTypes.BAS_KAR_BHAI }, { regex: /^\bagla dekh bhai\b/, tokenType: TokenTypes.AGLA_DEKH_BHAI }, + //functional programming + { regex: /^\bfunda\b/, tokenType: TokenTypes.FUNDA }, + { regex: /\w+(?=\(.*\))/, tokenType: TokenTypes.CALLABLE_TYPE }, + // Number { regex: /^-?\d+/, tokenType: TokenTypes.NUMBER_TYPE }, diff --git a/packages/parser/src/constants/constants.ts b/packages/parser/src/constants/constants.ts index f547d517..fcb2407b 100644 --- a/packages/parser/src/constants/constants.ts +++ b/packages/parser/src/constants/constants.ts @@ -11,6 +11,7 @@ export const NodeType = { LogicalORExpression: "LogicalORExpression", RelationalExpression: "RelationalExpression", EqualityExpression: "EqualityExpression", + CallableExpression: "CallableExpression", BlockStatement: "BlockStatement", EmptyStatement: "EmptyStatement", ExpressionStatement: "ExpressionStatement", @@ -26,5 +27,6 @@ export const NodeType = { StringLiteral: "StringLiteral", NullLiteral: "NullLiteral", VariableDeclaration: "VariableDeclaration", + FunctionDeclaration: "FunctionDeclaration", Program: "Program", } as const; diff --git a/packages/parser/src/module/bhaiLangModule.ts b/packages/parser/src/module/bhaiLangModule.ts index db2ecf40..677f59b9 100644 --- a/packages/parser/src/module/bhaiLangModule.ts +++ b/packages/parser/src/module/bhaiLangModule.ts @@ -9,6 +9,8 @@ import AdditiveExpression from "../components/parser/statement/expression/addititveExpression"; import AssignmentExpression from "../components/parser/statement/expression/assignmentExpression"; +import CallableExpression + from "../components/parser/statement/expression/callableExpression"; import EqualityExpression from "../components/parser/statement/expression/equalityExpression"; import IdentifierExpression @@ -69,6 +71,7 @@ export default class BhaiLangModule { private static _variableStatement?: VariableStatement; private static _ifStatement?: IfStatement; private static _assignmentExpression?: AssignmentExpression; + private static _callableExpression?: CallableExpression; private static _booleanLiteral?: BooleanLiteral; private static _nullLiteral?: NullLiteral; private static _equalityExpression?: EqualityExpression; @@ -280,6 +283,15 @@ export default class BhaiLangModule { return this._assignmentExpression; } + static getCallableExpression() { + if (!this._callableExpression) + this._callableExpression = new CallableExpression( + this.getTokenExecutor() + ); + + return this._callableExpression; + } + static getNumericLiteral() { if (!this._numericLiteral) { this._numericLiteral = new NumericLiteral(this.getTokenExecutor()); From 81b60daf7f01f6a3639008689bdcca44cfeff6af Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Fri, 18 Mar 2022 01:40:09 +0530 Subject: [PATCH 02/22] added function declaration --- .../parser/statement/functionStatement.ts | 54 +++++++++++++++++++ .../src/components/parser/statement/index.ts | 2 + .../src/components/parser/types/nodeTypes.ts | 2 + packages/parser/src/constants/bhaiLangSpec.ts | 4 +- packages/parser/src/constants/constants.ts | 1 + packages/parser/src/module/bhaiLangModule.ts | 12 +++++ 6 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 packages/parser/src/components/parser/statement/functionStatement.ts diff --git a/packages/parser/src/components/parser/statement/functionStatement.ts b/packages/parser/src/components/parser/statement/functionStatement.ts new file mode 100644 index 00000000..4023a112 --- /dev/null +++ b/packages/parser/src/components/parser/statement/functionStatement.ts @@ -0,0 +1,54 @@ +import Statement from "."; + +import { TokenTypes } from "../../../constants/bhaiLangSpec"; +import { NodeType } from "../../../constants/constants"; +import TokenExecutor from "../tokenExecutor"; +import { ASTNode } from "../types/nodeTypes"; + +import Expression from "./expression"; +import NullLiteral from "./expression/literals/nullLiteral"; + +export default class FunctionStatement extends Statement { + _nullLiteral: NullLiteral; + + constructor(tokenExecutor: TokenExecutor, nullLiteral: NullLiteral) { + super(tokenExecutor); + this._nullLiteral = nullLiteral; + } + + getStatement(): ASTNode { + this._tokenExecutor.eatTokenAndForwardLookahead( + TokenTypes.FUNDA_TYPE + ); + + const declaration = this._getFunctionDeclaration(); + + // this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.SEMI_COLON_TYPE); + + return { + type: NodeType.FunctionStatement, + declaration, + }; + } + + + private _getFunctionDeclaration(): ASTNode { + const id = Expression.getExpressionImpl( + NodeType.CallableExpression + ).getExpression(); + let lookahead=this._tokenExecutor.getLookahead() + if (lookahead == null) { + throw new SyntaxError(`Unexpected end of "jab tak bhai" statement`); + } + if(lookahead.type!==TokenTypes.OPEN_CURLY_BRACE_TYPE){ + throw new SyntaxError(`Unexpected token after funda signature ${id.name}, got "${lookahead.value}" : expected "{"`); + } + const body=Statement.getStatementImpl(this._tokenExecutor.getLookahead()!).getStatement(); + + return { + type: NodeType.FunctionDeclaration, + id, + body + }; + } +} diff --git a/packages/parser/src/components/parser/statement/index.ts b/packages/parser/src/components/parser/statement/index.ts index e03106e5..4004b2ba 100644 --- a/packages/parser/src/components/parser/statement/index.ts +++ b/packages/parser/src/components/parser/statement/index.ts @@ -39,6 +39,8 @@ export default abstract class Statement { case TokenTypes.AGLA_DEKH_BHAI: return BhaiLangModule.getContinueStatement(); + case TokenTypes.FUNDA_TYPE: + return BhaiLangModule.getFunctionStatement(); default: return BhaiLangModule.getExpressionStatement(); diff --git a/packages/parser/src/components/parser/types/nodeTypes.ts b/packages/parser/src/components/parser/types/nodeTypes.ts index a3397ff1..8b0ec693 100644 --- a/packages/parser/src/components/parser/types/nodeTypes.ts +++ b/packages/parser/src/components/parser/types/nodeTypes.ts @@ -11,7 +11,9 @@ export type ASTNode = { id?: ASTNode; init?: ASTNode | null; declarations?: ASTNode[]; + declaration?: ASTNode; test?: ASTNode; consequent?: ASTNode; + alternate?: ASTNode; args?: ASTNode[]; }; diff --git a/packages/parser/src/constants/bhaiLangSpec.ts b/packages/parser/src/constants/bhaiLangSpec.ts index bf4a5032..927ce8c7 100644 --- a/packages/parser/src/constants/bhaiLangSpec.ts +++ b/packages/parser/src/constants/bhaiLangSpec.ts @@ -19,7 +19,7 @@ export const TokenTypes = { AGLA_DEKH_BHAI: "agla dekh bhai", - FUNDA: "funda", //functional programming + FUNDA_TYPE: "bhai ye apna funda", //functional programming NALLA_TYPE: "NALLA", @@ -93,7 +93,7 @@ export const SPEC = [ { regex: /^\bagla dekh bhai\b/, tokenType: TokenTypes.AGLA_DEKH_BHAI }, //functional programming - { regex: /^\bfunda\b/, tokenType: TokenTypes.FUNDA }, + { regex: /^\bapna funda\b/, tokenType: TokenTypes.FUNDA_TYPE }, { regex: /\w+(?=\(.*\))/, tokenType: TokenTypes.CALLABLE_TYPE }, // Number diff --git a/packages/parser/src/constants/constants.ts b/packages/parser/src/constants/constants.ts index fcb2407b..b7c3b35a 100644 --- a/packages/parser/src/constants/constants.ts +++ b/packages/parser/src/constants/constants.ts @@ -27,6 +27,7 @@ export const NodeType = { StringLiteral: "StringLiteral", NullLiteral: "NullLiteral", VariableDeclaration: "VariableDeclaration", + FunctionStatement: "FunctionStatement", FunctionDeclaration: "FunctionDeclaration", Program: "Program", } as const; diff --git a/packages/parser/src/module/bhaiLangModule.ts b/packages/parser/src/module/bhaiLangModule.ts index 677f59b9..a9049cdc 100644 --- a/packages/parser/src/module/bhaiLangModule.ts +++ b/packages/parser/src/module/bhaiLangModule.ts @@ -37,6 +37,7 @@ import RelationalExpression from "../components/parser/statement/expression/relationalExpression"; import ExpressionStatement from "../components/parser/statement/expressionStatement"; +import FunctionStatement from "../components/parser/statement/functionStatement"; import IfStatement from "../components/parser/statement/ifStatement"; import InitStatement from "../components/parser/statement/initStatement"; import PrintStatement from "../components/parser/statement/printStatement"; @@ -81,6 +82,7 @@ export default class BhaiLangModule { private static _breakStatement?: BreakStatement; private static _continueStatement?: ContinueStatement; private static _whileStatement?: WhileStatement; + private static _functionStatement: FunctionStatement; static getTokenizer() { if (!this._tokenizer) this._tokenizer = new TokenizerImpl(SPEC); @@ -190,6 +192,16 @@ export default class BhaiLangModule { return this._variableStatement; } + static getFunctionStatement() { + if (!this._functionStatement) + this._functionStatement = new FunctionStatement( + this.getTokenExecutor(), + this.getNullLiteral() + ); + + return this._functionStatement; + } + static getAdditiveExpression() { if (!this._additiveExpression) { From 507de147ed38dff628b4a6bd1acfed3d15dd97f2 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Fri, 18 Mar 2022 13:15:09 +0530 Subject: [PATCH 03/22] return statement added in parser --- packages/interpreter/src/components/scope.ts | 21 +++++++++ .../components/visitor/callableExpression.ts | 33 ++++++++++++++ .../components/visitor/functionDeclaration.ts | 43 +++++++++++++++++++ .../components/visitor/functionStatement.ts | 15 +++++++ .../src/module/interpreterModule.ts | 7 +++ .../expression/callableExpression.ts | 2 - .../src/components/parser/statement/index.ts | 2 + .../parser/statement/returnStatement.ts | 25 +++++++++++ packages/parser/src/constants/bhaiLangSpec.ts | 5 ++- packages/parser/src/constants/constants.ts | 1 + packages/parser/src/module/bhaiLangModule.ts | 10 +++++ 11 files changed, 161 insertions(+), 3 deletions(-) create mode 100644 packages/interpreter/src/components/visitor/callableExpression.ts create mode 100644 packages/interpreter/src/components/visitor/functionDeclaration.ts create mode 100644 packages/interpreter/src/components/visitor/functionStatement.ts create mode 100644 packages/parser/src/components/parser/statement/returnStatement.ts diff --git a/packages/interpreter/src/components/scope.ts b/packages/interpreter/src/components/scope.ts index af224345..71096b81 100644 --- a/packages/interpreter/src/components/scope.ts +++ b/packages/interpreter/src/components/scope.ts @@ -4,14 +4,35 @@ import RuntimeException from "../exceptions/runtimeException"; export default class Scope { _variables: Map = new Map(); _isLoop = false; + _isFunction=false; _isBreakStatement = false; _isContinueStatement = false; _parentScope: Scope | null; + _isReturnStatement=false; + _returnVal:any=null; constructor(parentScope: Scope | null) { this._parentScope = parentScope; } + isFunction(){ + return this._isFunction; + } + + setFunction(isFunction:boolean){ + this._isFunction=isFunction; + } + setReturnStatement(isReturnStatement: boolean,returnValue:any) { + this._isReturnStatement=isReturnStatement; + this._returnVal=returnValue; + } + isReturnStatement() { + return this._isReturnStatement; + } + getReturnValue(){ + return this._returnVal; + } + isLoop() { return this._isLoop; } diff --git a/packages/interpreter/src/components/visitor/callableExpression.ts b/packages/interpreter/src/components/visitor/callableExpression.ts new file mode 100644 index 00000000..93437258 --- /dev/null +++ b/packages/interpreter/src/components/visitor/callableExpression.ts @@ -0,0 +1,33 @@ +import Visitor from "."; +import { ASTNode } from "bhai-lang-parser"; + +import InvalidStateException from "../../exceptions/invalidStateException"; +import InterpreterModule from "../../module/interpreterModule"; + +export default class CallableExpression implements Visitor { + visitNode(node: ASTNode) { + if (!node.name) { + throw new InvalidStateException(`Invalid node name for: ${node.type}`); + } + + let value:any = InterpreterModule.getCurrentScope().get(node.name); + let args=[]; + if (value.args) { + for (let i = 0; i < value.args.length; i++) { + if(node.args&&node.args[i]){ + args.push({ + identifier:value.args[i], + value:node.args[i].value + }); + } + else{ + args.push({ + identifier:value.args[i], + value:null + }); + } + } + } + return value.code(args); + } +} diff --git a/packages/interpreter/src/components/visitor/functionDeclaration.ts b/packages/interpreter/src/components/visitor/functionDeclaration.ts new file mode 100644 index 00000000..288e28e0 --- /dev/null +++ b/packages/interpreter/src/components/visitor/functionDeclaration.ts @@ -0,0 +1,43 @@ +import Visitor from "."; +import { ASTNode } from "bhai-lang-parser"; + +import InvalidStateException from "../../exceptions/invalidStateException"; +import InterpreterModule from "../../module/interpreterModule"; +import Scope from "../scope"; + +export default class FunctionDeclaration implements Visitor { + visitNode(node: ASTNode) { + if (!node.id || !node.body||!node) { + throw new InvalidStateException(`id or body not found for ${node.type}`); + } + + const identifier = node.id.name; + + let value; + const body = node.body; + if (body && !Array.isArray(body)) { + let scope=InterpreterModule.getCurrentScope() + value={ + args:node.id.args?.map(arg=>arg.name), + code:(args:{identifier:string,value:any}[]):any=>{ + let oldScope=InterpreterModule.getCurrentScope() + let newScope=new Scope(scope) + args.forEach(arg=>{ + newScope.declare(arg.identifier,arg.value) + }) + newScope.setFunction(true); + InterpreterModule.setCurrentScope(newScope) + InterpreterModule.getVisitor(body.type).visitNode(body); + let result=newScope.getReturnValue() + InterpreterModule.setCurrentScope(new Scope(oldScope)) + return result + } + } + } + const currentScope = InterpreterModule.getCurrentScope(); + + if (identifier) { + currentScope.declare(identifier, value); + } + } +} diff --git a/packages/interpreter/src/components/visitor/functionStatement.ts b/packages/interpreter/src/components/visitor/functionStatement.ts new file mode 100644 index 00000000..b4d3aa90 --- /dev/null +++ b/packages/interpreter/src/components/visitor/functionStatement.ts @@ -0,0 +1,15 @@ +import Visitor from "."; +import { ASTNode } from "bhai-lang-parser"; + +import InvalidStateException from "../../exceptions/invalidStateException"; +import InterpreterModule from "../../module/interpreterModule"; + +export default class FunctionStatement implements Visitor { + visitNode(node: ASTNode) { + if (!node.declaration) + throw new InvalidStateException( + `funda declarations in funda statement is not present: ${node.declaration}` + ); + InterpreterModule.getVisitor(node.declaration.type).visitNode(node.declaration); + } +} diff --git a/packages/interpreter/src/module/interpreterModule.ts b/packages/interpreter/src/module/interpreterModule.ts index adef304b..4d9f593c 100644 --- a/packages/interpreter/src/module/interpreterModule.ts +++ b/packages/interpreter/src/module/interpreterModule.ts @@ -8,9 +8,12 @@ import BinaryExpression from "../components/visitor/binaryExpression"; import BlockStatement from "../components/visitor/blockStatement"; import BooleanLiteral from "../components/visitor/booleanLiteral"; import BreakStatement from "../components/visitor/breakStatement"; +import CallableExpression from "../components/visitor/callableExpression"; import ContinueStatement from "../components/visitor/continueStatement"; import EmptyStatement from "../components/visitor/emptyStatement"; import ExpressionStatement from "../components/visitor/expressionStatement"; +import FunctionDeclaration from "../components/visitor/functionDeclaration"; +import FunctionStatement from "../components/visitor/functionStatement"; import IdentifierExpression from "../components/visitor/identifierExpression"; import IfStatement from "../components/visitor/ifStatement"; import InitStatement from "../components/visitor/initStatement"; @@ -47,6 +50,10 @@ export default class InterpreterModule { [NodeType.WhileStatement]: new WhileStatement(), [NodeType.BreakStatement]: new BreakStatement(), [NodeType.ContinueStatement]: new ContinueStatement(), + [NodeType.FunctionStatement]: new FunctionStatement(), + [NodeType.FunctionDeclaration]: new FunctionDeclaration(), + [NodeType.CallableExpression]: new CallableExpression(), + } as Record; private static _currentScope: Scope; diff --git a/packages/parser/src/components/parser/statement/expression/callableExpression.ts b/packages/parser/src/components/parser/statement/expression/callableExpression.ts index 806749e5..172eea19 100644 --- a/packages/parser/src/components/parser/statement/expression/callableExpression.ts +++ b/packages/parser/src/components/parser/statement/expression/callableExpression.ts @@ -6,8 +6,6 @@ import { ASTNode } from "../../types/nodeTypes"; export default class CallableExpression extends Expression { getExpression(): ASTNode { - console.log("CallableExpression.getExpression()"); - const name = this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.CALLABLE_TYPE).value; this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.OPEN_PARENTHESIS_TYPE); diff --git a/packages/parser/src/components/parser/statement/index.ts b/packages/parser/src/components/parser/statement/index.ts index 4004b2ba..0f5970f7 100644 --- a/packages/parser/src/components/parser/statement/index.ts +++ b/packages/parser/src/components/parser/statement/index.ts @@ -41,6 +41,8 @@ export default abstract class Statement { return BhaiLangModule.getContinueStatement(); case TokenTypes.FUNDA_TYPE: return BhaiLangModule.getFunctionStatement(); + case TokenTypes.RAKH_LE_BHAI: + return BhaiLangModule.getReturnStatement(); default: return BhaiLangModule.getExpressionStatement(); diff --git a/packages/parser/src/components/parser/statement/returnStatement.ts b/packages/parser/src/components/parser/statement/returnStatement.ts new file mode 100644 index 00000000..49b907dd --- /dev/null +++ b/packages/parser/src/components/parser/statement/returnStatement.ts @@ -0,0 +1,25 @@ +import Statement from "."; + +import { TokenTypes } from "../../../constants/bhaiLangSpec"; +import { NodeType } from "../../../constants/constants"; +import { ASTNode } from "../types/nodeTypes"; + +import Expression from "./expression"; + + +export default class ReturnStatement extends Statement { + getStatement(): ASTNode { + + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.RAKH_LE_BHAI); + + const value = Expression.getExpressionImpl( + NodeType.AssignmentExpression + ).getExpression(); + + return { + type: NodeType.RetrunStatement, + expression: value, + }; + } + +} \ No newline at end of file diff --git a/packages/parser/src/constants/bhaiLangSpec.ts b/packages/parser/src/constants/bhaiLangSpec.ts index 927ce8c7..444a8ea6 100644 --- a/packages/parser/src/constants/bhaiLangSpec.ts +++ b/packages/parser/src/constants/bhaiLangSpec.ts @@ -21,6 +21,8 @@ export const TokenTypes = { FUNDA_TYPE: "bhai ye apna funda", //functional programming + RAKH_LE_BHAI:"rakh le bhai",// return statement + NALLA_TYPE: "NALLA", SEMI_COLON_TYPE: ";", @@ -94,7 +96,8 @@ export const SPEC = [ //functional programming { regex: /^\bapna funda\b/, tokenType: TokenTypes.FUNDA_TYPE }, - { regex: /\w+(?=\(.*\))/, tokenType: TokenTypes.CALLABLE_TYPE }, + { regex: /^\brakh le bhai\b/, tokenType: TokenTypes.RAKH_LE_BHAI }, + { regex: /^\w+(?=\(.*\))/, tokenType: TokenTypes.CALLABLE_TYPE }, // Number { regex: /^-?\d+/, tokenType: TokenTypes.NUMBER_TYPE }, diff --git a/packages/parser/src/constants/constants.ts b/packages/parser/src/constants/constants.ts index b7c3b35a..8fe3b878 100644 --- a/packages/parser/src/constants/constants.ts +++ b/packages/parser/src/constants/constants.ts @@ -29,5 +29,6 @@ export const NodeType = { VariableDeclaration: "VariableDeclaration", FunctionStatement: "FunctionStatement", FunctionDeclaration: "FunctionDeclaration", + RetrunStatement: "RetrunStatement", Program: "Program", } as const; diff --git a/packages/parser/src/module/bhaiLangModule.ts b/packages/parser/src/module/bhaiLangModule.ts index a9049cdc..276e5951 100644 --- a/packages/parser/src/module/bhaiLangModule.ts +++ b/packages/parser/src/module/bhaiLangModule.ts @@ -41,6 +41,7 @@ import FunctionStatement from "../components/parser/statement/functionStatement" import IfStatement from "../components/parser/statement/ifStatement"; import InitStatement from "../components/parser/statement/initStatement"; import PrintStatement from "../components/parser/statement/printStatement"; +import ReturnStatement from "../components/parser/statement/returnStatement"; import VariableStatement from "../components/parser/statement/variableStatement"; import WhileStatement from "../components/parser/statement/whileStatement"; @@ -83,6 +84,7 @@ export default class BhaiLangModule { private static _continueStatement?: ContinueStatement; private static _whileStatement?: WhileStatement; private static _functionStatement: FunctionStatement; + static _returnStatement: ReturnStatement; static getTokenizer() { if (!this._tokenizer) this._tokenizer = new TokenizerImpl(SPEC); @@ -201,6 +203,14 @@ export default class BhaiLangModule { return this._functionStatement; } + static getReturnStatement() { + if (!this._returnStatement) + this._returnStatement = new ReturnStatement( + this.getTokenExecutor() + ); + + return this._returnStatement; + } static getAdditiveExpression() { From f3aa56d44041f23996629cd37406fb841d79f8bd Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Fri, 18 Mar 2022 18:17:23 +0530 Subject: [PATCH 04/22] callable functions and function return is working --- .../interpreter/src/components/dataClass.ts | 86 +++++++++++++++++++ packages/interpreter/src/components/scope.ts | 13 +-- .../visitor/assignmentExpression.ts | 7 +- .../components/visitor/binaryExpression.ts | 27 +++--- .../src/components/visitor/blockStatement.ts | 6 ++ .../src/components/visitor/booleanLiteral.ts | 6 +- .../components/visitor/callableExpression.ts | 8 +- .../components/visitor/functionDeclaration.ts | 3 +- .../visitor/identifierExpression.ts | 4 - .../src/components/visitor/ifStatement.ts | 5 +- .../src/components/visitor/index.ts | 3 +- .../src/components/visitor/nullLiteral.ts | 5 +- .../src/components/visitor/numericLiteral.ts | 6 +- .../src/components/visitor/printStatement.ts | 16 ++-- .../src/components/visitor/returnStatement.ts | 19 ++++ .../src/components/visitor/stringLiteral.ts | 6 +- .../components/visitor/variableDeclaration.ts | 7 +- .../src/components/visitor/whileStatement.ts | 4 +- packages/interpreter/src/helpers/index.ts | 69 ++++++++------- .../src/module/interpreterModule.ts | 2 + .../parser/statement/returnStatement.ts | 2 +- packages/parser/src/constants/constants.ts | 2 +- 22 files changed, 228 insertions(+), 78 deletions(-) create mode 100644 packages/interpreter/src/components/dataClass.ts create mode 100644 packages/interpreter/src/components/visitor/returnStatement.ts diff --git a/packages/interpreter/src/components/dataClass.ts b/packages/interpreter/src/components/dataClass.ts new file mode 100644 index 00000000..cffc84d9 --- /dev/null +++ b/packages/interpreter/src/components/dataClass.ts @@ -0,0 +1,86 @@ +export enum DataTypes{ + Null='null', + Boolean='boolean', + Numeric='numeric', + String='string', + Callable='callable', +} +export class DataObject { + protected _value: any; + protected _type:string; + public isDataObject:boolean; + + constructor(value: any,type:string) { + this._value = value; + this._type = type; + this.isDataObject=true; + } + + getValue(): any { + return this._value; + } + getType():string{ + return this._type; + } + getStringValue():string{ + return this._value.toString(); + } +} + +export class BooleanObject extends DataObject{ + constructor(value: boolean) { + super(value,DataTypes.Boolean); + } + getStringValue(): string { + return this._value?"sahi":"galat"; + } +} + +export class NumericObject extends DataObject{ + constructor(value: number) { + super(value,DataTypes.Numeric); + } +} + +export class StringObject extends DataObject{ + constructor(value: string) { + super(value,DataTypes.String); + } +} + +export class NullObject extends DataObject{ + constructor() { + super(null,DataTypes.Null); + } + getStringValue(): string { + return "nalla"; + } +} + +export class CallableObject extends DataObject{ + constructor(value: any) { + super(value,DataTypes.Callable); + } +} + +export function sanatizeData(data:any):DataObject{ + if((data==null)||(data==undefined)){ + return new NullObject(); + } + if(typeof data=='boolean'){ + return new BooleanObject(data); + } + if(typeof data=='number'){ + return new NumericObject(data); + } + if(typeof data=='string'){ + return new StringObject(data); + } + if(typeof data=='function'){ + return new CallableObject(data); + } + if(typeof data=='object' && data.isDataObject){ + return data as DataObject; + } + else throw new Error(`Ye kya kar raha hai: "${data}" sahi nhi hai. ye konsa data type hai bhai`); +} \ No newline at end of file diff --git a/packages/interpreter/src/components/scope.ts b/packages/interpreter/src/components/scope.ts index 71096b81..283aa9b5 100644 --- a/packages/interpreter/src/components/scope.ts +++ b/packages/interpreter/src/components/scope.ts @@ -1,8 +1,9 @@ import RuntimeException from "../exceptions/runtimeException"; +import { DataObject, NullObject, sanatizeData } from "./dataClass"; export default class Scope { - _variables: Map = new Map(); + _variables: Map = new Map(); _isLoop = false; _isFunction=false; _isBreakStatement = false; @@ -30,6 +31,7 @@ export default class Scope { return this._isReturnStatement; } getReturnValue(){ + if(!this._returnVal) this._returnVal=new NullObject(); return this._returnVal; } @@ -57,9 +59,10 @@ export default class Scope { return this._isContinueStatement; } - get(identifier: string): unknown { + get(identifier: string): DataObject { if (this._variables.has(identifier)) { - return this._variables.get(identifier); + let value = sanatizeData(this._variables.get(identifier)); + if(value) return value; } if (this._parentScope !== null) { @@ -69,7 +72,7 @@ export default class Scope { throw new RuntimeException(`Variable "${identifier}" bana to le pehle.`); } - assign(identifier: string, value: unknown) { + assign(identifier: string, value: DataObject) { if (this._variables.has(identifier)) { this._variables.set(identifier, value); return; @@ -85,7 +88,7 @@ export default class Scope { ); } - declare(identifier: string, value: unknown) { + declare(identifier: string, value: DataObject) { if (this._variables.has(identifier)) { throw new RuntimeException( `Variable "${identifier}" pehle se exist karta hai bhai. Check karle.` diff --git a/packages/interpreter/src/components/visitor/assignmentExpression.ts b/packages/interpreter/src/components/visitor/assignmentExpression.ts index 8fc09cf7..2a8d141c 100644 --- a/packages/interpreter/src/components/visitor/assignmentExpression.ts +++ b/packages/interpreter/src/components/visitor/assignmentExpression.ts @@ -6,6 +6,7 @@ import NallaPointerException from "../../exceptions/nallaPointerException"; import RuntimeException from "../../exceptions/runtimeException"; import { getOperationValue } from "../../helpers"; import InterpreterModule from "../../module/interpreterModule"; +import { DataObject, DataTypes, NullObject } from "../dataClass"; export default class AssignmentExpression implements Visitor { @@ -16,7 +17,7 @@ export default class AssignmentExpression implements Visitor { ); let identifier = node.left.name; - let value: unknown; + let value: DataObject|null|void=null; const currentScope = InterpreterModule.getCurrentScope(); if (node.right) { @@ -24,6 +25,7 @@ export default class AssignmentExpression implements Visitor { node.right ); } + if(value==null) value =new NullObject() if (identifier && node.operator) { const left = currentScope.get(identifier); @@ -33,7 +35,7 @@ export default class AssignmentExpression implements Visitor { `Nalla operand ni jamta "${node.operator}" ke sath` ); - if ((left === true || left === false) && node.operator !== "=") + if (left.getType()==DataTypes.Boolean && node.operator !== "=") throw new RuntimeException( `Boolean operand ni jamta "${node.operator}" ke sath` ); @@ -46,5 +48,6 @@ export default class AssignmentExpression implements Visitor { return currentScope.get(identifier); } + } } diff --git a/packages/interpreter/src/components/visitor/binaryExpression.ts b/packages/interpreter/src/components/visitor/binaryExpression.ts index 9ed6c335..db4fa982 100644 --- a/packages/interpreter/src/components/visitor/binaryExpression.ts +++ b/packages/interpreter/src/components/visitor/binaryExpression.ts @@ -6,6 +6,7 @@ import NallaPointerException from "../../exceptions/nallaPointerException"; import RuntimeException from "../../exceptions/runtimeException"; import { getOperationValue } from "../../helpers"; import InterpreterModule from "../../module/interpreterModule"; +import { BooleanObject, DataObject, NullObject, sanatizeData } from "../dataClass"; export default class BinaryExpression implements Visitor { @@ -16,28 +17,32 @@ export default class BinaryExpression implements Visitor { ); } - let left, right; + let left:DataObject=new NullObject(), right:DataObject=new NullObject(); // handling logical & binary both at the same place as both operate on two operands if (node.type == NodeType.BinaryExpression) { this._checkNalla(node); this._checkBoolean(node); - left = InterpreterModule.getVisitor(node.left.type).visitNode( + left = sanatizeData(InterpreterModule.getVisitor(node.left.type).visitNode( node.left - ); - right = InterpreterModule.getVisitor(node.right.type).visitNode( + )); + right = sanatizeData(InterpreterModule.getVisitor(node.right.type).visitNode( node.right - ); + )); } else if (node.type == NodeType.LogicalExpression) { this._checkNalla(node); - left = node.left.type == NodeType.BooleanLiteral ? (node.left.value == "sahi" ? true : false) : InterpreterModule.getVisitor(node.left.type).visitNode( + left = node.left.type == NodeType.BooleanLiteral ? + new BooleanObject(node.left.value == "sahi" ? true : false) + : sanatizeData(InterpreterModule.getVisitor(node.left.type).visitNode( node.left - ); + )); - right = node.right.type == NodeType.BooleanLiteral ? (node.right.value == "sahi" ? true : false) : InterpreterModule.getVisitor(node.right.type).visitNode( + right = node.right.type == NodeType.BooleanLiteral ? + new BooleanObject(node.right.value == "sahi" ? true : false) + : sanatizeData(InterpreterModule.getVisitor(node.right.type).visitNode( node.right - ); + )); } return getOperationValue({ left, right }, node.operator); @@ -91,12 +96,12 @@ export default class BinaryExpression implements Visitor { if (node.left.type === NodeType.IdentifierExpression && node.left.name) { const value = InterpreterModule.getCurrentScope().get(node.left.name); - if (value === true || value === false) throw runtimeException; + if (value.getValue() === true || value.getValue() === false) throw runtimeException; } if (node.right.type === NodeType.IdentifierExpression && node.right.name) { const value = InterpreterModule.getCurrentScope().get(node.right.name); - if (value === true || value === false) throw runtimeException; + if (value.getValue() === true || value.getValue() === false) throw runtimeException; } } } diff --git a/packages/interpreter/src/components/visitor/blockStatement.ts b/packages/interpreter/src/components/visitor/blockStatement.ts index cf0fc382..b7ed87c2 100644 --- a/packages/interpreter/src/components/visitor/blockStatement.ts +++ b/packages/interpreter/src/components/visitor/blockStatement.ts @@ -11,6 +11,7 @@ export default class BlockStatement implements Visitor { InterpreterModule.setCurrentScope(new Scope(parentScope)); InterpreterModule.getCurrentScope().setLoop(parentScope.isLoop()); + InterpreterModule.getCurrentScope().setFunction(parentScope.isFunction()); if (Array.isArray(node.body)) { node.body.every((statement: ASTNode) => { @@ -21,6 +22,11 @@ export default class BlockStatement implements Visitor { parentScope.setContinueStatement(true); return false; } + if(InterpreterModule.getCurrentScope().isReturnStatement()&&parentScope.isFunction()){ + let retValue=InterpreterModule.getCurrentScope().getReturnValue(); + parentScope.setReturnStatement(true,retValue); + return false; + } InterpreterModule.getVisitor(statement.type).visitNode(statement); return true; }); diff --git a/packages/interpreter/src/components/visitor/booleanLiteral.ts b/packages/interpreter/src/components/visitor/booleanLiteral.ts index a41a5bbd..ad3322a7 100644 --- a/packages/interpreter/src/components/visitor/booleanLiteral.ts +++ b/packages/interpreter/src/components/visitor/booleanLiteral.ts @@ -1,8 +1,12 @@ import Visitor from "."; import { ASTNode } from "bhai-lang-parser"; +import RuntimeException from "../../exceptions/runtimeException"; +import { BooleanObject } from "../dataClass"; export default class BooleanLiteral implements Visitor { visitNode(node: ASTNode) { - return node.value; + if(node.value!=="sahi"&&node.value!=="galat") + throw new RuntimeException(`Ye kya kar raha hai: "${node.value}" sahi nhi hai ${node.type} me. isme sahi/galat dal`); + return new BooleanObject(node.value === "sahi" ? true : false); } } diff --git a/packages/interpreter/src/components/visitor/callableExpression.ts b/packages/interpreter/src/components/visitor/callableExpression.ts index 93437258..2b2988db 100644 --- a/packages/interpreter/src/components/visitor/callableExpression.ts +++ b/packages/interpreter/src/components/visitor/callableExpression.ts @@ -3,6 +3,8 @@ import { ASTNode } from "bhai-lang-parser"; import InvalidStateException from "../../exceptions/invalidStateException"; import InterpreterModule from "../../module/interpreterModule"; +import { sanatizeData } from "../dataClass"; +import RuntimeException from "../../exceptions/runtimeException"; export default class CallableExpression implements Visitor { visitNode(node: ASTNode) { @@ -10,7 +12,11 @@ export default class CallableExpression implements Visitor { throw new InvalidStateException(`Invalid node name for: ${node.type}`); } - let value:any = InterpreterModule.getCurrentScope().get(node.name); + let callable = sanatizeData(InterpreterModule.getCurrentScope().get(node.name)); + if (callable.getType() !== "callable") + throw new RuntimeException(`ye kya kar rha tu: ${node.name} to koi funda hai hi nhi, aise nhi chalega`); + + let value=callable.getValue(); let args=[]; if (value.args) { for (let i = 0; i < value.args.length; i++) { diff --git a/packages/interpreter/src/components/visitor/functionDeclaration.ts b/packages/interpreter/src/components/visitor/functionDeclaration.ts index 288e28e0..499cb622 100644 --- a/packages/interpreter/src/components/visitor/functionDeclaration.ts +++ b/packages/interpreter/src/components/visitor/functionDeclaration.ts @@ -4,6 +4,7 @@ import { ASTNode } from "bhai-lang-parser"; import InvalidStateException from "../../exceptions/invalidStateException"; import InterpreterModule from "../../module/interpreterModule"; import Scope from "../scope"; +import { CallableObject } from "../dataClass"; export default class FunctionDeclaration implements Visitor { visitNode(node: ASTNode) { @@ -37,7 +38,7 @@ export default class FunctionDeclaration implements Visitor { const currentScope = InterpreterModule.getCurrentScope(); if (identifier) { - currentScope.declare(identifier, value); + currentScope.declare(identifier, new CallableObject(value)); } } } diff --git a/packages/interpreter/src/components/visitor/identifierExpression.ts b/packages/interpreter/src/components/visitor/identifierExpression.ts index 16e0270d..4777d656 100644 --- a/packages/interpreter/src/components/visitor/identifierExpression.ts +++ b/packages/interpreter/src/components/visitor/identifierExpression.ts @@ -12,10 +12,6 @@ export default class IdentifierExpression implements Visitor { let value = InterpreterModule.getCurrentScope().get(node.name); - if (value === null) value = "nalla"; - else if (value === true) value = "sahi"; - else if (value === false) value = "galat"; - return value; } } diff --git a/packages/interpreter/src/components/visitor/ifStatement.ts b/packages/interpreter/src/components/visitor/ifStatement.ts index dcd7acbf..96ff5e7b 100644 --- a/packages/interpreter/src/components/visitor/ifStatement.ts +++ b/packages/interpreter/src/components/visitor/ifStatement.ts @@ -3,6 +3,7 @@ import { ASTNode } from "bhai-lang-parser"; import InterpreterModule from "../../module/interpreterModule"; import Scope from "../scope"; +import { DataTypes, sanatizeData } from "../dataClass"; export default class IfStatement implements Visitor { @@ -10,8 +11,8 @@ export default class IfStatement implements Visitor { const test = node.test; const parentScope = InterpreterModule.getCurrentScope(); if (test) { - const testResult = InterpreterModule.getVisitor(test.type).visitNode(test); - if (testResult === true || testResult === "sahi") { + const testResult = sanatizeData(InterpreterModule.getVisitor(test.type).visitNode(test)); + if (testResult.getType() !== DataTypes.Null && testResult.getValue() === true) { const consequent = node.consequent; if (consequent) { InterpreterModule.setCurrentScope(new Scope(parentScope)); diff --git a/packages/interpreter/src/components/visitor/index.ts b/packages/interpreter/src/components/visitor/index.ts index cc6b9e33..6f1f8f63 100644 --- a/packages/interpreter/src/components/visitor/index.ts +++ b/packages/interpreter/src/components/visitor/index.ts @@ -1,5 +1,6 @@ import { ASTNode } from "bhai-lang-parser"; +import { DataObject } from "../dataClass"; export default interface Visitor { - visitNode(node: ASTNode): unknown; + visitNode(node: ASTNode): DataObject|null|void; } diff --git a/packages/interpreter/src/components/visitor/nullLiteral.ts b/packages/interpreter/src/components/visitor/nullLiteral.ts index eda6d55d..7a7dd0ad 100644 --- a/packages/interpreter/src/components/visitor/nullLiteral.ts +++ b/packages/interpreter/src/components/visitor/nullLiteral.ts @@ -1,8 +1,11 @@ import Visitor from "."; import { ASTNode } from "bhai-lang-parser"; +import { NullObject } from "../dataClass"; export default class NullLiteral implements Visitor { visitNode(node: ASTNode) { - return node.value; + if (node.value !== "nalla") + throw new Error(`Ye kya kar raha hai: "${node.value}" sahi nhi hai. isme nalla dal`); + return new NullObject(); } } diff --git a/packages/interpreter/src/components/visitor/numericLiteral.ts b/packages/interpreter/src/components/visitor/numericLiteral.ts index ca022029..86416093 100644 --- a/packages/interpreter/src/components/visitor/numericLiteral.ts +++ b/packages/interpreter/src/components/visitor/numericLiteral.ts @@ -1,8 +1,12 @@ import Visitor from "."; import { ASTNode } from "bhai-lang-parser"; +import { NumericObject } from "../dataClass"; +import RuntimeException from "../../exceptions/runtimeException"; export default class NumericLiteral implements Visitor { visitNode(node: ASTNode) { - return node.value; + if(typeof node.value!=="number") + throw new RuntimeException(`Ye kya kar raha hai: "${node.value}" sahi nhi hai ${node.type} me. isme number dal`); + return new NumericObject(node.value); } } diff --git a/packages/interpreter/src/components/visitor/printStatement.ts b/packages/interpreter/src/components/visitor/printStatement.ts index 2f8a94de..e02bf2c6 100644 --- a/packages/interpreter/src/components/visitor/printStatement.ts +++ b/packages/interpreter/src/components/visitor/printStatement.ts @@ -3,6 +3,7 @@ import { ASTNode } from "bhai-lang-parser"; import InvalidStateException from "../../exceptions/invalidStateException"; import InterpreterModule from "../../module/interpreterModule"; +import { sanatizeData } from "../dataClass"; export default class PrintStatement implements Visitor { @@ -14,15 +15,12 @@ export default class PrintStatement implements Visitor { const value = node.expressions .map((expression: ASTNode) => { - let currentNodeOutput = InterpreterModule.getVisitor(expression.type).visitNode(expression); - if (currentNodeOutput === true) - currentNodeOutput = "sahi"; - else if (currentNodeOutput === false) - currentNodeOutput = "galat"; - return currentNodeOutput; - } - ) - .join(" "); + let currentNodeOutput = sanatizeData(InterpreterModule.getVisitor(expression.type).visitNode(expression)); + if(currentNodeOutput===undefined) + return "nalla" + return currentNodeOutput?.getStringValue(); + } + ).join(" "); console.log(value); } } diff --git a/packages/interpreter/src/components/visitor/returnStatement.ts b/packages/interpreter/src/components/visitor/returnStatement.ts new file mode 100644 index 00000000..bdace53d --- /dev/null +++ b/packages/interpreter/src/components/visitor/returnStatement.ts @@ -0,0 +1,19 @@ +import Visitor from "."; +import { ASTNode } from "bhai-lang-parser"; + +import InvalidStateException from "../../exceptions/invalidStateException"; +import InterpreterModule from "../../module/interpreterModule"; +import { sanatizeData } from "../dataClass"; + + +export default class ReturnStatement implements Visitor { + visitNode(node: ASTNode) { + if (!node.expression) + throw new InvalidStateException( + `No expressions to print: ${node.expressions}` + ); + let retVal= sanatizeData(InterpreterModule.getVisitor(node.expression.type).visitNode(node.expression)); + InterpreterModule.getCurrentScope().setReturnStatement(true,retVal); + return retVal; + } +} diff --git a/packages/interpreter/src/components/visitor/stringLiteral.ts b/packages/interpreter/src/components/visitor/stringLiteral.ts index be354200..ca4a056e 100644 --- a/packages/interpreter/src/components/visitor/stringLiteral.ts +++ b/packages/interpreter/src/components/visitor/stringLiteral.ts @@ -1,8 +1,12 @@ import Visitor from "."; import { ASTNode } from "bhai-lang-parser"; +import RuntimeException from "../../exceptions/runtimeException"; +import { StringObject } from "../dataClass"; export default class StringLiteral implements Visitor { visitNode(node: ASTNode) { - return node.value; + if(typeof node.value!=="string") + throw new RuntimeException(`Ye kya kar raha hai: "${node.value}" sahi nhi hai ${node.name} me. isme sting dal`); + return new StringObject(node.value); } } diff --git a/packages/interpreter/src/components/visitor/variableDeclaration.ts b/packages/interpreter/src/components/visitor/variableDeclaration.ts index c459e5ee..1f85597b 100644 --- a/packages/interpreter/src/components/visitor/variableDeclaration.ts +++ b/packages/interpreter/src/components/visitor/variableDeclaration.ts @@ -3,6 +3,7 @@ import { ASTNode, NodeType } from "bhai-lang-parser"; import InvalidStateException from "../../exceptions/invalidStateException"; import InterpreterModule from "../../module/interpreterModule"; +import { BooleanObject, NullObject, sanatizeData } from "../dataClass"; export default class VariableDeclaration implements Visitor { visitNode(node: ASTNode) { @@ -14,11 +15,11 @@ export default class VariableDeclaration implements Visitor { let value; - if (node.init.type === NodeType.NullLiteral) value = null; + if (node.init.type === NodeType.NullLiteral) value = new NullObject(); else if (node.init.type === NodeType.BooleanLiteral) - value = node.init.value === "sahi" ? true : false; + value = new BooleanObject(node.init.value === "sahi" ? true : false); else - value = InterpreterModule.getVisitor(node.init.type).visitNode(node.init); + value = sanatizeData(InterpreterModule.getVisitor(node.init.type).visitNode(node.init)); const currentScope = InterpreterModule.getCurrentScope(); diff --git a/packages/interpreter/src/components/visitor/whileStatement.ts b/packages/interpreter/src/components/visitor/whileStatement.ts index e7037b52..09685535 100644 --- a/packages/interpreter/src/components/visitor/whileStatement.ts +++ b/packages/interpreter/src/components/visitor/whileStatement.ts @@ -19,7 +19,9 @@ export default class WhileStatement implements Visitor { InterpreterModule.getCurrentScope().setLoop(true); - for (let testResult = getConditionValue(), executions = 0; testResult === true || testResult === "sahi"; testResult = getConditionValue(), executions++) { + for (let testResult = getConditionValue(), executions = 0; + testResult?.getValue() === true||testResult?.getValue()!==null; + testResult = getConditionValue(), executions++) { if (InterpreterModule.getCurrentScope().isBreakStatement()) { break; diff --git a/packages/interpreter/src/helpers/index.ts b/packages/interpreter/src/helpers/index.ts index b23debf0..59a2e696 100644 --- a/packages/interpreter/src/helpers/index.ts +++ b/packages/interpreter/src/helpers/index.ts @@ -1,40 +1,42 @@ +import { BooleanObject, DataObject, DataTypes, NumericObject, StringObject } from "../components/dataClass"; import InvalidStateException from "../exceptions/invalidStateException"; import RuntimeException from "../exceptions/runtimeException"; export function checkNumberOperands(operands: { - left: unknown; - right: unknown; -}): operands is { left: number; right: number } { + left: DataObject; + right: DataObject; +}):boolean{ return ( - typeof operands.left === "number" && typeof operands.right === "number" + operands.left.getType() === DataTypes.Numeric && operands.right.getType() === DataTypes.Numeric ); } export function checkStringOperands(operands: { - left: unknown; - right: unknown; -}): operands is { left: string; right: string } { + left: DataObject; + right: DataObject; +}):boolean{ return ( - typeof operands.left === "string" && typeof operands.right === "string" + operands.left.getType() === DataTypes.String && operands.right.getType() === DataTypes.String ); } export function checkNumberAndStringOperands(operands: { - left: unknown; - right: unknown; -}): operands is { left: string; right: string } { + left: DataObject; + right: DataObject; +}): operands is { left: StringObject; right: NumericObject }|{ left: NumericObject; right: StringObject } { return ( - (typeof operands.left === "string" && typeof operands.right === "number") || (typeof operands.right === "string" && typeof operands.left === "number") + (operands.left.getType() === DataTypes.String && operands.right.getType() === DataTypes.Numeric) || + (operands.right.getType() === DataTypes.String && operands.left.getType() === DataTypes.Numeric) ); } export function getOperationValue( - operands: { left: unknown; right: unknown }, + operands: { left: DataObject; right: DataObject }, operator: string ) { const exception = new RuntimeException( - `Ye kya kar raha hai: "${operator}" ke sath "${typeof operands.left}" aur "${typeof operands.right}" nahi jamte.` + `Ye kya kar raha hai: "${operator}" ke sath "${typeof operands.left.getStringValue()}" aur "${typeof operands.right.getStringValue()}" nahi jamte.` ); switch (operator) { @@ -44,15 +46,15 @@ export function getOperationValue( case "+=": case "+": if (checkNumberOperands(operands)) { - return operands.left + operands.right; + return new NumericObject(operands.left.getValue() + operands.right.getValue()); } if (checkStringOperands(operands)) { - return operands.left + operands.right; + return new StringObject(operands.left.getValue() + operands.right.getValue()); } if (checkNumberAndStringOperands(operands)) { - return operands.left.toString() + operands.right.toString(); + return new StringObject(operands.left.getStringValue() + operands.right.getStringValue()); } throw exception; @@ -60,7 +62,7 @@ export function getOperationValue( case "-=": case "-": if (checkNumberOperands(operands)) { - return operands.left - operands.right; + return new NumericObject(operands.left.getValue() - operands.right.getValue()); } throw exception; @@ -68,19 +70,19 @@ export function getOperationValue( case "*=": case "*": if (checkNumberOperands(operands)) { - return operands.left * operands.right; + return new NumericObject(operands.left.getValue() * operands.right.getValue()); } throw exception; case "/=": case "/": - if (operands.right === 0) { + if (operands.right.getValue() === 0) { throw new RuntimeException(`Kya kar rha hai tu??...zero se divide ni karte`); } if (checkNumberOperands(operands)) { - return operands.left / operands.right; + return new NumericObject(operands.left.getValue() / operands.right.getValue()); } throw exception; @@ -88,66 +90,69 @@ export function getOperationValue( case "%=": case "%": if (checkNumberOperands(operands)) { - return operands.left % operands.right; + return new NumericObject(operands.left.getValue() % operands.right.getValue()); } throw exception; case "==": if (checkNumberOperands(operands)) { - return operands.left === operands.right; + return new BooleanObject(operands.left.getValue() === operands.right.getValue()); } if (checkStringOperands(operands)) { - return operands.left === operands.right; + return new BooleanObject(operands.left.getValue() === operands.right.getValue()); } throw exception; case "!=": if (checkNumberOperands(operands)) { - return operands.left !== operands.right; + return new BooleanObject(operands.left.getValue() !== operands.right.getValue()); } if (checkStringOperands(operands)) { - return operands.left !== operands.right; + return new BooleanObject(operands.left.getValue() !== operands.right.getValue()); } throw exception; case ">": if (checkNumberOperands(operands)) { - return operands.left > operands.right; + return new BooleanObject(operands.left.getValue() > operands.right.getValue()); } throw exception; case "<": if (checkNumberOperands(operands)) { - return operands.left < operands.right; + return new BooleanObject(operands.left.getValue() < operands.right.getValue()); } throw exception; case ">=": if (checkNumberOperands(operands)) { - return operands.left >= operands.right; + return new BooleanObject(operands.left.getValue() >= operands.right.getValue()); } throw exception; case "<=": if (checkNumberOperands(operands)) { - return operands.left <= operands.right; + return new BooleanObject(operands.left.getValue() <= operands.right.getValue()); } throw exception; case "&&": - return operands.left && operands.right; + // if (operands.left.getType() !== DataTypes.Boolean || operands.right.getType() !== DataTypes.Boolean) { + // throw exception; + // } + return new BooleanObject(operands.left.getValue() && operands.right.getValue()); case "||": - return operands.left || operands.right; + return new BooleanObject((operands.left.getValue() || operands.right.getValue())==true); default: throw new InvalidStateException(`Unsupported operator: ${operator}`); diff --git a/packages/interpreter/src/module/interpreterModule.ts b/packages/interpreter/src/module/interpreterModule.ts index 4d9f593c..b8fff628 100644 --- a/packages/interpreter/src/module/interpreterModule.ts +++ b/packages/interpreter/src/module/interpreterModule.ts @@ -21,6 +21,7 @@ import NullLiteral from "../components/visitor/nullLiteral"; import NumericLiteral from "../components/visitor/numericLiteral"; import PrintStatement from "../components/visitor/printStatement"; import Program from "../components/visitor/program"; +import ReturnStatement from "../components/visitor/returnStatement"; import StringLiteral from "../components/visitor/stringLiteral"; import VariableDeclaration from "../components/visitor/variableDeclaration"; import VariableStatement from "../components/visitor/variableStatement"; @@ -53,6 +54,7 @@ export default class InterpreterModule { [NodeType.FunctionStatement]: new FunctionStatement(), [NodeType.FunctionDeclaration]: new FunctionDeclaration(), [NodeType.CallableExpression]: new CallableExpression(), + [NodeType.ReturnStatement]: new ReturnStatement(), } as Record; diff --git a/packages/parser/src/components/parser/statement/returnStatement.ts b/packages/parser/src/components/parser/statement/returnStatement.ts index 49b907dd..68e36931 100644 --- a/packages/parser/src/components/parser/statement/returnStatement.ts +++ b/packages/parser/src/components/parser/statement/returnStatement.ts @@ -17,7 +17,7 @@ export default class ReturnStatement extends Statement { ).getExpression(); return { - type: NodeType.RetrunStatement, + type: NodeType.ReturnStatement, expression: value, }; } diff --git a/packages/parser/src/constants/constants.ts b/packages/parser/src/constants/constants.ts index 8fe3b878..383b3123 100644 --- a/packages/parser/src/constants/constants.ts +++ b/packages/parser/src/constants/constants.ts @@ -29,6 +29,6 @@ export const NodeType = { VariableDeclaration: "VariableDeclaration", FunctionStatement: "FunctionStatement", FunctionDeclaration: "FunctionDeclaration", - RetrunStatement: "RetrunStatement", + ReturnStatement: "ReturnStatement", Program: "Program", } as const; From 774d9c13e6cd130011929509445f9c6ef7051f1c Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Fri, 18 Mar 2022 21:08:20 +0530 Subject: [PATCH 05/22] Update index.ts --- packages/interpreter/src/helpers/index.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/interpreter/src/helpers/index.ts b/packages/interpreter/src/helpers/index.ts index 3cb841b4..725e7b7d 100644 --- a/packages/interpreter/src/helpers/index.ts +++ b/packages/interpreter/src/helpers/index.ts @@ -96,10 +96,10 @@ export function getOperationValue( throw exception; case "==": - return BooleanObject(operands.left.getValue() === operands.right.getValue()); + return new BooleanObject(operands.left.getValue() === operands.right.getValue()); case "!=": - return BooleanObject(operands.left.getValue() !== operands.right.getValue()); + return new BooleanObject(operands.left.getValue() !== operands.right.getValue()); case ">": if (checkNumberOperands(operands)) { From fc93884ac3262a697a10158d1c80bf4cce5a86ae Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Fri, 18 Mar 2022 21:25:31 +0530 Subject: [PATCH 06/22] Update scope.ts --- packages/interpreter/src/components/scope.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/interpreter/src/components/scope.ts b/packages/interpreter/src/components/scope.ts index 283aa9b5..3128d421 100644 --- a/packages/interpreter/src/components/scope.ts +++ b/packages/interpreter/src/components/scope.ts @@ -62,7 +62,7 @@ export default class Scope { get(identifier: string): DataObject { if (this._variables.has(identifier)) { let value = sanatizeData(this._variables.get(identifier)); - if(value) return value; + return value; } if (this._parentScope !== null) { From 0ffb32efa626e44dd94b5b7bf7eaa38e334b04b1 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Fri, 18 Mar 2022 21:28:45 +0530 Subject: [PATCH 07/22] Update printStatement.ts --- packages/interpreter/src/components/visitor/printStatement.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/packages/interpreter/src/components/visitor/printStatement.ts b/packages/interpreter/src/components/visitor/printStatement.ts index e02bf2c6..5c049abb 100644 --- a/packages/interpreter/src/components/visitor/printStatement.ts +++ b/packages/interpreter/src/components/visitor/printStatement.ts @@ -16,8 +16,6 @@ export default class PrintStatement implements Visitor { const value = node.expressions .map((expression: ASTNode) => { let currentNodeOutput = sanatizeData(InterpreterModule.getVisitor(expression.type).visitNode(expression)); - if(currentNodeOutput===undefined) - return "nalla" return currentNodeOutput?.getStringValue(); } ).join(" "); From 57c70a8f59e2c6875132064df5f55f869e6ff865 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 19 Mar 2022 00:22:41 +0530 Subject: [PATCH 08/22] bugfix --- apps/docs/components/Code/index.tsx | 6 +++++- apps/docs/components/Documentation/index.tsx | 16 ++++++++++++++++ apps/docs/components/common/syntax.ts | 2 +- .../src/components/visitor/callableExpression.ts | 7 ++++--- .../components/visitor/functionDeclaration.ts | 4 ++-- 5 files changed, 28 insertions(+), 7 deletions(-) diff --git a/apps/docs/components/Code/index.tsx b/apps/docs/components/Code/index.tsx index f26253f6..810fe869 100644 --- a/apps/docs/components/Code/index.tsx +++ b/apps/docs/components/Code/index.tsx @@ -15,8 +15,12 @@ hi bhai bhai ye hai a = 3; bhai ye hai b = 0; + apna funda square(x){ + rakh le bhai x*x; + } + jab tak bhai (b < 5) { - bol bhai b; + bol bhai square(b); agar bhai (b == a){ bol bhai "b is equal to a"; diff --git a/apps/docs/components/Documentation/index.tsx b/apps/docs/components/Documentation/index.tsx index fe224fb2..97307ebb 100644 --- a/apps/docs/components/Documentation/index.tsx +++ b/apps/docs/components/Documentation/index.tsx @@ -94,6 +94,22 @@ bye bhai } warna bhai { bol bhai "a is greater than or equal to 25"; } +bye bhai + ` + }, + { + name: "Function", + description: ( + <> + Define function using apna funda then the funcrtion name and parameters within "(" ")", blocks are executed and the return value can be provided usingrakh le bhai. + + ), + code: `hi bhai + apna funda test(c){ + bol bhai c; + rakh le bhai "return bhi "+c; + } + bol bhai test("kam kiya bhai"); bye bhai ` }, diff --git a/apps/docs/components/common/syntax.ts b/apps/docs/components/common/syntax.ts index df595f97..5fd05cae 100644 --- a/apps/docs/components/common/syntax.ts +++ b/apps/docs/components/common/syntax.ts @@ -18,7 +18,7 @@ export const bhaiLangSyntax = languages.extend("clike", { pattern: /(["'])((?:\\\1|(?:(?!\1)).)*)(\1)/, greedy: true, }, - keyword: /\b(?:hi bhai|bye bhai|bol bhai|bhai ye hai|nalla|agar bhai|warna bhai|jab tak bhai|bas kar bhai|agla dekh bhai)\b/, + keyword: /\b(?:hi bhai|bye bhai|bol bhai|bhai ye hai|nalla|agar bhai|warna bhai|jab tak bhai|bas kar bhai|agla dekh bhai|apna funda|rakh le bhai)\b/, boolean: /\b(?:sahi|galat)\b/, number: /(?:(?:\b\d+(?:\.\d*)?|\B\.\d+)(?:e[-+]?\d+)?)i?/i, operator: diff --git a/packages/interpreter/src/components/visitor/callableExpression.ts b/packages/interpreter/src/components/visitor/callableExpression.ts index 2b2988db..8fb8a91b 100644 --- a/packages/interpreter/src/components/visitor/callableExpression.ts +++ b/packages/interpreter/src/components/visitor/callableExpression.ts @@ -3,7 +3,7 @@ import { ASTNode } from "bhai-lang-parser"; import InvalidStateException from "../../exceptions/invalidStateException"; import InterpreterModule from "../../module/interpreterModule"; -import { sanatizeData } from "../dataClass"; +import { NullObject, sanatizeData } from "../dataClass"; import RuntimeException from "../../exceptions/runtimeException"; export default class CallableExpression implements Visitor { @@ -21,15 +21,16 @@ export default class CallableExpression implements Visitor { if (value.args) { for (let i = 0; i < value.args.length; i++) { if(node.args&&node.args[i]){ + let argv=sanatizeData(InterpreterModule.getVisitor(node.args[i].type).visitNode(node.args[i])); args.push({ identifier:value.args[i], - value:node.args[i].value + value:argv }); } else{ args.push({ identifier:value.args[i], - value:null + value:new NullObject() }); } } diff --git a/packages/interpreter/src/components/visitor/functionDeclaration.ts b/packages/interpreter/src/components/visitor/functionDeclaration.ts index 499cb622..bff95e13 100644 --- a/packages/interpreter/src/components/visitor/functionDeclaration.ts +++ b/packages/interpreter/src/components/visitor/functionDeclaration.ts @@ -4,7 +4,7 @@ import { ASTNode } from "bhai-lang-parser"; import InvalidStateException from "../../exceptions/invalidStateException"; import InterpreterModule from "../../module/interpreterModule"; import Scope from "../scope"; -import { CallableObject } from "../dataClass"; +import { CallableObject, DataObject } from "../dataClass"; export default class FunctionDeclaration implements Visitor { visitNode(node: ASTNode) { @@ -20,7 +20,7 @@ export default class FunctionDeclaration implements Visitor { let scope=InterpreterModule.getCurrentScope() value={ args:node.id.args?.map(arg=>arg.name), - code:(args:{identifier:string,value:any}[]):any=>{ + code:(args:{identifier:string,value:DataObject}[]):any=>{ let oldScope=InterpreterModule.getCurrentScope() let newScope=new Scope(scope) args.forEach(arg=>{ From 08fd24df5fd6eb3d0aaa9ba368e2691c0c32c104 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 19 Mar 2022 00:22:57 +0530 Subject: [PATCH 09/22] docs updated --- apps/docs/components/Documentation/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/components/Documentation/index.tsx b/apps/docs/components/Documentation/index.tsx index 97307ebb..b6c4b684 100644 --- a/apps/docs/components/Documentation/index.tsx +++ b/apps/docs/components/Documentation/index.tsx @@ -101,7 +101,7 @@ bye bhai name: "Function", description: ( <> - Define function using apna funda then the funcrtion name and parameters within "(" ")", blocks are executed and the return value can be provided usingrakh le bhai. + Define function using apna funda then the funcrtion name and parameters within "(" ")", blocks are executed and the return value can be provided using rakh le bhai. ), code: `hi bhai From 6051d32493ec132d3faa682d7026f2641bd73ba6 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 19 Mar 2022 00:29:32 +0530 Subject: [PATCH 10/22] docs fix --- apps/docs/components/Documentation/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/components/Documentation/index.tsx b/apps/docs/components/Documentation/index.tsx index b6c4b684..bda5a208 100644 --- a/apps/docs/components/Documentation/index.tsx +++ b/apps/docs/components/Documentation/index.tsx @@ -101,7 +101,7 @@ bye bhai name: "Function", description: ( <> - Define function using apna funda then the funcrtion name and parameters within "(" ")", blocks are executed and the return value can be provided using rakh le bhai. + Define function using apna funda then the funcrtion name and parameters within '(' ')', blocks are executed and the return value can be provided using rakh le bhai. ), code: `hi bhai From 3336243b2c592dd716952363ab12f5484e57b79a Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 19 Mar 2022 00:33:16 +0530 Subject: [PATCH 11/22] docs fix --- apps/docs/components/Documentation/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/docs/components/Documentation/index.tsx b/apps/docs/components/Documentation/index.tsx index bda5a208..52a40a14 100644 --- a/apps/docs/components/Documentation/index.tsx +++ b/apps/docs/components/Documentation/index.tsx @@ -101,7 +101,7 @@ bye bhai name: "Function", description: ( <> - Define function using apna funda then the funcrtion name and parameters within '(' ')', blocks are executed and the return value can be provided using rakh le bhai. + Define function using apna funda then the funcrtion name and parameters within ( ), blocks are executed and the return value can be provided using rakh le bhai. ), code: `hi bhai From 52dd0c63cc641491361023a07d7f9a64bb2349f3 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 19 Mar 2022 00:58:01 +0530 Subject: [PATCH 12/22] bugfix --- .../src/components/visitor/whileStatement.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/interpreter/src/components/visitor/whileStatement.ts b/packages/interpreter/src/components/visitor/whileStatement.ts index 09685535..8ed022ef 100644 --- a/packages/interpreter/src/components/visitor/whileStatement.ts +++ b/packages/interpreter/src/components/visitor/whileStatement.ts @@ -4,13 +4,18 @@ import { ASTNode } from "bhai-lang-parser"; import RuntimeException from "../../exceptions/runtimeException"; import InterpreterModule from "../../module/interpreterModule"; import Scope from "../scope"; +import { sanatizeData } from "../dataClass"; export default class WhileStatement implements Visitor { visitNode(node: ASTNode) { const test = node.test; if (test) { - const getConditionValue = ()=> InterpreterModule.getVisitor(test.type).visitNode(test); + const getConditionValue = ()=> { + // console.log("while statement ",test); + + return sanatizeData(InterpreterModule.getVisitor(test.type).visitNode(test)); + } const parentScope = InterpreterModule.getCurrentScope(); @@ -20,7 +25,7 @@ export default class WhileStatement implements Visitor { for (let testResult = getConditionValue(), executions = 0; - testResult?.getValue() === true||testResult?.getValue()!==null; + testResult.getValue() === true; testResult = getConditionValue(), executions++) { if (InterpreterModule.getCurrentScope().isBreakStatement()) { @@ -47,3 +52,4 @@ export default class WhileStatement implements Visitor { } } } + From 3dc5b92ef5ac403b4057f4ab0d72b6a637b0d55b Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 19 Mar 2022 00:58:33 +0530 Subject: [PATCH 13/22] clean up --- .../interpreter/src/components/visitor/whileStatement.ts | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/packages/interpreter/src/components/visitor/whileStatement.ts b/packages/interpreter/src/components/visitor/whileStatement.ts index 8ed022ef..f92f9b74 100644 --- a/packages/interpreter/src/components/visitor/whileStatement.ts +++ b/packages/interpreter/src/components/visitor/whileStatement.ts @@ -11,11 +11,8 @@ export default class WhileStatement implements Visitor { visitNode(node: ASTNode) { const test = node.test; if (test) { - const getConditionValue = ()=> { - // console.log("while statement ",test); - - return sanatizeData(InterpreterModule.getVisitor(test.type).visitNode(test)); - } + const getConditionValue = ()=> sanatizeData(InterpreterModule.getVisitor(test.type).visitNode(test)); + const parentScope = InterpreterModule.getCurrentScope(); From 49480494b071b4c5f3b9a0039d262088a955b272 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 19 Mar 2022 19:21:22 +0530 Subject: [PATCH 14/22] Update dataClass.ts --- packages/interpreter/src/components/dataClass.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/interpreter/src/components/dataClass.ts b/packages/interpreter/src/components/dataClass.ts index cffc84d9..a8092660 100644 --- a/packages/interpreter/src/components/dataClass.ts +++ b/packages/interpreter/src/components/dataClass.ts @@ -63,7 +63,7 @@ export class CallableObject extends DataObject{ } } -export function sanatizeData(data:any):DataObject{ +export function sanatizeData(data:unknown):DataObject{ if((data==null)||(data==undefined)){ return new NullObject(); } @@ -83,4 +83,4 @@ export function sanatizeData(data:any):DataObject{ return data as DataObject; } else throw new Error(`Ye kya kar raha hai: "${data}" sahi nhi hai. ye konsa data type hai bhai`); -} \ No newline at end of file +} From f53d781de9276dd8c97c72a5f80aad63722869bf Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 19 Mar 2022 19:27:03 +0530 Subject: [PATCH 15/22] type fix --- packages/interpreter/src/components/dataClass.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/interpreter/src/components/dataClass.ts b/packages/interpreter/src/components/dataClass.ts index a8092660..df58dd13 100644 --- a/packages/interpreter/src/components/dataClass.ts +++ b/packages/interpreter/src/components/dataClass.ts @@ -63,7 +63,7 @@ export class CallableObject extends DataObject{ } } -export function sanatizeData(data:unknown):DataObject{ +export function sanatizeData(data:any|unknown):DataObject{ if((data==null)||(data==undefined)){ return new NullObject(); } @@ -79,7 +79,7 @@ export function sanatizeData(data:unknown):DataObject{ if(typeof data=='function'){ return new CallableObject(data); } - if(typeof data=='object' && data.isDataObject){ + if(data.isDataObject==true){ return data as DataObject; } else throw new Error(`Ye kya kar raha hai: "${data}" sahi nhi hai. ye konsa data type hai bhai`); From f89fc4f3108a7ef639f379c3eb69ad63006cd5af Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sun, 20 Mar 2022 09:45:00 +0530 Subject: [PATCH 16/22] clean up --- .../components/visitor/binaryExpression.ts | 39 ++----------------- .../src/components/visitor/ifStatement.ts | 4 +- .../components/visitor/variableDeclaration.ts | 12 ++---- .../src/components/visitor/whileStatement.ts | 2 +- 4 files changed, 9 insertions(+), 48 deletions(-) diff --git a/packages/interpreter/src/components/visitor/binaryExpression.ts b/packages/interpreter/src/components/visitor/binaryExpression.ts index ba051a40..3fd1ac7f 100644 --- a/packages/interpreter/src/components/visitor/binaryExpression.ts +++ b/packages/interpreter/src/components/visitor/binaryExpression.ts @@ -6,7 +6,7 @@ import NallaPointerException from "../../exceptions/nallaPointerException"; import RuntimeException from "../../exceptions/runtimeException"; import { getOperationValue } from "../../helpers"; import InterpreterModule from "../../module/interpreterModule"; -import { BooleanObject, DataObject, NullObject, sanatizeData } from "../dataClass"; +import { sanatizeData } from "../dataClass"; export default class BinaryExpression implements Visitor { @@ -17,32 +17,17 @@ export default class BinaryExpression implements Visitor { ); } - let left:DataObject=new NullObject(), right:DataObject=new NullObject(); - // handling logical & binary both at the same place as both operate on two operands if (node.type == NodeType.BinaryExpression) { if (node.operator !== "==" && node.operator !== "!=") { this._checkNalla(node); this._checkBoolean(node); } - left = this._getNodeValue(node.left); - right = this._getNodeValue(node.right); } else if (node.type == NodeType.LogicalExpression) { this._checkNalla(node); - - left = node.left.type == NodeType.BooleanLiteral ? - new BooleanObject(node.left.value == "sahi" ? true : false) - : sanatizeData(InterpreterModule.getVisitor(node.left.type).visitNode( - node.left - )); - - right = node.right.type == NodeType.BooleanLiteral ? - new BooleanObject(node.right.value == "sahi" ? true : false) - : sanatizeData(InterpreterModule.getVisitor(node.right.type).visitNode( - node.right - )); - } + const left = sanatizeData(InterpreterModule.getVisitor(node.left.type).visitNode(node.left)); + const right = sanatizeData(InterpreterModule.getVisitor(node.right.type).visitNode(node.right)); return getOperationValue({ left, right }, node.operator); } @@ -102,22 +87,4 @@ export default class BinaryExpression implements Visitor { if (value.getValue() === true || value.getValue() === false) throw runtimeException; } } - - private _getNodeValue(node: ASTNode) { - - if (node.type === NodeType.NullLiteral) - return new NullObject(); - - if (node.type === NodeType.BooleanLiteral) { - return new BooleanObject(node.value === "sahi" ? true : false); - } - - if (node.type === NodeType.IdentifierExpression && node.name) - return InterpreterModule.getCurrentScope().get(node.name); - - return sanatizeData(InterpreterModule.getVisitor(node.type).visitNode( - node - )); - } - } diff --git a/packages/interpreter/src/components/visitor/ifStatement.ts b/packages/interpreter/src/components/visitor/ifStatement.ts index 96ff5e7b..4043666e 100644 --- a/packages/interpreter/src/components/visitor/ifStatement.ts +++ b/packages/interpreter/src/components/visitor/ifStatement.ts @@ -3,7 +3,7 @@ import { ASTNode } from "bhai-lang-parser"; import InterpreterModule from "../../module/interpreterModule"; import Scope from "../scope"; -import { DataTypes, sanatizeData } from "../dataClass"; +import { sanatizeData } from "../dataClass"; export default class IfStatement implements Visitor { @@ -12,7 +12,7 @@ export default class IfStatement implements Visitor { const parentScope = InterpreterModule.getCurrentScope(); if (test) { const testResult = sanatizeData(InterpreterModule.getVisitor(test.type).visitNode(test)); - if (testResult.getType() !== DataTypes.Null && testResult.getValue() === true) { + if (testResult.getValue()) { const consequent = node.consequent; if (consequent) { InterpreterModule.setCurrentScope(new Scope(parentScope)); diff --git a/packages/interpreter/src/components/visitor/variableDeclaration.ts b/packages/interpreter/src/components/visitor/variableDeclaration.ts index 1f85597b..0b2059d4 100644 --- a/packages/interpreter/src/components/visitor/variableDeclaration.ts +++ b/packages/interpreter/src/components/visitor/variableDeclaration.ts @@ -1,9 +1,9 @@ import Visitor from "."; -import { ASTNode, NodeType } from "bhai-lang-parser"; +import { ASTNode } from "bhai-lang-parser"; import InvalidStateException from "../../exceptions/invalidStateException"; import InterpreterModule from "../../module/interpreterModule"; -import { BooleanObject, NullObject, sanatizeData } from "../dataClass"; +import { sanatizeData } from "../dataClass"; export default class VariableDeclaration implements Visitor { visitNode(node: ASTNode) { @@ -13,13 +13,7 @@ export default class VariableDeclaration implements Visitor { const identifier = node.id.name; - let value; - - if (node.init.type === NodeType.NullLiteral) value = new NullObject(); - else if (node.init.type === NodeType.BooleanLiteral) - value = new BooleanObject(node.init.value === "sahi" ? true : false); - else - value = sanatizeData(InterpreterModule.getVisitor(node.init.type).visitNode(node.init)); + let value= sanatizeData(InterpreterModule.getVisitor(node.init.type).visitNode(node.init)); const currentScope = InterpreterModule.getCurrentScope(); diff --git a/packages/interpreter/src/components/visitor/whileStatement.ts b/packages/interpreter/src/components/visitor/whileStatement.ts index f92f9b74..7347bec7 100644 --- a/packages/interpreter/src/components/visitor/whileStatement.ts +++ b/packages/interpreter/src/components/visitor/whileStatement.ts @@ -22,7 +22,7 @@ export default class WhileStatement implements Visitor { for (let testResult = getConditionValue(), executions = 0; - testResult.getValue() === true; + testResult.getValue(); testResult = getConditionValue(), executions++) { if (InterpreterModule.getCurrentScope().isBreakStatement()) { From bd11d036990c56e3a61a43e6e6f4003f7d641694 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Mon, 21 Mar 2022 16:01:53 +0530 Subject: [PATCH 17/22] conflict fix and merged --- .../interpreter/src/components/visitor/ifStatement.ts | 8 ++++++-- packages/parser/src/components/parser/types/nodeTypes.ts | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/packages/interpreter/src/components/visitor/ifStatement.ts b/packages/interpreter/src/components/visitor/ifStatement.ts index b5a1f235..b7f857f7 100644 --- a/packages/interpreter/src/components/visitor/ifStatement.ts +++ b/packages/interpreter/src/components/visitor/ifStatement.ts @@ -36,12 +36,16 @@ export default class IfStatement implements Visitor { } else { // Evaluate the "test" condition of the "nahi to bhai" node // If the condition is true, evaluate the node and break - const testResult = sanatizeData((InterpreterModule.getVisitor(alternateTest!.type).visitNode(alternateTest)); + const testResult = sanatizeData(InterpreterModule.getVisitor(alternateTest!.type).visitNode(alternateTest)); if (testResult.getValue()) { - this.evaluateNode(alternate.consequent, parentScope); + this.evaluateNode(alternate, parentScope); break; } } + if (testResult.getValue()) { + this.evaluateNode(alternate.consequent, parentScope); + break; + } } } } diff --git a/packages/parser/src/components/parser/types/nodeTypes.ts b/packages/parser/src/components/parser/types/nodeTypes.ts index 62563893..c00b4937 100644 --- a/packages/parser/src/components/parser/types/nodeTypes.ts +++ b/packages/parser/src/components/parser/types/nodeTypes.ts @@ -14,6 +14,6 @@ export type ASTNode = { declaration?: ASTNode; test?: ASTNode; consequent?: ASTNode; - alternate?: ASTNode[]; + alternates?: ASTNode[]; args?: ASTNode[]; }; From 3853cf82572a60a46bc5309130a523dd0b1b7544 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Mon, 21 Mar 2022 19:24:54 +0530 Subject: [PATCH 18/22] test cases updated --- packages/interpreter/src/helpers/index.ts | 19 +- .../test/visitorUnitTests/helper.test.ts | 381 +++++++++--------- .../unitTestsForNegativeCases.test.ts | 3 +- 3 files changed, 215 insertions(+), 188 deletions(-) diff --git a/packages/interpreter/src/helpers/index.ts b/packages/interpreter/src/helpers/index.ts index 22fe7d16..deedc9c0 100644 --- a/packages/interpreter/src/helpers/index.ts +++ b/packages/interpreter/src/helpers/index.ts @@ -129,14 +129,21 @@ export function getOperationValue( throw exception; case "&&": - // if (operands.left.getType() !== DataTypes.Boolean || operands.right.getType() !== DataTypes.Boolean) { - // throw exception; - // } - return new BooleanObject(operands.left.getValue() && operands.right.getValue()); + if(operands.left.getValue()){ + return operands.right; + } + else { + return operands.left; + } case "||": - return new BooleanObject((operands.left.getValue() || operands.right.getValue())==true); - + if(operands.left.getValue()){ + return operands.left; + } + else{ + return operands.right; + } + default: throw new InvalidStateException(`Unsupported operator: ${operator}`); } diff --git a/packages/interpreter/test/visitorUnitTests/helper.test.ts b/packages/interpreter/test/visitorUnitTests/helper.test.ts index 162138c6..68f635bb 100644 --- a/packages/interpreter/test/visitorUnitTests/helper.test.ts +++ b/packages/interpreter/test/visitorUnitTests/helper.test.ts @@ -1,5 +1,6 @@ import { RuntimeException } from "../../src"; import InvalidStateException from "../../src/exceptions/invalidStateException"; +import {BooleanObject, DataObject, NullObject, NumericObject, StringObject} from "../../src/components/dataClass"; import { checkNumberOperands, checkStringOperands, @@ -12,8 +13,8 @@ const testCaseProvider = [ { name: "test checkNumberOperands with number oprands, should return true", input: { - left: 45, - right: 67, + left: new NumericObject(45), + right: new NumericObject(67), }, output: true, function: checkNumberOperands, @@ -21,8 +22,8 @@ const testCaseProvider = [ { name: "test checkNumberOperands without number oprands, should return false", input: { - left: "hello", - right: "67", + left: new StringObject("hello"), + right: new StringObject("67"), }, output: false, function: checkNumberOperands, @@ -30,8 +31,8 @@ const testCaseProvider = [ { name: "test checkNumberOperands with one number oprand and one non-number operand, should return false", input: { - left: 90, - right: "67", + left: new NumericObject(90), + right: new StringObject("67"), }, output: false, function: checkNumberOperands, @@ -39,8 +40,8 @@ const testCaseProvider = [ { name: "test checkNumberOperands with one number oprand and one non-number operand - 2, should return false", input: { - left: "67", - right: 5678, + left: new StringObject("67"), + right: new NumericObject(5678), }, output: false, function: checkNumberOperands, @@ -49,8 +50,8 @@ const testCaseProvider = [ { name: "test checkStringOperands with string oprands, should return true", input: { - left: "45", - right: "asdasdas", + left: new StringObject("45"), + right: new StringObject("asdasdas"), }, output: true, function: checkStringOperands, @@ -58,8 +59,8 @@ const testCaseProvider = [ { name: "test checkStringOperands without string oprands, should return false", input: { - left: 23432, - right: null, + left: new NumericObject(23432), + right: new NullObject(), }, output: false, function: checkStringOperands, @@ -67,8 +68,8 @@ const testCaseProvider = [ { name: "test checkStringOperands with one string oprand and one non-string operand, should return false", input: { - left: 90, - right: "67", + left: new NumericObject(90), + right: new StringObject("67"), }, output: false, function: checkStringOperands, @@ -76,419 +77,437 @@ const testCaseProvider = [ { name: "test checkStringOperands with one number string and one non-string operand - 2, should return false", input: { - left: "67", - right: 5678, + left: new StringObject("67"), + right: new NumericObject(5678), }, output: false, function: checkStringOperands, }, ]; -const getOperationValuePosTestCasesProvider = [ +const getOperationValuePosTestCasesProvider:{ + name: string, + input1: { + left: DataObject, + right: DataObject, + }, + input2: string, + output: DataObject, + function: Function, +}[] = [ // getOperationValue tests { name: `test getOperationValue "=" operator with string oprands, should return value of right operand - number`, input1: { - left: 23432, - right: 890, + left: new NumericObject(23432), + right: new NumericObject(890), }, input2: "=", - output: 890, + output: new NumericObject(890), function: getOperationValue, }, { name: `test getOperationValue "=" operator with string oprands, should return value of right operand - null`, input1: { - left: 23432, - right: null, + left: new NumericObject(23432), + right: new NullObject(), }, input2: "=", - output: null, + output: new NullObject(), function: getOperationValue, }, { name: `test getOperationValue "=" operator with string oprands, should return value of right operand - string`, input1: { - left: 23432, - right: "hello", + left: new NumericObject(23432), + right: new StringObject("hello"), }, input2: "=", - output: "hello", + output: new StringObject("hello"), function: getOperationValue, }, { name: `test getOperationValue "+" operator with string oprands, should success`, input1: { - left: "hello", - right: "crap", + left: new StringObject("hello"), + right: new StringObject("crap"), }, input2: "+", - output: "hellocrap", + output: new StringObject("hellocrap"), function: getOperationValue, }, { name: `test getOperationValue "+" operator with number oprands, should success`, input1: { - left: 2, - right: 3, + left: new NumericObject(2), + right: new NumericObject(3), }, input2: "+", - output: 5, + output: new NumericObject(5), function: getOperationValue, }, { name: `test getOperationValue "+" operator with one number and one string oprands, should success`, input1: { - left: 15, - right: "hello", + left: new NumericObject(15), + right: new StringObject("hello"), }, input2: "+", - output: "15hello", + output: new StringObject("15hello"), function: getOperationValue, }, { name: `test getOperationValue "+" operator with second operand number and first string, should success`, input1: { - left: "hello", - right: 15, + left: new StringObject("hello"), + right: new NumericObject(15), }, input2: "+", - output: "hello15", + output: new StringObject("hello15"), function: getOperationValue, }, { name: `test getOperationValue "+" operator with one very large number and one string oprands, should success`, input1: { - left: 15378247823432, - right: "hello", + left: new NumericObject(15378247823432), + right: new StringObject("hello"), }, input2: "+", - output: "15378247823432hello", + output: new StringObject("15378247823432hello"), function: getOperationValue, }, { name: `test getOperationValue "+=" operator with number oprands, should success`, input1: { - left: 2, - right: 3, + left: new NumericObject(2), + right: new NumericObject(3), }, input2: "+=", - output: 5, + output: new NumericObject(5), function: getOperationValue, }, { name: `test getOperationValue "+=" operator with string oprands, should success`, input1: { - left: "hello", - right: "crap", + left: new StringObject("hello"), + right: new StringObject("crap"), }, input2: "+=", - output: "hellocrap", + output: new StringObject("hellocrap"), function: getOperationValue, }, { name: `test getOperationValue "+=" operator with one number and one string oprands, should success`, input1: { - left: 15, - right: "hello", + left: new NumericObject(15), + right: new StringObject("hello"), }, input2: "+=", - output: "15hello", + output: new StringObject("15hello"), function: getOperationValue, }, { name: `test getOperationValue "+=" operator with second operand number and first string, should success`, input1: { - left: "hello", - right: 15, + left: new StringObject("hello"), + right: new NumericObject(15), }, input2: "+=", - output: "hello15", + output: new StringObject("hello15"), function: getOperationValue, }, { name: `test getOperationValue "+=" operator with one very large number and one string oprands, should success`, input1: { - left: 15378247823432, - right: "hello", + left: new NumericObject(15378247823432), + right: new StringObject("hello"), }, input2: "+", - output: "15378247823432hello", + output: new StringObject("15378247823432hello"), function: getOperationValue, }, { name: `test getOperationValue "-" operator with number oprands, should success`, input1: { - left: 5, - right: 3, + left: new NumericObject(5), + right: new NumericObject(3), }, input2: "-", - output: 2, + output: new NumericObject(2), function: getOperationValue, }, { name: `test getOperationValue "-=" operator with number oprands, should success`, input1: { - left: 5, - right: 3, + left: new NumericObject(5), + right: new NumericObject(3), }, input2: "-=", - output: 2, + output: new NumericObject(2), function: getOperationValue, }, { name: `test getOperationValue "*=" operator with number oprands, should success`, input1: { - left: 5, - right: 3, + left: new NumericObject(5), + right: new NumericObject(3), }, input2: "*=", - output: 15, + output: new NumericObject(15), function: getOperationValue, }, { name: `test getOperationValue "*" operator with number oprands, should success`, input1: { - left: 5, - right: 3, + left: new NumericObject(5), + right: new NumericObject(3), }, input2: "*", - output: 15, + output: new NumericObject(15), function: getOperationValue, }, { name: `test getOperationValue "/=" operator with number oprands, should success`, input1: { - left: 15, - right: 3, + left: new NumericObject(15), + right: new NumericObject(3), }, input2: "/=", - output: 5, + output: new NumericObject(5), function: getOperationValue, }, { name: `test getOperationValue "/" operator with number oprands, should success`, input1: { - left: 15, - right: 3, + left: new NumericObject(15), + right: new NumericObject(3), }, input2: "/", - output: 5, + output: new NumericObject(5), function: getOperationValue, }, { name: `test getOperationValue "==" operator with number oprands, should success`, input1: { - left: 3, - right: 3, + left: new NumericObject(3), + right: new NumericObject(3), }, input2: "==", - output: true, + output: new BooleanObject(true), function: getOperationValue, }, { name: `test getOperationValue "==" operator with number oprands unequal, should success`, input1: { - left: 5, - right: 3, + left: new NumericObject(5), + right: new NumericObject(3), }, input2: "==", - output: false, + output: new BooleanObject(false), function: getOperationValue, }, { name: `test getOperationValue "==" operator with string oprands, should success`, input1: { - left: "hell", - right: "hell", + left: new StringObject("hell"), + right: new StringObject("hell"), }, input2: "==", - output: true, + output: new BooleanObject(true), function: getOperationValue, }, { name: `test getOperationValue "==" operator with string oprands unequal, should success`, input1: { - left: "crap", - right: "hell", + left: new StringObject("crap"), + right: new StringObject("hell"), }, input2: "==", - output: false, + output: new BooleanObject(false), function: getOperationValue, }, { name: `test getOperationValue "==" operator with one string & one number, should success`, input1: { - left: 15, - right: "hell", + left: new NumericObject(15), + right: new StringObject("hell"), }, input2: "==", - output: false, + output: new BooleanObject(false), function: getOperationValue, }, // != { name: `test getOperationValue "!=" operator with number oprands, should success`, input1: { - left: 3, - right: 3, + left: new NumericObject(3), + right: new NumericObject(3), }, input2: "!=", - output: false, + output: new BooleanObject(false), function: getOperationValue, }, { name: `test getOperationValue "!=" operator with number oprands unequal, should success`, input1: { - left: 5, - right: 3, + left: new NumericObject(5), + right: new NumericObject(3), }, input2: "!=", - output: true, + output: new BooleanObject(true), function: getOperationValue, }, { name: `test getOperationValue "!=" operator with string oprands, should success`, input1: { - left: "hell", - right: "hell", + left: new StringObject("hell"), + right: new StringObject("hell"), }, input2: "!=", - output: false, + output: new BooleanObject(false), function: getOperationValue, }, { name: `test getOperationValue "!=" operator with string oprands unequal, should success`, input1: { - left: "crap", - right: "hell", + left: new StringObject("crap"), + right: new StringObject("hell"), }, input2: "!=", - output: true, + output: new BooleanObject(true), function: getOperationValue, }, { name: `test getOperationValue "!=" operator with one string & one number, should success`, input1: { - left: 15, - right: "hell", + left: new NumericObject(15), + right: new StringObject("hell"), }, input2: "!=", - output: true, + output: new BooleanObject(true), function: getOperationValue, }, // > { name: `test getOperationValue ">" operator with number oprands, should success`, input1: { - left: 3, - right: 3, + left: new NumericObject(3), + right: new NumericObject(3), }, input2: ">", - output: false, + output: new BooleanObject(false), function: getOperationValue, }, { name: `test getOperationValue ">" operator with number oprands left greater, should success`, input1: { - left: 5, - right: 3, + left: new NumericObject(5), + right: new NumericObject(3), }, input2: ">", - output: true, + output: new BooleanObject(true), function: getOperationValue, }, // < { name: `test getOperationValue "<" operator with number oprands, should success`, input1: { - left: 3, - right: 3, + left: new NumericObject(3), + right: new NumericObject(3), }, input2: "<", - output: false, + output: new BooleanObject(false), function: getOperationValue, }, { name: `test getOperationValue "<" operator with number oprands left smaller, should success`, input1: { - left: 1, - right: 3, + left: new NumericObject(1), + right: new NumericObject(3), }, input2: "<", - output: true, + output: new BooleanObject(true), function: getOperationValue, }, // >= { name: `test getOperationValue ">=" operator with number oprands equal, should success`, input1: { - left: 3, - right: 3, + left: new NumericObject(3), + right: new NumericObject(3), }, input2: ">=", - output: true, + output: new BooleanObject(true), function: getOperationValue, }, { name: `test getOperationValue ">=" operator with number oprands left greater, should success`, input1: { - left: 5, - right: 3, + left: new NumericObject(5), + right: new NumericObject(3), }, input2: ">=", - output: true, + output: new BooleanObject(true), function: getOperationValue, }, { name: `test getOperationValue ">=" operator with number oprands left smaller, should success`, input1: { - left: 1, - right: 3, + left: new NumericObject(1), + right: new NumericObject(3), }, input2: ">=", - output: false, + output: new BooleanObject(false), function: getOperationValue, }, // <= { name: `test getOperationValue "<=" operator with number oprands equal, should success`, input1: { - left: 3, - right: 3, + left: new NumericObject(3), + right: new NumericObject(3), }, input2: "<=", - output: true, + output: new BooleanObject(true), function: getOperationValue, }, { name: `test getOperationValue "<=" operator with number oprands left greater, should success`, input1: { - left: 5, - right: 3, + left: new NumericObject(5), + right: new NumericObject(3), }, input2: "<=", - output: false, + output: new BooleanObject(false), function: getOperationValue, }, { name: `test getOperationValue "<=" operator with number oprands left smaller, should success`, input1: { - left: 1, - right: 3, + left: new NumericObject(1), + right: new NumericObject(3), }, input2: "<=", - output: true, + output: new BooleanObject(true), function: getOperationValue, } ]; -const getOperationValueNegTestCasesProvider = [ +const getOperationValueNegTestCasesProvider:{ + name: string, + input1: { + left: DataObject, + right: DataObject, + }, + input2: string, + function: Function, + exception: any, +}[] = [ { name: `test getOperationValue "+" operator with one boolean and one string oprands, should throw an exception`, input1: { - left: true, - right: "hello", + left: new BooleanObject(true), + right: new StringObject("hello"), }, input2: "+", exception: RuntimeException, @@ -497,8 +516,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "+" operator with second operand boolean and first string, should throw an exception`, input1: { - left: "true", - right: false, + left: new StringObject("true"), + right: new BooleanObject(false), }, input2: "+", exception: RuntimeException, @@ -507,8 +526,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "+=" operator with one boolean and one string oprands, should throw an exception`, input1: { - left: true, - right: "hello", + left: new BooleanObject(true), + right: new StringObject("hello"), }, input2: "+=", exception: RuntimeException, @@ -517,8 +536,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "+=" operator with second operand boolean and first string, should throw an exception`, input1: { - left: "true", - right: false, + left: new StringObject("true"), + right: new BooleanObject(false), }, input2: "+=", exception: RuntimeException, @@ -527,8 +546,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "-" operator with one number and one string oprands, should throw an exception`, input1: { - left: 15, - right: "hello", + left: new NumericObject(15), + right: new StringObject("hello"), }, input2: "-", exception: RuntimeException, @@ -537,8 +556,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "-=" operator with one number and one string oprands, should throw an exception`, input1: { - left: 15, - right: "hello", + left: new NumericObject(15), + right: new StringObject("hello"), }, input2: "-=", exception: RuntimeException, @@ -547,8 +566,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "-=" operator with both strings oprands, should throw an exception`, input1: { - left: "15", - right: "hello", + left: new StringObject("15"), + right: new StringObject("hello"), }, input2: "-=", exception: RuntimeException, @@ -557,8 +576,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "*" operator with one number and one string oprands, should throw an exception`, input1: { - left: 15, - right: "hello", + left: new NumericObject(15), + right: new StringObject("hello"), }, input2: "*", exception: RuntimeException, @@ -567,8 +586,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "*=" operator with one number and one string oprands, should throw an exception`, input1: { - left: 15, - right: "hello", + left: new NumericObject(15), + right: new StringObject("hello"), }, input2: "*=", exception: RuntimeException, @@ -577,8 +596,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "/" operator with one number and one string oprands, should throw an exception`, input1: { - left: 15, - right: "hello", + left: new NumericObject(15), + right: new StringObject("hello"), }, input2: "/", exception: RuntimeException, @@ -587,8 +606,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "/=" operator with one number and one string oprands, should throw an exception`, input1: { - left: 15, - right: "hello", + left: new NumericObject(15), + right: new StringObject("hello"), }, input2: "/=", exception: RuntimeException, @@ -597,8 +616,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "/" operator with zero divisor, should throw an exception`, input1: { - left: 15, - right: 0, + left: new NumericObject(15), + right: new NumericObject(0), }, input2: "/", exception: RuntimeException, @@ -607,8 +626,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "/=" operator with zero divisor, should throw an exception`, input1: { - left: 15, - right: 0, + left: new NumericObject(15), + right: new NumericObject(0), }, input2: "/", exception: RuntimeException, @@ -617,8 +636,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "#" operator, should throw an exception`, input1: { - left: 15, - right: 5, + left: new NumericObject(15), + right: new NumericObject(5), }, input2: "#", exception: InvalidStateException, @@ -627,8 +646,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue ">" operator with one string & one number, should throw an exception`, input1: { - left: 15, - right: "hell", + left: new NumericObject(15), + right: new StringObject("hell"), }, input2: ">", exception: RuntimeException, @@ -637,8 +656,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue ">" operator with both string , should throw an exception`, input1: { - left: "cap", - right: "hell", + left: new StringObject("cap"), + right: new StringObject("hell"), }, input2: ">", exception: RuntimeException, @@ -647,8 +666,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "<" operator with one string & one number, should throw an exception`, input1: { - left: 15, - right: "hell", + left: new NumericObject(15), + right: new StringObject("hell"), }, input2: "<", exception: RuntimeException, @@ -657,8 +676,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "<" operator with both string , should throw an exception`, input1: { - left: "cap", - right: "hell", + left: new StringObject("cap"), + right: new StringObject("hell"), }, input2: "<", exception: RuntimeException, @@ -667,8 +686,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue ">=" operator with one string & one number, should throw an exception`, input1: { - left: 15, - right: "hell", + left: new NumericObject(15), + right: new StringObject("hell"), }, input2: ">=", exception: RuntimeException, @@ -677,8 +696,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue ">=" operator with both string , should throw an exception`, input1: { - left: "cap", - right: "hell", + left: new StringObject("cap"), + right: new StringObject("hell"), }, input2: ">=", exception: RuntimeException, @@ -687,8 +706,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "<=" operator with one string & one number, should throw an exception`, input1: { - left: 15, - right: "hell", + left: new NumericObject(15), + right: new StringObject("hell"), }, input2: "<=", exception: RuntimeException, @@ -697,8 +716,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "<=" operator with both string , should throw an exception`, input1: { - left: "cap", - right: "hell", + left: new StringObject("cap"), + right: new StringObject("hell"), }, input2: "<=", exception: RuntimeException, @@ -707,8 +726,8 @@ const getOperationValueNegTestCasesProvider = [ { name: `test getOperationValue "**" operator with unsupported operator , should throw an exception`, input1: { - left: "cap", - right: "hell", + left: new StringObject("cap"), + right: new StringObject("hell"), }, input2: "**", exception: InvalidStateException, diff --git a/packages/interpreter/test/visitorUnitTests/unitTestsForNegativeCases.test.ts b/packages/interpreter/test/visitorUnitTests/unitTestsForNegativeCases.test.ts index 38a3f306..63f72aae 100644 --- a/packages/interpreter/test/visitorUnitTests/unitTestsForNegativeCases.test.ts +++ b/packages/interpreter/test/visitorUnitTests/unitTestsForNegativeCases.test.ts @@ -1,6 +1,7 @@ import { NodeType } from "bhai-lang-parser"; import { RuntimeException } from "../../src"; +import { NumericObject } from "../../src/components/dataClass"; import Scope from "../../src/components/scope"; import AssignmentExpression from "../../src/components/visitor/assignmentExpression"; import BinaryExpression from "../../src/components/visitor/binaryExpression"; @@ -160,7 +161,7 @@ test("interpreter test PrintStatement without expressions should throw an except }); test("interpreter test Scope assign with undeclared variable should throw an exception", () => { - expect(() => scope.assign("undeclared_identifier", 45)).toThrow( + expect(() => scope.assign("undeclared_identifier", new NumericObject(45))).toThrow( RuntimeException ); }); From 1058b1f801999f30e88ceaa1389a22d72eeac465 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sun, 10 Apr 2022 00:32:01 +0530 Subject: [PATCH 19/22] added requested changes --- .../statement/expression/callableExpression.ts | 1 - .../parser/statement/functionStatement.ts | 16 ++++++++-------- packages/parser/src/constants/bhaiLangSpec.ts | 2 +- packages/parser/src/module/bhaiLangModule.ts | 2 +- 4 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/parser/src/components/parser/statement/expression/callableExpression.ts b/packages/parser/src/components/parser/statement/expression/callableExpression.ts index 172eea19..b14fdfa6 100644 --- a/packages/parser/src/components/parser/statement/expression/callableExpression.ts +++ b/packages/parser/src/components/parser/statement/expression/callableExpression.ts @@ -16,7 +16,6 @@ export default class CallableExpression extends Expression { args.push(this._getArgs()); } while ( this._tokenExecutor.getLookahead()?.type === TokenTypes.COMMA_TYPE && - this._tokenExecutor.getLookahead()?.type !== TokenTypes.CLOSED_PARENTHESIS_TYPE&& this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.COMMA_TYPE) ); } diff --git a/packages/parser/src/components/parser/statement/functionStatement.ts b/packages/parser/src/components/parser/statement/functionStatement.ts index 4023a112..27573078 100644 --- a/packages/parser/src/components/parser/statement/functionStatement.ts +++ b/packages/parser/src/components/parser/statement/functionStatement.ts @@ -23,8 +23,6 @@ export default class FunctionStatement extends Statement { const declaration = this._getFunctionDeclaration(); - // this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.SEMI_COLON_TYPE); - return { type: NodeType.FunctionStatement, declaration, @@ -33,21 +31,23 @@ export default class FunctionStatement extends Statement { private _getFunctionDeclaration(): ASTNode { - const id = Expression.getExpressionImpl( - NodeType.CallableExpression - ).getExpression(); + const functionSigneture = Expression.getExpressionImpl( NodeType.CallableExpression).getExpression(); + let lookahead=this._tokenExecutor.getLookahead() + if (lookahead == null) { - throw new SyntaxError(`Unexpected end of "jab tak bhai" statement`); + throw new SyntaxError(`Unexpected end of "apna funda" statement`); } + if(lookahead.type!==TokenTypes.OPEN_CURLY_BRACE_TYPE){ - throw new SyntaxError(`Unexpected token after funda signature ${id.name}, got "${lookahead.value}" : expected "{"`); + throw new SyntaxError(`Unexpected token after funda signature ${functionSigneture.name}, got "${lookahead.value}" : expected "{"`); } + const body=Statement.getStatementImpl(this._tokenExecutor.getLookahead()!).getStatement(); return { type: NodeType.FunctionDeclaration, - id, + declaration:functionSigneture, body }; } diff --git a/packages/parser/src/constants/bhaiLangSpec.ts b/packages/parser/src/constants/bhaiLangSpec.ts index 42af69b6..428a7576 100644 --- a/packages/parser/src/constants/bhaiLangSpec.ts +++ b/packages/parser/src/constants/bhaiLangSpec.ts @@ -100,7 +100,7 @@ export const SPEC = [ //functional programming { regex: /^\bapna funda\b/, tokenType: TokenTypes.FUNDA_TYPE }, { regex: /^\brakh le bhai\b/, tokenType: TokenTypes.RAKH_LE_BHAI }, - { regex: /^\w+(?=\(.*\))/, tokenType: TokenTypes.CALLABLE_TYPE }, + { regex: /^\w+(?=[ ]*\(.*\))/, tokenType: TokenTypes.CALLABLE_TYPE }, // Number { regex: /^-?\d+/, tokenType: TokenTypes.NUMBER_TYPE }, diff --git a/packages/parser/src/module/bhaiLangModule.ts b/packages/parser/src/module/bhaiLangModule.ts index c768e5f7..5e7f4017 100644 --- a/packages/parser/src/module/bhaiLangModule.ts +++ b/packages/parser/src/module/bhaiLangModule.ts @@ -84,7 +84,7 @@ export default class BhaiLangModule { private static _continueStatement?: ContinueStatement; private static _whileStatement?: WhileStatement; private static _functionStatement: FunctionStatement; - static _returnStatement: ReturnStatement; + private static _returnStatement: ReturnStatement; static getTokenizer() { if (!this._tokenizer) this._tokenizer = new TokenizerImpl(SPEC); From 9c333138bf5b84dc63eb6185ec8111ecf925c1cf Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 30 Jul 2022 12:51:44 +0530 Subject: [PATCH 20/22] fixed function argument parsing --- .../components/visitor/functionDeclaration.ts | 10 ++-- .../parser/statement/functionStatement.ts | 57 ++++++++++++++++++- .../src/components/parser/types/nodeTypes.ts | 1 + packages/parser/src/constants/constants.ts | 1 + 4 files changed, 62 insertions(+), 7 deletions(-) diff --git a/packages/interpreter/src/components/visitor/functionDeclaration.ts b/packages/interpreter/src/components/visitor/functionDeclaration.ts index bff95e13..a5b8de01 100644 --- a/packages/interpreter/src/components/visitor/functionDeclaration.ts +++ b/packages/interpreter/src/components/visitor/functionDeclaration.ts @@ -8,18 +8,18 @@ import { CallableObject, DataObject } from "../dataClass"; export default class FunctionDeclaration implements Visitor { visitNode(node: ASTNode) { - if (!node.id || !node.body||!node) { + if (!node.signature || !node.body||!node) { throw new InvalidStateException(`id or body not found for ${node.type}`); } - const identifier = node.id.name; + const functionName = node.signature.name let value; const body = node.body; if (body && !Array.isArray(body)) { let scope=InterpreterModule.getCurrentScope() value={ - args:node.id.args?.map(arg=>arg.name), + args:node.signature.args?.map(arg=>arg.id?.name), code:(args:{identifier:string,value:DataObject}[]):any=>{ let oldScope=InterpreterModule.getCurrentScope() let newScope=new Scope(scope) @@ -37,8 +37,8 @@ export default class FunctionDeclaration implements Visitor { } const currentScope = InterpreterModule.getCurrentScope(); - if (identifier) { - currentScope.declare(identifier, new CallableObject(value)); + if (functionName) { + currentScope.declare(functionName, new CallableObject(value)); } } } diff --git a/packages/parser/src/components/parser/statement/functionStatement.ts b/packages/parser/src/components/parser/statement/functionStatement.ts index 27573078..0a6c3aef 100644 --- a/packages/parser/src/components/parser/statement/functionStatement.ts +++ b/packages/parser/src/components/parser/statement/functionStatement.ts @@ -31,7 +31,8 @@ export default class FunctionStatement extends Statement { private _getFunctionDeclaration(): ASTNode { - const functionSigneture = Expression.getExpressionImpl( NodeType.CallableExpression).getExpression(); + + const functionSigneture = this._getFunctionSignature(); let lookahead=this._tokenExecutor.getLookahead() @@ -47,8 +48,60 @@ export default class FunctionStatement extends Statement { return { type: NodeType.FunctionDeclaration, - declaration:functionSigneture, + signature:functionSigneture, body }; } + + private _getFunctionSignature(): ASTNode { + const functionName = this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.CALLABLE_TYPE).value; + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.OPEN_PARENTHESIS_TYPE); + const args=this._getFunctionArguments(); + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.CLOSED_PARENTHESIS_TYPE); + return { + type: NodeType.FunctionSignature, + name:functionName, + args + }; + } + + private _getFunctionArguments(): ASTNode[] { + const declarations: ASTNode[] = []; + do { + declarations.push(this._getArgumentDeclaration()); + } while ( + this._tokenExecutor.getLookahead()?.type === TokenTypes.COMMA_TYPE && + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.COMMA_TYPE) + ); + return declarations; + } + + private _getArgumentDeclaration(): ASTNode { + const id = Expression.getExpressionImpl( + NodeType.IdentifierExpression + ).getExpression(); + + // Optional VariableInitializer + const init = + this._tokenExecutor.getLookahead()?.type !== TokenTypes.CLOSED_PARENTHESIS_TYPE && + this._tokenExecutor.getLookahead()?.type !== TokenTypes.COMMA_TYPE + ? this._getArgumentInitializer() + : this._nullLiteral.getLiteral(); + + return { + type: NodeType.VariableDeclaration, + id, + init, + }; + } + private _getArgumentInitializer() { + this._tokenExecutor.eatTokenAndForwardLookahead( + TokenTypes.SIMPLE_ASSIGN_TYPE + ); + + return Expression.getExpressionImpl( + NodeType.PrimaryExpression + ).getExpression(); + } + } diff --git a/packages/parser/src/components/parser/types/nodeTypes.ts b/packages/parser/src/components/parser/types/nodeTypes.ts index c00b4937..a0b3fa2c 100644 --- a/packages/parser/src/components/parser/types/nodeTypes.ts +++ b/packages/parser/src/components/parser/types/nodeTypes.ts @@ -16,4 +16,5 @@ export type ASTNode = { consequent?: ASTNode; alternates?: ASTNode[]; args?: ASTNode[]; + signature?: ASTNode; }; diff --git a/packages/parser/src/constants/constants.ts b/packages/parser/src/constants/constants.ts index 383b3123..78650c45 100644 --- a/packages/parser/src/constants/constants.ts +++ b/packages/parser/src/constants/constants.ts @@ -29,6 +29,7 @@ export const NodeType = { VariableDeclaration: "VariableDeclaration", FunctionStatement: "FunctionStatement", FunctionDeclaration: "FunctionDeclaration", + FunctionSignature: "FunctionSignature", ReturnStatement: "ReturnStatement", Program: "Program", } as const; From 567b961b0999fe40a782a979026dbed744f652ed Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Sat, 30 Jul 2022 14:09:54 +0530 Subject: [PATCH 21/22] added test cases --- .../src/components/visitor/returnStatement.ts | 6 +++ .../parser/statement/functionStatement.ts | 7 ++- .../parser/statement/returnStatement.ts | 10 ++-- .../test/integration/negativeTestsHelper.ts | 22 ++++++++- .../test/integration/positiveTestsHelper.ts | 49 ++++++++++++++++++- packages/parser/test/parser/statement.test.ts | 35 +++++++++++-- .../test/tokenizer/tokenizerImpl.test.ts | 1 + 7 files changed, 118 insertions(+), 12 deletions(-) diff --git a/packages/interpreter/src/components/visitor/returnStatement.ts b/packages/interpreter/src/components/visitor/returnStatement.ts index bdace53d..3aa2c1ee 100644 --- a/packages/interpreter/src/components/visitor/returnStatement.ts +++ b/packages/interpreter/src/components/visitor/returnStatement.ts @@ -4,10 +4,12 @@ import { ASTNode } from "bhai-lang-parser"; import InvalidStateException from "../../exceptions/invalidStateException"; import InterpreterModule from "../../module/interpreterModule"; import { sanatizeData } from "../dataClass"; +import RuntimeException from "../../exceptions/runtimeException"; export default class ReturnStatement implements Visitor { visitNode(node: ASTNode) { + if (InterpreterModule.getCurrentScope().isFunction()) { if (!node.expression) throw new InvalidStateException( `No expressions to print: ${node.expressions}` @@ -15,5 +17,9 @@ export default class ReturnStatement implements Visitor { let retVal= sanatizeData(InterpreterModule.getVisitor(node.expression.type).visitNode(node.expression)); InterpreterModule.getCurrentScope().setReturnStatement(true,retVal); return retVal; + } + else{ + throw new RuntimeException(`Kya "rakh le bhai"?? Funda kidhar hai?`); + } } } diff --git a/packages/parser/src/components/parser/statement/functionStatement.ts b/packages/parser/src/components/parser/statement/functionStatement.ts index 0a6c3aef..462a4b1a 100644 --- a/packages/parser/src/components/parser/statement/functionStatement.ts +++ b/packages/parser/src/components/parser/statement/functionStatement.ts @@ -56,7 +56,12 @@ export default class FunctionStatement extends Statement { private _getFunctionSignature(): ASTNode { const functionName = this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.CALLABLE_TYPE).value; this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.OPEN_PARENTHESIS_TYPE); - const args=this._getFunctionArguments(); + + let args:ASTNode[]=[] + if(this._tokenExecutor.getLookahead()?.type!=TokenTypes.CLOSED_PARENTHESIS_TYPE){ + args=this._getFunctionArguments(); + } + this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.CLOSED_PARENTHESIS_TYPE); return { type: NodeType.FunctionSignature, diff --git a/packages/parser/src/components/parser/statement/returnStatement.ts b/packages/parser/src/components/parser/statement/returnStatement.ts index 68e36931..5192bbc6 100644 --- a/packages/parser/src/components/parser/statement/returnStatement.ts +++ b/packages/parser/src/components/parser/statement/returnStatement.ts @@ -5,17 +5,19 @@ import { NodeType } from "../../../constants/constants"; import { ASTNode } from "../types/nodeTypes"; import Expression from "./expression"; +import NullLiteral from "./expression/literals/nullLiteral"; export default class ReturnStatement extends Statement { getStatement(): ASTNode { this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.RAKH_LE_BHAI); - - const value = Expression.getExpressionImpl( - NodeType.AssignmentExpression + let value:ASTNode = new NullLiteral(this._tokenExecutor).getLiteral(); + if (this._tokenExecutor.getLookahead()?.type!==TokenTypes.SEMI_COLON_TYPE) + value = Expression.getExpressionImpl( + NodeType.AssignmentExpression ).getExpression(); - + this._tokenExecutor.eatOptionalSemiColonToken(); return { type: NodeType.ReturnStatement, expression: value, diff --git a/packages/parser/test/integration/negativeTestsHelper.ts b/packages/parser/test/integration/negativeTestsHelper.ts index 831e1621..48e56975 100644 --- a/packages/parser/test/integration/negativeTestsHelper.ts +++ b/packages/parser/test/integration/negativeTestsHelper.ts @@ -178,6 +178,26 @@ export const NegativeStatementTests = [ `, output: SyntaxError, }, + // Function statement negative tests + { + name: "Function statement test - bad augument syntax", + input: ` + hi bhai + apna funda add(a+b){ + rakh le bhai; + } + bye bhai + `, + output: SyntaxError, + },{ + name: "Function statement test - no body", + input: ` + hi bhai + apna funda add(a,b) + bye bhai + `, + output: SyntaxError, + }, ]; export const NegativeExpressionsTests = [ @@ -414,4 +434,4 @@ export const ContinueStatementNegativeTests = [ `, output: SyntaxError, }, -] \ No newline at end of file +] diff --git a/packages/parser/test/integration/positiveTestsHelper.ts b/packages/parser/test/integration/positiveTestsHelper.ts index b502efce..d14b2a1a 100644 --- a/packages/parser/test/integration/positiveTestsHelper.ts +++ b/packages/parser/test/integration/positiveTestsHelper.ts @@ -684,5 +684,52 @@ export const WhileStatementTests = [ bye bhai; `, output: `{"type":"Program","body":{"type":"InitStatement","body":[{"type":"WhileStatement","test":{"type":"BinaryExpression","operator":">","left":{"type":"IdentifierExpression","name":"x"},"right":{"type":"NumericLiteral","value":9}},"body":{"type":"BlockStatement","body":[{"type":"ContinueStatement"},{"type":"EmptyStatement"}]}},{"type":"VariableStatement","declarations":[{"type":"VariableDeclaration","id":{"type":"IdentifierExpression","name":"a"},"init":{"type":"NumericLiteral","value":90}}]}]}}`, - }, + } ]; + +export const FunctionStatementTests = [ + { + name: "function statement success test: function calling with print statement", + input: ` + hi bhai + apna funda janam(nam){ + bol bhai nam; + } + janam("test"); + bye bhai; + `, + output: `{"type":"Program","body":{"type":"InitStatement","body":[{"type":"FunctionStatement","declaration":{"type":"FunctionDeclaration","signature":{"type":"FunctionSignature","name":"janam","args":[{"type":"VariableDeclaration","id":{"type":"IdentifierExpression","name":"nam"},"init":{"type":"NullLiteral","value":"nalla"}}]},"body":{"type":"BlockStatement","body":[{"type":"PrintStatement","expressions":[{"type":"IdentifierExpression","name":"nam"}]}]}}},{"type":"ExpressionStatement","expression":{"type":"CallableExpression","name":"janam","args":[{"type":"StringLiteral","value":"test"}]}}]}}`, + }, + ,{ + name: "function statement success test: function calling with return statement", + input: ` + hi bhai + apna funda add(a,b){ + rakh le bhai a+b; + } + bol bhai add(10,20); + bye bhai; + `, + output: `{"type":"Program","body":{"type":"InitStatement","body":[{"type":"FunctionStatement","declaration":{"type":"FunctionDeclaration","signature":{"type":"FunctionSignature","name":"add","args":[{"type":"VariableDeclaration","id":{"type":"IdentifierExpression","name":"a"},"init":{"type":"NullLiteral","value":"nalla"}},{"type":"VariableDeclaration","id":{"type":"IdentifierExpression","name":"b"},"init":{"type":"NullLiteral","value":"nalla"}}]},"body":{"type":"BlockStatement","body":[{"type":"ReturnStatement","expression":{"type":"BinaryExpression","operator":"+","left":{"type":"IdentifierExpression","name":"a"},"right":{"type":"IdentifierExpression","name":"b"}}},{"type":"EmptyStatement"}]}}},{"type":"PrintStatement","expressions":[{"type":"CallableExpression","name":"add","args":[{"type":"NumericLiteral","value":10},{"type":"NumericLiteral","value":20}]}]}]}}`, + },{ + name: "function statement success test: function closures", + input: ` + hi bhai + apna funda Counter() { + bhai ye hai count = 1; + + apna funda increment() { + count += 1; + rakh le bhai count; + } + + rakh le bhai increment; + } + + bhai ye hai tick = Counter(); + bol bhai tick(); + bye bhai; + `, + output: `{"type":"Program","body":{"type":"InitStatement","body":[{"type":"FunctionStatement","declaration":{"type":"FunctionDeclaration","signature":{"type":"FunctionSignature","name":"Counter","args":[]},"body":{"type":"BlockStatement","body":[{"type":"VariableStatement","declarations":[{"type":"VariableDeclaration","id":{"type":"IdentifierExpression","name":"count"},"init":{"type":"NumericLiteral","value":1}}]},{"type":"FunctionStatement","declaration":{"type":"FunctionDeclaration","signature":{"type":"FunctionSignature","name":"increment","args":[]},"body":{"type":"BlockStatement","body":[{"type":"ExpressionStatement","expression":{"type":"AssignmentExpression","operator":"+=","left":{"type":"IdentifierExpression","name":"count"},"right":{"type":"NumericLiteral","value":1}}},{"type":"ReturnStatement","expression":{"type":"IdentifierExpression","name":"count"}},{"type":"EmptyStatement"}]}}},{"type":"ReturnStatement","expression":{"type":"IdentifierExpression","name":"increment"}},{"type":"EmptyStatement"}]}}},{"type":"VariableStatement","declarations":[{"type":"VariableDeclaration","id":{"type":"IdentifierExpression","name":"tick"},"init":{"type":"CallableExpression","name":"Counter","args":[]}}]},{"type":"PrintStatement","expressions":[{"type":"CallableExpression","name":"tick","args":[]}]}]}}`, + }, +] diff --git a/packages/parser/test/parser/statement.test.ts b/packages/parser/test/parser/statement.test.ts index 7e9f6d70..5c2a99a6 100644 --- a/packages/parser/test/parser/statement.test.ts +++ b/packages/parser/test/parser/statement.test.ts @@ -1,19 +1,21 @@ import Statement from "../../src/components/parser/statement"; import BlockStatement from "../../src/components/parser/statement/blockStatement"; +import FunctionStatement from "../../src/components/parser/statement/functionStatement"; import { TokenTypes } from "../../src/constants/bhaiLangSpec"; import BhaiLangModule from "../../src/module/bhaiLangModule"; jest.mock("../../src/module/bhaiLangModule"); -const blockStatementMock = new (( - BlockStatement -))() as jest.Mocked; + afterEach(() => { jest.clearAllMocks(); }); test("test getStatementImpl of statement class with should success", () => { + const statementMock = new (( + BlockStatement + ))() as jest.Mocked; const lookahead = { type: TokenTypes.OPEN_CURLY_BRACE_TYPE, value: "{", @@ -21,11 +23,34 @@ test("test getStatementImpl of statement class with should success", () => { BhaiLangModule.getBlockStatement = jest .fn() - .mockReturnValue(blockStatementMock); + .mockReturnValue(statementMock); expect(Statement.getStatementImpl(lookahead)).toStrictEqual( - blockStatementMock + statementMock ); expect(BhaiLangModule.getBlockStatement).toHaveBeenCalledTimes(1); }); + +test("test getStatementImpl of function declaration statement should success", () => { + const statementMock = new (( + FunctionStatement + ))() as jest.Mocked; + const lookahead = { + type: TokenTypes.FUNDA_TYPE, + value: `apna funda testing(a,b){ + bol bhai a; + }`, + }; + + BhaiLangModule.getFunctionStatement = jest + .fn() + .mockReturnValue(statementMock); + + expect(Statement.getStatementImpl(lookahead)).toStrictEqual( + statementMock + ); + + expect(BhaiLangModule.getFunctionStatement).toHaveBeenCalledTimes(1); +}); + diff --git a/packages/parser/test/tokenizer/tokenizerImpl.test.ts b/packages/parser/test/tokenizer/tokenizerImpl.test.ts index 3447f3a4..6c36743c 100644 --- a/packages/parser/test/tokenizer/tokenizerImpl.test.ts +++ b/packages/parser/test/tokenizer/tokenizerImpl.test.ts @@ -76,3 +76,4 @@ test("test Tokenizer.hasMoreTokens without initTokenizer should success", () => expect(tokenizer.hasMoreTokens()).toStrictEqual(false); }); + From a2b0d83a4778cf99a4272f41b1f922a58611f871 Mon Sep 17 00:00:00 2001 From: Anirban Kar Date: Mon, 28 Nov 2022 16:01:24 +0530 Subject: [PATCH 22/22] bugfix --- .../src/components/visitor/blockStatement.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/interpreter/src/components/visitor/blockStatement.ts b/packages/interpreter/src/components/visitor/blockStatement.ts index b7ed87c2..a4cf993d 100644 --- a/packages/interpreter/src/components/visitor/blockStatement.ts +++ b/packages/interpreter/src/components/visitor/blockStatement.ts @@ -15,6 +15,8 @@ export default class BlockStatement implements Visitor { if (Array.isArray(node.body)) { node.body.every((statement: ASTNode) => { + InterpreterModule.getVisitor(statement.type).visitNode(statement); + if (InterpreterModule.getCurrentScope().isBreakStatement()) { return false; } @@ -22,12 +24,12 @@ export default class BlockStatement implements Visitor { parentScope.setContinueStatement(true); return false; } - if(InterpreterModule.getCurrentScope().isReturnStatement()&&parentScope.isFunction()){ - let retValue=InterpreterModule.getCurrentScope().getReturnValue(); - parentScope.setReturnStatement(true,retValue); + if (InterpreterModule.getCurrentScope().isReturnStatement() && parentScope.isFunction()) { + let retValue = InterpreterModule.getCurrentScope().getReturnValue(); + parentScope.setReturnStatement(true, retValue); return false; } - InterpreterModule.getVisitor(statement.type).visitNode(statement); + return true; }); }