diff --git a/BackEnd/Interpreter/Expressions/Expressions.ts b/BackEnd/Interpreter/Expressions/Expressions.ts index 0eb8790..adacff0 100644 --- a/BackEnd/Interpreter/Expressions/Expressions.ts +++ b/BackEnd/Interpreter/Expressions/Expressions.ts @@ -41,7 +41,7 @@ export const evaluate_assignment_expression = ( rhs_value = (rhs as BooleanVal).value; } - const assigner = (node.assignee) as MemberExpr; + const assigner = node.assignee as MemberExpr; const assigner_name = (assigner.object as Identifier).symbol; const index = evaluate(assigner.property, env) as NumberVal; const index_val = index.value; @@ -93,8 +93,10 @@ export const evaluate_member_expression = ( env: Environment, ): RuntimeVal => { const object = evaluate(member.object, env); + // console.log(object); if (object.type === "array") { + // console.log(object.size); const index = evaluate(member.property, env) as NumberVal; if (index.type !== "number") { throw new Error("Array index must be a number"); diff --git a/BackEnd/Interpreter/Statements/Declaration_statements.ts b/BackEnd/Interpreter/Statements/Declaration_statements.ts index 4bd584a..d224f0d 100644 --- a/BackEnd/Interpreter/Statements/Declaration_statements.ts +++ b/BackEnd/Interpreter/Statements/Declaration_statements.ts @@ -3,7 +3,13 @@ import { VariableDeclaration, } from "../../../FrontEnd/AST.ts"; import Environment from "../../Scope/environment.ts"; -import { ArrayVal, MAKE_NUll, RuntimeVal } from "../../values.ts"; +import { + ArrayVal, + MAKE_NUll, + MAKE_NUM, + NumberVal, + RuntimeVal, +} from "../../values.ts"; import { evaluate } from "../interpreter.ts"; /** @@ -34,11 +40,32 @@ export const evaluate_array_declaration = ( declaration: ArrayDeclaration, env: Environment, ): RuntimeVal => { + const size_expr = evaluate(declaration.size, env); + const values_provided = declaration.values.length; + + if (size_expr.type !== "number") { + throw `error from array Shas`; + } + const arr_size = (evaluate(declaration.size, env) as NumberVal).value; + if (arr_size > 10000000) { + throw `Memory limit exceeded`; + } else if (arr_size <= 0) { + throw `Invalid Array size`; + } + if (values_provided > arr_size) { + throw `Size of array exceeded`; + } + let count: number = values_provided; + while (count < arr_size) { + declaration.values.push({ kind: "NumericLiteral", value: 0 }); + count++; + } + const arr = { type: "array", name: declaration.name, values: declaration.values, - size: declaration.size, + size: arr_size, } as ArrayVal; return env.declareVar(declaration.name, arr, false); diff --git a/FrontEnd/AST.ts b/FrontEnd/AST.ts index f07916f..75b90bb 100644 --- a/FrontEnd/AST.ts +++ b/FrontEnd/AST.ts @@ -317,7 +317,7 @@ export interface BreakStatement extends Stmt { export interface ArrayDeclaration extends Stmt { kind: "ArrayDeclaration"; name: string; - size: number; + size: Expr; values: Expr[]; } diff --git a/FrontEnd/Parser.ts b/FrontEnd/Parser.ts index 9a2ad6b..8c8e2ab 100644 --- a/FrontEnd/Parser.ts +++ b/FrontEnd/Parser.ts @@ -81,11 +81,11 @@ export default class Parser { * @returns The consumed token. */ private expect(type: TokenType, err: string) { - const prev = this.tokens.shift() as Token; - if (!prev || prev.type !== type) { - throw "Compiler Error: " + err + " scanned " + prev.value; + const next = this.tokens.shift() as Token; + if (!next || next.type !== type) { + throw `SyntaxError:line:${next.curr_line}: Hey Avenger you just snapped an ERROR! ${err} instead scanned '${next.value}' `; } - return prev; + return next; } /** @@ -107,6 +107,39 @@ export default class Parser { return program; } + /** + * Parses easter egg in AssembleScript + */ + private parse_easter_egg(): Stmt { + switch (this.at().type) { + case TokenType.If: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'if' did you mean 'ifWorthy'`; + case TokenType.Else: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'else' did you mean 'otherwise'`; + case TokenType.While: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'while' did you mean 'fightUntil'`; + case TokenType.For: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'for' did you mean 'wakandFor'`; + case TokenType.Switch: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'switch' did you mean 'multiverse'`; + case TokenType.Case: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'case' did you mean 'madness'`; + case TokenType.Let: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'let' did you mean 'newAvenger'`; + case TokenType.Const: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'const' did you mean 'newEternal'`; + case TokenType.Return: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'return' did you mean 'snap'`; + case TokenType.Break: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'break' did you mean 'endGame'`; + case TokenType.True: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'true' did you mean 'SHIELD'`; + case TokenType.False: + throw `SyntaxError:line:${this.at().curr_line}: Hey Avenger you just snapped an error found an unexpected token 'false' did you mean 'HYDRA'`; + } + throw `Hey Avenger you just snapped an error {If this error displayed it's fault of developer contact them with a screenshot for reward}`; + } + /** * Parses a statement. * @returns The parsed statement. @@ -133,17 +166,39 @@ export default class Parser { return this.parse_function_definition(); case TokenType.Snap: return this.parse_return_statement(); - + //?Easter egg 🥚 + case TokenType.If: + return this.parse_easter_egg(); + case TokenType.Else: + return this.parse_easter_egg(); + case TokenType.While: + return this.parse_easter_egg(); + case TokenType.For: + return this.parse_easter_egg(); + case TokenType.Switch: + return this.parse_easter_egg(); + case TokenType.Case: + return this.parse_easter_egg(); + case TokenType.Let: + return this.parse_easter_egg(); + case TokenType.Const: + return this.parse_easter_egg(); + case TokenType.Return: + return this.parse_easter_egg(); + case TokenType.Break: + return this.parse_easter_egg(); + case TokenType.True: + return this.parse_easter_egg(); + case TokenType.False: + return this.parse_easter_egg(); default: { const expr = this.parse_expr(); - this.expect( - TokenType.Semicolon, - "Expected semicolon at the end of the statement", - ); + this.expect(TokenType.Semicolon, `Expected ';' after expression`); return expr; } } } + /** * Parses a function parameter. * @@ -172,10 +227,7 @@ export default class Parser { "Expected function name", ).value; - this.expect( - TokenType.OpenParen, - "Expected opening parenthesis after function name", - ); + this.expect(TokenType.OpenParen, "Expected '(' after function name"); const params: FunctionParam[] = []; if (this.at().type !== TokenType.CloseParen) { @@ -185,25 +237,16 @@ export default class Parser { } } - this.expect( - TokenType.CloseParen, - "Expected closing parenthesis after function parameters", - ); + this.expect(TokenType.CloseParen, "Expected ')' after function parameters"); - this.expect( - TokenType.OpenBrace, - "Expected opening brace before function body", - ); + this.expect(TokenType.OpenBrace, "Expected '{' before function body"); const body: Stmt[] = []; while (this.at().type !== TokenType.CloseBrace && this.not_eof()) { body.push(this.parse_stmt()); } - this.expect( - TokenType.CloseBrace, - "Expected closing brace after function body", - ); + this.expect(TokenType.CloseBrace, "Expected '}' after function body"); return { kind: "FunctionDefinition", @@ -227,10 +270,7 @@ export default class Parser { value = this.parse_expr(); } - this.expect( - TokenType.Semicolon, - "Expected semicolon after return statement", - ); + this.expect(TokenType.Semicolon, "Expected ';' after return statement"); return { kind: "ReturnStatement", @@ -238,42 +278,6 @@ export default class Parser { } as ReturnStatement; } - // /** - // * Parses a function call expression. - // * - // * @param callee - The expression that represents the function being called. - // * @returns A FunctionCallExpr object representing the parsed function call expression. - // * @throws {Error} If there are syntax errors or missing tokens in the function call expression. - // */ - // private parse_function_call_expr(callee: Expr): FunctionCallExpr { - // const args: Expr[] = []; - // this.expect(TokenType.Identifier, "Expected function name after 'call'"); - // this.expect( - // TokenType.OpenParen, - // "Expected opening parenthesis after function name", - // ); - - // if (this.at().type !== TokenType.CloseParen) { - // args.push(this.parse_assignment_expr()); - // while (this.at().type === TokenType.Comma && this.eat()) { - // args.push(this.parse_assignment_expr()); - // } - // } - - // this.expect( - // TokenType.CloseParen, - // "Expected closing parenthesis after function arguments", - // ); - - // this.expect(TokenType.Semicolon, "Expected semicolon after function call"); - - // return { - // kind: "FunctionCallExpr", - // args, - // callee, - // } as FunctionCallExpr; - // } - /** * Parses a 'for' loop statement. * @@ -284,20 +288,20 @@ export default class Parser { // Eat 'for' token this.eat(); - // Expect identifier as iterator in 'for' statement + // Expect identifier as iterator in 'wakandaFor' loop const iterator = this.expect( TokenType.Identifier, - "Expected identifier as iterator in 'for' statement", + "Expected identifier as an iterator in 'wakandaFor' loop", ).value; // Expect 'in' keyword after iterator - this.expect(TokenType.In, "Expected `in` keyword after iterator"); + this.expect(TokenType.In, "Expected 'in' keyword after iterator"); // Parse start expression const start = this.parse_expr(); // Expect 'to' keyword after start expression - this.expect(TokenType.To, "Expected `to` keyword after start expression"); + this.expect(TokenType.To, "Expected 'to' keyword after start expression"); // Parse end expression const end = this.parse_expr(); @@ -309,10 +313,10 @@ export default class Parser { step = this.parse_expr(); } - // Expect opening brace before 'for' statement body + // Expect '{' before 'for' statement body this.expect( TokenType.OpenBrace, - "Expected opening brace before 'for' statement body", + "Expected '{' before 'wakandaFor' statement body", ); // Parse statements within the 'for' loop body @@ -321,10 +325,10 @@ export default class Parser { body.push(this.parse_stmt()); } - // Expect closing brace after 'for' statement body + // Expect '}' after 'for' statement body this.expect( TokenType.CloseBrace, - "Expected closing brace after 'for' statement body", + "Expected '}' after 'wakandaFor' statement body", ); // Return the parsed 'for' loop statement object @@ -348,25 +352,22 @@ export default class Parser { // Eat 'switch' token this.eat(); - // Expect opening parenthesis after 'switch' - this.expect( - TokenType.OpenParen, - "Expected opening parenthesis after 'switch'", - ); + // Expect '(' after 'switch' + this.expect(TokenType.OpenParen, "Expected '(' after 'multiverse' "); // Parse the expression used as the discriminant const discriminant = this.parse_expr(); - // Expect closing parenthesis after switch expression + // Expect ')' after switch expression this.expect( TokenType.CloseParen, - "Expected closing parenthesis after switch expression", + "Expected ')' after 'multiverse' expression", ); - // Expect opening brace before 'switch' statement body + // Expect '{' before 'switch' statement body this.expect( TokenType.OpenBrace, - "Expected opening brace before 'switch' statement body", + "Expected '{' before 'multiverse' statement body", ); const cases: SwitchCase[] = []; @@ -382,7 +383,7 @@ export default class Parser { const test = this.parse_expr(); // Expect colon after 'case' expression - this.expect(TokenType.Colon, "Expected colon after 'case' expression"); + this.expect(TokenType.Colon, "Expected ':' after 'madness' expression"); // Parse statements within the 'case' block const consequent: Stmt[] = []; @@ -400,7 +401,7 @@ export default class Parser { this.eat(); // Eat 'default' token // Expect colon after 'default' - this.expect(TokenType.Colon, "Expected colon after 'default'"); + this.expect(TokenType.Colon, "Expected ':' after 'default'"); // Parse statements within the 'default' block defaultCase = []; @@ -413,17 +414,17 @@ export default class Parser { defaultCase.push(this.parse_stmt()); } } else { - throw "Expected 'case' or 'default' inside 'switch' statement"; + throw `SyntaxError:line:${this.at().curr_line}: Expected 'madness' or 'default' inside 'multiverse' statement`; } } - // Expect closing brace after 'switch' statement body + // Expect '}' after 'switch' statement body this.expect( TokenType.CloseBrace, - "Expected closing brace after 'switch' statement body", + "Expected '}' after 'multiverse' statement body", ); - // Return the parsed 'switch' statement object + // Return the parsed 'multiverse' statement object return { kind: "SwitchStatement", discriminant, @@ -442,52 +443,44 @@ export default class Parser { this.eat(); // Eat 'array' token // Parse identifier - const name = this.expect(TokenType.Identifier, "Expected array name").value; + const name = this.expect( + TokenType.Identifier, + "Expected 'team (array)' name", + ).value; // Check for optional size or values - let size = 0; + let size: Expr; let vals: Expr[]; // Check for optional size or values - if (this.at().type === TokenType.OpenParen) { - // Expect opening parenthesis ( after array identifier - this.expect( - TokenType.OpenParen, - "Expected opening parenthesis ( after array identifier", - ); - - // Parse the array size as a number - size = parseInt( - this.expect(TokenType.Number, "Expected array size").value, - ); - - if (size > 10000000) { - throw `Error occurred at array declaration ${name}: Cannot declare Array of size greater than 10000000`; - } + // Expect '[' after array identifier + this.expect( + TokenType.OpenBracket, + `Expected '[' after identifier '${name}'`, + ); - // Expect closing parenthesis ) after array size or values - this.expect( - TokenType.CloseParen, - "Expected closing parenthesis ) after array size or values", - ); - } + // Parse the array size as a number + size = this.parse_expr(); + + // Expect ')' ) after array size or values + this.expect(TokenType.CloseBracket, "Expected ']'"); // Expect = to add values - this.expect(TokenType.Equals, "Expected = to add values"); + this.expect(TokenType.Equals, "Expected '=' for team assignment"); // Expect { to add array values - this.expect(TokenType.OpenBrace, "Expected { to add array value"); + this.expect(TokenType.OpenBrace, "Expected '{' to add team value"); // Parse the array values vals = this.parse_array_values(size); // Expect } to add array values - this.expect(TokenType.CloseBrace, "Expected } to add array value"); + this.expect(TokenType.CloseBrace, "Expected '}' to add team value"); // Expect ; at the end of the declaration statement this.expect( TokenType.Semicolon, - "Expected ; at end of declaration statement", + "Expected ';' at end of declaration statement", ); // Return the parsed array declaration statement object @@ -506,29 +499,15 @@ export default class Parser { * @returns An array of parsed expressions representing the array values. * @throws {Error} If the array size is exceeded by the number of values provided. */ - private parse_array_values(size: number): Expr[] { + private parse_array_values(size: Expr): Expr[] { const values: Expr[] = []; - let cnt = 1; - // Continue parsing values until encountering the closing brace or reaching the end of input + // Continue parsing values until encountering the '}' or reaching the end of input while (this.at().type !== TokenType.CloseBrace && this.not_eof()) { - if (cnt <= size) { - // If there are still available slots in the array, parse the expression and add it to the values - values.push(this.parse_expr()); - } else { - // If the array size is exceeded, throw an error indicating the expected size - throw `Array size exceeded by the number of values provided. Expected ${size} values.`; - } + values.push(this.parse_expr()); if (this.at().type === TokenType.Comma) { this.eat(); // Eat the comma if present } - cnt++; - } - - // If there are remaining empty slots in the array, fill them with a default NumericLiteral of value 0 - while (cnt <= size) { - values.push({ kind: "NumericLiteral", value: 0 } as NumericLiteral); - cnt++; } return values; @@ -542,16 +521,16 @@ export default class Parser { */ private parse_if_statement(): IfStatement | ElseStatement { this.eat(); // Eat 'if' token - this.expect(TokenType.OpenParen, "Expected opening parenthesis after 'if'"); + this.expect(TokenType.OpenParen, "Expected '(' after 'ifWorthy'"); const condition = this.parse_expr(); this.expect( TokenType.CloseParen, - "Expected closing parenthesis after condition in 'if' statement", + "Expected ')' after condition in 'ifWorthy' statement", ); this.expect( TokenType.OpenBrace, - "Expected opening brace before 'if' statement body", + "Expected '{' before 'ifWorthy' statement body", ); const body: Stmt[] = []; while (this.at().type !== TokenType.CloseBrace && this.not_eof()) { @@ -559,7 +538,7 @@ export default class Parser { } this.expect( TokenType.CloseBrace, - "Expected closing brace after 'if' statement body", + "Expected '}' after 'ifWorthy' statement body", ); let elseBranch: IfStatement | ElseStatement | undefined = undefined; if (this.at().type === TokenType.Otherwise) { @@ -571,7 +550,7 @@ export default class Parser { // 'else' statement this.expect( TokenType.OpenBrace, - "Expected opening brace before 'else' statement body", + "Expected '{' before 'otherwise' statement body", ); const elseBody: Stmt[] = []; while (this.at().type !== TokenType.CloseBrace && this.not_eof()) { @@ -579,7 +558,7 @@ export default class Parser { } this.expect( TokenType.CloseBrace, - "Expected closing brace after 'else' statement body", + "Expected '}' after 'otherwise' statement body", ); elseBranch = { kind: "ElseStatement", body: elseBody }; @@ -597,21 +576,18 @@ export default class Parser { */ private parse_while_statement(): Stmt { this.eat(); // Eat 'while' token - this.expect( - TokenType.OpenParen, - "Expected opening parenthesis after 'while'", - ); + this.expect(TokenType.OpenParen, "Expected '(' after 'fightUntil'"); const condition = this.parse_expr(); this.expect( TokenType.CloseParen, - "Expected closing parenthesis after condition in 'while' statement", + "Expected ')' after condition in 'fightUntil' statement", ); this.expect( TokenType.OpenBrace, - "Expected opening brace before 'while' statement body", + "Expected '{' before 'fightUntil' statement body", ); const body: Stmt[] = []; @@ -622,7 +598,7 @@ export default class Parser { this.expect( TokenType.CloseBrace, - "Expected closing brace after 'while' statement body", + "Expected '}' after 'fightUntil' statement body", ); return { @@ -649,7 +625,7 @@ export default class Parser { // Consume semicolon this.eat(); if (isConstant) { - throw `Must assign value to constant expression.`; + throw `SyntaxError:line:${this.at().curr_line}: Must assign value to constant expression ${identifier}`; } return { kind: "VariableDeclaration", @@ -659,7 +635,10 @@ export default class Parser { } as VariableDeclaration; } - this.expect(TokenType.Equals, "Expected assignment to identifier"); + this.expect( + TokenType.Equals, + `Expected assignment to identifier ${identifier}`, + ); const declaration = { kind: "VariableDeclaration", @@ -669,7 +648,7 @@ export default class Parser { } as VariableDeclaration; // Check semicolon - this.expect(TokenType.Semicolon, "Expected Semicolon"); + this.expect(TokenType.Semicolon, "Expected ';'"); return declaration; } @@ -687,7 +666,7 @@ export default class Parser { */ private parse_break_statement(): Expr { this.eat(); // Eat 'break' token - this.expect(TokenType.Semicolon, "Expected Semicolon"); + this.expect(TokenType.Semicolon, "Expected ';'"); return { kind: "BreakStatement" } as BreakStatement; } @@ -883,12 +862,12 @@ export default class Parser { * @throws {Error} If there are syntax errors or missing tokens in the expression. */ private parse_args(): Expr[] { - this.expect(TokenType.OpenParen, `Expected open parenthesis`); + this.expect(TokenType.OpenParen, `Expected '('`); const args = this.at().type === TokenType.CloseParen ? [] : this.parse_arguments_list(); - this.expect(TokenType.CloseParen, `Expected close parenthesis`); + this.expect(TokenType.CloseParen, `Expected ')'`); return args; } @@ -920,7 +899,7 @@ export default class Parser { while (this.at().type === TokenType.OpenBracket) { this.eat(); // Consume the opening bracket const property = this.parse_expr(); - this.expect(TokenType.CloseBracket, "Missing closing bracket"); + this.expect(TokenType.CloseBracket, "Expected ']'"); object = { kind: "MemberExpr", @@ -997,21 +976,21 @@ export default class Parser { return { kind: "NullLiteral", value: "null" } as NullLiteral; case TokenType.OpenParen: { - this.eat(); //Eat opening parenthesis + this.eat(); //Eat '(' const value = this.parse_expr(); this.expect( TokenType.CloseParen, - "Unexpected token found inside parenthesized expression. Expected closing parenthesis", - ); //Eat closing parenthesis + "Unexpected token found inside parenthesized expression. Expected ')'", + ); //Eat ')' return value; } case TokenType.OpenBrace: { - this.eat(); //Eat opening parenthesis + this.eat(); //Eat '(' const value = this.parse_expr(); this.expect( TokenType.CloseBrace, - "Unexpected token found inside parenthesized expression. Expected closing parenthesis", - ); //Eat closing parenthesis + "Unexpected token found inside parenthesized expression. Expected ')'", + ); //Eat ')' return value; } @@ -1020,7 +999,7 @@ export default class Parser { return this.parse_not_expr(); default: - throw `Unexpected token found while parsing scanned ${this.at().value}`; + throw `SyntaxError:line:${this.at().curr_line}: Unexpected token found while parsing scanned ${this.at().value}`; } } } diff --git a/FrontEnd/lexer.ts b/FrontEnd/lexer.ts index edac5ae..706d232 100644 --- a/FrontEnd/lexer.ts +++ b/FrontEnd/lexer.ts @@ -1,3 +1,4 @@ +let line_cnt: number = 1; /** * Represents the type of a token. */ @@ -42,6 +43,20 @@ export enum TokenType { CloseBrace, // } OpenBracket, // [ CloseBracket, // ] + //Easter eggs + If, + Else, + While, + For, + Switch, + Case, + Let, + Const, + Return, + Break, + True, + False, + // End of the file token EOF, } @@ -69,6 +84,19 @@ const KEYWORDS: Record = { or: TokenType.LogicalOperator, assemble: TokenType.Assemble, snap: TokenType.Snap, + //? Easter Egg 🥚 + if: TokenType.If, + else: TokenType.Else, + while: TokenType.While, + for: TokenType.For, + switch: TokenType.Switch, + case: TokenType.Case, + let: TokenType.Let, + const: TokenType.Const, + return: TokenType.Return, + break: TokenType.Break, + true: TokenType.True, + false: TokenType.False, }; /** @@ -77,6 +105,7 @@ const KEYWORDS: Record = { export interface Token { value: string; type: TokenType; + curr_line: number; } /** @@ -85,8 +114,8 @@ export interface Token { * @param type - The type of the token. * @returns The token with the specified value and type. */ -function getToken(value = "", type: TokenType): Token { - return { value, type }; +function getToken(value = "", type: TokenType, curr_line: number): Token { + return { value, type, curr_line }; } /** @@ -138,14 +167,14 @@ function getMultiCharacterToken(src: string[]): Token | null { for (const operator in operators) { if (src.slice(0, operator.length).join("") === operator) { src.splice(0, operator.length); - return getToken(operator, operators[operator]); + return getToken(operator, operators[operator], line_cnt); } } if (src[0] === "=") { // Handle single "=" as a separate token src.shift(); - return getToken("=", TokenType.Equals); + return getToken("=", TokenType.Equals, line_cnt); } if (src[0] === '"') { @@ -158,7 +187,9 @@ function getMultiCharacterToken(src: string[]): Token | null { if (src[0] === '"') { src.shift(); // Consume the closing double quote - return getToken(string, TokenType.String); + return getToken(string, TokenType.String, line_cnt); + } else { + throw `SyntaxError:line:${line_cnt}: missing terminating '"' character.`; } } @@ -171,6 +202,9 @@ function getMultiCharacterToken(src: string[]): Token | null { * @returns True if the string represents a skippable character, false otherwise. */ function isSkippable(src: string): boolean { + if (src === "\n") { + line_cnt = line_cnt + 1; + } return ( src === " " || src === "\n" || src === "\t" || src === "\r" || src === '"' ); @@ -192,6 +226,9 @@ export function tokenize(sourceCode: string): Token[] { let comment = ""; comment += src.shift(); while (src.length > 0 && src[0] !== "$") { + if (src[0] === "\n") { + line_cnt = line_cnt + 1; + } comment += src.shift(); } comment += src.shift(); @@ -201,19 +238,20 @@ export function tokenize(sourceCode: string): Token[] { while (src.length > 0 && src[0] !== "\n") { comment += src.shift(); } + line_cnt = line_cnt + 1; comment += src.shift(); } else if (src[0] === "(") { - tokens.push(getToken(src.shift(), TokenType.OpenParen)); + tokens.push(getToken(src.shift(), TokenType.OpenParen, line_cnt)); } else if (src[0] === ")") { - tokens.push(getToken(src.shift(), TokenType.CloseParen)); + tokens.push(getToken(src.shift(), TokenType.CloseParen, line_cnt)); } else if (src[0] === "{") { - tokens.push(getToken(src.shift(), TokenType.OpenBrace)); + tokens.push(getToken(src.shift(), TokenType.OpenBrace, line_cnt)); } else if (src[0] === "}") { - tokens.push(getToken(src.shift(), TokenType.CloseBrace)); + tokens.push(getToken(src.shift(), TokenType.CloseBrace, line_cnt)); } else if (src[0] === "[") { - tokens.push(getToken(src.shift(), TokenType.OpenBracket)); + tokens.push(getToken(src.shift(), TokenType.OpenBracket, line_cnt)); } else if (src[0] === "]") { - tokens.push(getToken(src.shift(), TokenType.CloseBracket)); + tokens.push(getToken(src.shift(), TokenType.CloseBracket, line_cnt)); } else if ( src[0] === "+" || src[0] === "-" || @@ -222,17 +260,17 @@ export function tokenize(sourceCode: string): Token[] { src[0] === "%" || src[0] === "^" ) { - tokens.push(getToken(src.shift(), TokenType.BinaryOperator)); + tokens.push(getToken(src.shift(), TokenType.BinaryOperator, line_cnt)); } else if (src[0] === ";") { - tokens.push(getToken(src.shift(), TokenType.Semicolon)); + tokens.push(getToken(src.shift(), TokenType.Semicolon, line_cnt)); } else if (src[0] === "!") { - tokens.push(getToken(src.shift(), TokenType.NotOperator)); + tokens.push(getToken(src.shift(), TokenType.NotOperator, line_cnt)); } else if (src[0] === ":") { - tokens.push(getToken(src.shift(), TokenType.Colon)); + tokens.push(getToken(src.shift(), TokenType.Colon, line_cnt)); } else if (src[0] === ",") { - tokens.push(getToken(src.shift(), TokenType.Comma)); + tokens.push(getToken(src.shift(), TokenType.Comma, line_cnt)); } else if (src[0] === ".") { - tokens.push(getToken(src.shift(), TokenType.Dot)); + tokens.push(getToken(src.shift(), TokenType.Dot, line_cnt)); } else { const token = getMultiCharacterToken(src); if (token) { @@ -246,10 +284,10 @@ export function tokenize(sourceCode: string): Token[] { // Check for keywords const reserved: TokenType = KEYWORDS[id]; if (typeof reserved === "number") { - tokens.push(getToken(id, reserved)); + tokens.push(getToken(id, reserved, line_cnt)); } else { // unreserved means user defined identifier - tokens.push(getToken(id, TokenType.Identifier)); + tokens.push(getToken(id, TokenType.Identifier, line_cnt)); } } // Build number token else if (isNum(src[0])) { @@ -258,20 +296,22 @@ export function tokenize(sourceCode: string): Token[] { num += src.shift(); } - tokens.push(getToken(num, TokenType.Number)); + tokens.push(getToken(num, TokenType.Number, line_cnt)); } // Skip the skippable character else if (isSkippable(src[0])) { // Skip the current character src.shift(); } // Handle unrecognized characters else { - throw `Unrecognized character: , ${src[0]}`; + throw `SyntaxError:line:${line_cnt}: Unrecognised character ${ + src[0] + } found.`; } } } // Push EOF token - tokens.push({ type: TokenType.EOF, value: "EndOfFile" }); + tokens.push({ type: TokenType.EOF, value: "EndOfFile", curr_line: line_cnt }); return tokens; } diff --git a/main.ts b/main.ts index 7c3098c..24af285 100644 --- a/main.ts +++ b/main.ts @@ -13,7 +13,7 @@ async function __run(inputFile: string) { const program = parser.produceAST(input); evaluate(program, env); } -__run("./test.avenger"); +__run("./feat.avenger"); /** * Initializes the script execution. */ diff --git a/test.avenger b/test.avenger index 4806d20..20226e6 100644 --- a/test.avenger +++ b/test.avenger @@ -56,18 +56,18 @@ assemble is_prime_sieve(n) { ifWorthy(n <= 1) { snap HYDRA; } - team sieve(100) = {SHIELD}; + team sieve[n+1] = {SHIELD}; sieve[0] = HYDRA; sieve[1] = HYDRA; - wakandaFor idx in 2 to 99 step 1{ + wakandaFor idx in 2 to n step 1{ sieve[idx] = SHIELD; } newAvenger i = 2; - fightUntil(i * i <= 100){ + fightUntil(i * i <= n){ ifWorthy(sieve[i]){ newAvenger j = i * i; - fightUntil(j <= 100){ + fightUntil(j <= n){ sieve[j] = HYDRA; j = j + i; } @@ -156,31 +156,27 @@ assemble binary_search(arr, n, key){ } # Test case for binary_search assemble test_binary_search(){ - team arr_zero(1) = {1}; + team arr_zero[1] = {1}; newAvenger size_zero = 1; - team arr_one(7) = {1,2,3,4,5,6,7}; + team arr_one[7] = {1,2,3,4,5,6,7}; newAvenger size_one = 7; - team arr_two(6) = {1,2,3,4,5,6}; + team arr_two[6] = {1,2,3,4,5,6}; newAvenger size_two = 6; - team arr_three(0) = {}; - newAvenger size_three = 0; + team arr_three[3] = {1,2,3}; + newAvenger size_three = 3; - team arr_four(3) = {1,2,3}; - newAvenger size_four = 3; - - team arr_five(1) = {1}; - newAvenger size_five = 1; + team arr_four[1] = {1}; + newAvenger size_four = 1; vision("Testing reverse_int..."); assertEqual(binary_search(arr_zero, size_zero, 1), SHIELD); assertEqual(binary_search(arr_one, size_one, 4), SHIELD); assertEqual(binary_search(arr_two, size_two, 8), HYDRA); - assertEqual(binary_search(arr_three, size_three, 1), HYDRA); + assertEqual(binary_search(arr_three, size_three, 50), HYDRA); assertEqual(binary_search(arr_four, size_four, 1), SHIELD); - assertEqual(binary_search(arr_five, size_five, 0), HYDRA); vision(" "); } @@ -212,7 +208,7 @@ assemble recursiveBinarySearchFunction(arr, x,start, end){ assemble test_recursive_binary_code() { - team arr(6) = {1, 3, 5, 7, 8, 9}; + team arr[6] = {1, 3, 5, 7, 8, 9}; vision("Testing Binary search recurssive"); assertEqual(recursiveBinarySearchFunction(arr,5,0,5), SHIELD); assertEqual(recursiveBinarySearchFunction(arr,9,0,5), SHIELD); @@ -255,8 +251,8 @@ $ ========================================================================== assemble test_nested_teams(){ vision("Testing Nested Teams..."); newAvenger str = "Loki"; - team apk(1) = {str}; - team arr(1) = {apk}; + team apk[1] = {str}; + team arr[1] = {apk}; wakandaFor i in 0 to 0{ wakandaFor j in 0 to 0{ assertEqual(arr[i][j], "Loki"); @@ -335,7 +331,7 @@ $ ========================================================================== ========================================================================== $ assemble test_teams(){ vision("Testing teams..."); - team avengers(4) = {"Captain America", "Iron Man", "Thor", "Hulk"}; + team avengers[4]= {"Captain America", "Iron Man", "Thor", "Hulk"}; newAvenger firstAvenger = avengers[0]; newAvenger secondAvenger = avengers[1]; @@ -344,9 +340,9 @@ assemble test_teams(){ assertEqual(secondAvenger, "Iron Man"); - team male(10) = {"Captain America", "Iron Man" ,"Dr. Strange", "Hulk", "Hawkeye", "Spiderman", "Thor", "T`Challa", "Ant Man", "Moon Knight"}; - team female(7) = {"Black Widow","Captain Marvel","Wanda Maximoff","She Hulk","Ms Marvel","Gamora","Nebula"}; - team avenger(2) = {male, female}; + team male[10] = {"Captain America", "Iron Man" ,"Dr. Strange", "Hulk", "Hawkeye", "Spiderman", "Thor", "T`Challa", "Ant Man", "Moon Knight"}; + team female[7]= {"Black Widow","Captain Marvel","Wanda Maximoff","She Hulk","Ms Marvel","Gamora","Nebula"}; + team avenger[2]= {male, female}; assertEqual(avenger[0][4], "Hawkeye"); assertEqual(avenger[1][6], "Nebula"); @@ -364,7 +360,7 @@ assemble test_user_defined_twopointo(){ assemble f_(){ snap ("Called f_"); } - team functions(2) = {f(), f_}; + team functions[2] = {f(), f_}; assertEqual(functions[1](),"Called f_"); assemble testFunction(){ @@ -465,8 +461,8 @@ $ ========================================================================== ========================================================================== $ assemble test_snap_in_fightUntil_loop(){ vision("Testing snap in while loop..."); - team testArray(6) = {11,13,17,19,23,27}; - team testArray_(6) = {11,13,17,20,23,27}; + team testArray[6] = {11,13,17,19,23,27}; + team testArray_[6] = {11,13,17,20,23,27}; assemble isEvenPresent(arr){ newAvenger idx = 0; fightUntil(idx < 6){