diff --git a/apps/docs/components/Code/index.tsx b/apps/docs/components/Code/index.tsx
index 4b8467ea..80e50f81 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 c0a51083..12f81c84 100644
--- a/apps/docs/components/Documentation/index.tsx
+++ b/apps/docs/components/Documentation/index.tsx
@@ -96,6 +96,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 using rakh 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 1d2c7c08..14f19589 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|nahi to 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|nahi to 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/dataClass.ts b/packages/interpreter/src/components/dataClass.ts
new file mode 100644
index 00000000..df58dd13
--- /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|unknown):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(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`);
+}
diff --git a/packages/interpreter/src/components/scope.ts b/packages/interpreter/src/components/scope.ts
index af224345..3128d421 100644
--- a/packages/interpreter/src/components/scope.ts
+++ b/packages/interpreter/src/components/scope.ts
@@ -1,17 +1,40 @@
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;
_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(){
+ if(!this._returnVal) this._returnVal=new NullObject();
+ return this._returnVal;
+ }
+
isLoop() {
return this._isLoop;
}
@@ -36,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));
+ return value;
}
if (this._parentScope !== null) {
@@ -48,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;
@@ -64,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 c9909950..3fd1ac7f 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 { sanatizeData } from "../dataClass";
export default class BinaryExpression implements Visitor {
@@ -16,28 +17,17 @@ export default class BinaryExpression implements Visitor {
);
}
- let left, right;
-
// 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 ? (node.left.value == "sahi" ? true : false) : 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(
- 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);
}
@@ -60,12 +50,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 === null) throw nallaException;
+ if (value === null||value.getValue()==null) throw nallaException;
}
if (node.right.type === NodeType.IdentifierExpression && node.right.name) {
const value = InterpreterModule.getCurrentScope().get(node.right.name);
- if (value === null) throw nallaException;
+ if (value === null||value.getValue()==null) throw nallaException;
}
}
@@ -89,30 +79,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;
}
}
-
- private _getNodeValue(node: ASTNode) {
-
- if (node.type === NodeType.NullLiteral)
- return null;
-
- if (node.type === NodeType.BooleanLiteral) {
- return node.value === "sahi" ? true : false;
- }
-
- if (node.type === NodeType.IdentifierExpression && node.name)
- return InterpreterModule.getCurrentScope().get(node.name);
-
- return InterpreterModule.getVisitor(node.type).visitNode(
- node
- );
- }
-
}
diff --git a/packages/interpreter/src/components/visitor/blockStatement.ts b/packages/interpreter/src/components/visitor/blockStatement.ts
index cf0fc382..a4cf993d 100644
--- a/packages/interpreter/src/components/visitor/blockStatement.ts
+++ b/packages/interpreter/src/components/visitor/blockStatement.ts
@@ -11,9 +11,12 @@ 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) => {
+ InterpreterModule.getVisitor(statement.type).visitNode(statement);
+
if (InterpreterModule.getCurrentScope().isBreakStatement()) {
return false;
}
@@ -21,7 +24,12 @@ export default class BlockStatement implements Visitor {
parentScope.setContinueStatement(true);
return false;
}
- InterpreterModule.getVisitor(statement.type).visitNode(statement);
+ if (InterpreterModule.getCurrentScope().isReturnStatement() && parentScope.isFunction()) {
+ let retValue = InterpreterModule.getCurrentScope().getReturnValue();
+ parentScope.setReturnStatement(true, retValue);
+ return false;
+ }
+
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
new file mode 100644
index 00000000..8fb8a91b
--- /dev/null
+++ b/packages/interpreter/src/components/visitor/callableExpression.ts
@@ -0,0 +1,40 @@
+import Visitor from ".";
+import { ASTNode } from "bhai-lang-parser";
+
+import InvalidStateException from "../../exceptions/invalidStateException";
+import InterpreterModule from "../../module/interpreterModule";
+import { NullObject, sanatizeData } from "../dataClass";
+import RuntimeException from "../../exceptions/runtimeException";
+
+export default class CallableExpression implements Visitor {
+ visitNode(node: ASTNode) {
+ if (!node.name) {
+ throw new InvalidStateException(`Invalid node name for: ${node.type}`);
+ }
+
+ 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++) {
+ 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:argv
+ });
+ }
+ else{
+ args.push({
+ identifier:value.args[i],
+ value:new NullObject()
+ });
+ }
+ }
+ }
+ 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..a5b8de01
--- /dev/null
+++ b/packages/interpreter/src/components/visitor/functionDeclaration.ts
@@ -0,0 +1,44 @@
+import Visitor from ".";
+import { ASTNode } from "bhai-lang-parser";
+
+import InvalidStateException from "../../exceptions/invalidStateException";
+import InterpreterModule from "../../module/interpreterModule";
+import Scope from "../scope";
+import { CallableObject, DataObject } from "../dataClass";
+
+export default class FunctionDeclaration implements Visitor {
+ visitNode(node: ASTNode) {
+ if (!node.signature || !node.body||!node) {
+ throw new InvalidStateException(`id or body not found for ${node.type}`);
+ }
+
+ const functionName = node.signature.name
+
+ let value;
+ const body = node.body;
+ if (body && !Array.isArray(body)) {
+ let scope=InterpreterModule.getCurrentScope()
+ value={
+ 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)
+ 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 (functionName) {
+ currentScope.declare(functionName, new CallableObject(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/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 7ee8555a..b7f857f7 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 { sanatizeData } from "../dataClass";
export default class IfStatement implements Visitor {
@@ -19,10 +20,11 @@ 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.getValue()) {
this.evaluateNode(node.consequent, parentScope);
- } else {
+ }
+ else {
const alternates = node.alternates;
if (alternates && alternates.length > 0) {
for (var alternate of alternates) {
@@ -34,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 = InterpreterModule.getVisitor(alternateTest!.type).visitNode(alternateTest);
- if (testResult === true || testResult === "sahi") {
- this.evaluateNode(alternate.consequent, parentScope);
+ const testResult = sanatizeData(InterpreterModule.getVisitor(alternateTest!.type).visitNode(alternateTest));
+ if (testResult.getValue()) {
+ this.evaluateNode(alternate, parentScope);
break;
}
}
+ if (testResult.getValue()) {
+ this.evaluateNode(alternate.consequent, parentScope);
+ break;
+ }
}
}
}
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..5c049abb 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,10 @@ 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));
+ 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..3aa2c1ee
--- /dev/null
+++ b/packages/interpreter/src/components/visitor/returnStatement.ts
@@ -0,0 +1,25 @@
+import Visitor from ".";
+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}`
+ );
+ 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/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..0b2059d4 100644
--- a/packages/interpreter/src/components/visitor/variableDeclaration.ts
+++ b/packages/interpreter/src/components/visitor/variableDeclaration.ts
@@ -1,8 +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 { sanatizeData } from "../dataClass";
export default class VariableDeclaration implements Visitor {
visitNode(node: ASTNode) {
@@ -12,13 +13,7 @@ export default class VariableDeclaration implements Visitor {
const identifier = node.id.name;
- let value;
-
- if (node.init.type === NodeType.NullLiteral) value = null;
- else if (node.init.type === NodeType.BooleanLiteral)
- value = node.init.value === "sahi" ? true : false;
- else
- value = 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 e7037b52..7347bec7 100644
--- a/packages/interpreter/src/components/visitor/whileStatement.ts
+++ b/packages/interpreter/src/components/visitor/whileStatement.ts
@@ -4,13 +4,15 @@ 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 = ()=> sanatizeData(InterpreterModule.getVisitor(test.type).visitNode(test));
+
const parentScope = InterpreterModule.getCurrentScope();
@@ -19,7 +21,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();
+ testResult = getConditionValue(), executions++) {
if (InterpreterModule.getCurrentScope().isBreakStatement()) {
break;
@@ -45,3 +49,4 @@ export default class WhileStatement implements Visitor {
}
}
}
+
diff --git a/packages/interpreter/src/helpers/index.ts b/packages/interpreter/src/helpers/index.ts
index 3b3eb4f7..deedc9c0 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,53 +90,60 @@ 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 "==":
-
- return operands.left === operands.right;
-
+ return new BooleanObject(operands.left.getValue() === operands.right.getValue());
case "!=":
-
- return operands.left !== operands.right;
-
+ return new BooleanObject(operands.left.getValue() !== operands.right.getValue());
+
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.getValue()){
+ return operands.right;
+ }
+ else {
+ return operands.left;
+ }
case "||":
- return operands.left || operands.right;
-
+ if(operands.left.getValue()){
+ return operands.left;
+ }
+ else{
+ return operands.right;
+ }
+
default:
throw new InvalidStateException(`Unsupported operator: ${operator}`);
}
diff --git a/packages/interpreter/src/module/interpreterModule.ts b/packages/interpreter/src/module/interpreterModule.ts
index adef304b..b8fff628 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";
@@ -18,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";
@@ -47,6 +51,11 @@ 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(),
+ [NodeType.ReturnStatement]: new ReturnStatement(),
+
} as Record;
private static _currentScope: Scope;
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
);
});
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..b14fdfa6
--- /dev/null
+++ b/packages/parser/src/components/parser/statement/expression/callableExpression.ts
@@ -0,0 +1,33 @@
+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 {
+ 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.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/statement/functionStatement.ts b/packages/parser/src/components/parser/statement/functionStatement.ts
new file mode 100644
index 00000000..462a4b1a
--- /dev/null
+++ b/packages/parser/src/components/parser/statement/functionStatement.ts
@@ -0,0 +1,112 @@
+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();
+
+ return {
+ type: NodeType.FunctionStatement,
+ declaration,
+ };
+ }
+
+
+ private _getFunctionDeclaration(): ASTNode {
+
+ const functionSigneture = this._getFunctionSignature();
+
+ let lookahead=this._tokenExecutor.getLookahead()
+
+ if (lookahead == null) {
+ 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 ${functionSigneture.name}, got "${lookahead.value}" : expected "{"`);
+ }
+
+ const body=Statement.getStatementImpl(this._tokenExecutor.getLookahead()!).getStatement();
+
+ return {
+ type: NodeType.FunctionDeclaration,
+ signature:functionSigneture,
+ body
+ };
+ }
+
+ private _getFunctionSignature(): ASTNode {
+ const functionName = this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.CALLABLE_TYPE).value;
+ this._tokenExecutor.eatTokenAndForwardLookahead(TokenTypes.OPEN_PARENTHESIS_TYPE);
+
+ 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,
+ 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/statement/index.ts b/packages/parser/src/components/parser/statement/index.ts
index e03106e5..0f5970f7 100644
--- a/packages/parser/src/components/parser/statement/index.ts
+++ b/packages/parser/src/components/parser/statement/index.ts
@@ -39,6 +39,10 @@ export default abstract class Statement {
case TokenTypes.AGLA_DEKH_BHAI:
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..5192bbc6
--- /dev/null
+++ b/packages/parser/src/components/parser/statement/returnStatement.ts
@@ -0,0 +1,27 @@
+import Statement from ".";
+
+import { TokenTypes } from "../../../constants/bhaiLangSpec";
+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);
+ 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,
+ };
+ }
+
+}
\ No newline at end of file
diff --git a/packages/parser/src/components/parser/types/nodeTypes.ts b/packages/parser/src/components/parser/types/nodeTypes.ts
index 022bbf56..a0b3fa2c 100644
--- a/packages/parser/src/components/parser/types/nodeTypes.ts
+++ b/packages/parser/src/components/parser/types/nodeTypes.ts
@@ -11,7 +11,10 @@ export type ASTNode = {
id?: ASTNode;
init?: ASTNode | null;
declarations?: ASTNode[];
+ declaration?: ASTNode;
test?: ASTNode;
consequent?: ASTNode;
alternates?: ASTNode[];
+ args?: ASTNode[];
+ signature?: ASTNode;
};
diff --git a/packages/parser/src/constants/bhaiLangSpec.ts b/packages/parser/src/constants/bhaiLangSpec.ts
index 50525df9..428a7576 100644
--- a/packages/parser/src/constants/bhaiLangSpec.ts
+++ b/packages/parser/src/constants/bhaiLangSpec.ts
@@ -21,6 +21,10 @@ export const TokenTypes = {
AGLA_DEKH_BHAI: "agla dekh bhai",
+ FUNDA_TYPE: "bhai ye apna funda", //functional programming
+
+ RAKH_LE_BHAI:"rakh le bhai",// return statement
+
NALLA_TYPE: "NALLA",
SEMI_COLON_TYPE: ";",
@@ -39,6 +43,8 @@ export const TokenTypes = {
IDENTIFIER_TYPE: "IDENTIFIER",
+ CALLABLE_TYPE: "CALLABLE",
+
SIMPLE_ASSIGN_TYPE: "SIMPLE_ASSIGN",
COMPLEX_ASSIGN_TYPE: "COMPLEX_ASSIGN",
@@ -91,6 +97,11 @@ 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: /^\bapna funda\b/, tokenType: TokenTypes.FUNDA_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 f547d517..78650c45 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,9 @@ export const NodeType = {
StringLiteral: "StringLiteral",
NullLiteral: "NullLiteral",
VariableDeclaration: "VariableDeclaration",
+ FunctionStatement: "FunctionStatement",
+ FunctionDeclaration: "FunctionDeclaration",
+ FunctionSignature: "FunctionSignature",
+ ReturnStatement: "ReturnStatement",
Program: "Program",
} as const;
diff --git a/packages/parser/src/module/bhaiLangModule.ts b/packages/parser/src/module/bhaiLangModule.ts
index 6ddf3199..5e7f4017 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
@@ -35,9 +37,11 @@ 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";
+import ReturnStatement from "../components/parser/statement/returnStatement";
import VariableStatement
from "../components/parser/statement/variableStatement";
import WhileStatement from "../components/parser/statement/whileStatement";
@@ -69,6 +73,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;
@@ -78,6 +83,8 @@ export default class BhaiLangModule {
private static _breakStatement?: BreakStatement;
private static _continueStatement?: ContinueStatement;
private static _whileStatement?: WhileStatement;
+ private static _functionStatement: FunctionStatement;
+ private static _returnStatement: ReturnStatement;
static getTokenizer() {
if (!this._tokenizer) this._tokenizer = new TokenizerImpl(SPEC);
@@ -187,6 +194,24 @@ export default class BhaiLangModule {
return this._variableStatement;
}
+ static getFunctionStatement() {
+ if (!this._functionStatement)
+ this._functionStatement = new FunctionStatement(
+ this.getTokenExecutor(),
+ this.getNullLiteral()
+ );
+
+ return this._functionStatement;
+ }
+ static getReturnStatement() {
+ if (!this._returnStatement)
+ this._returnStatement = new ReturnStatement(
+ this.getTokenExecutor()
+ );
+
+ return this._returnStatement;
+ }
+
static getAdditiveExpression() {
if (!this._additiveExpression) {
@@ -280,6 +305,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());
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);
});
+