From 50276bf9a98a2dd0e1ca457c8eff2249db1e298d Mon Sep 17 00:00:00 2001 From: Rohith-Raju Date: Sun, 14 Apr 2024 12:14:57 +0530 Subject: [PATCH 1/5] update: added more tests to interpreter --- src/Interpreter.cpp | 1 - test/TestInterpreter.cpp | 79 +++++++++++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 22 deletions(-) diff --git a/src/Interpreter.cpp b/src/Interpreter.cpp index dc1bfea..6630405 100644 --- a/src/Interpreter.cpp +++ b/src/Interpreter.cpp @@ -114,7 +114,6 @@ void Interpreter::visitPrintStmnt(Print *stmnt) { Object Interpreter::visitExprStmnt(Expression *stmnt) { Object value = evaluate(stmnt->expression); - std::cout << value.str() << "\n"; return value; } diff --git a/test/TestInterpreter.cpp b/test/TestInterpreter.cpp index 49e84ed..9b6329e 100644 --- a/test/TestInterpreter.cpp +++ b/test/TestInterpreter.cpp @@ -7,14 +7,11 @@ #include "Scanner.h" #include "gtest/gtest.h" #include -#include -#include -#include #include #include -TEST(InterpreterTest, TestInterpreterExpression) { - std::string test = "10 + (40 + (20 - 30) - 10) + 50;"; +TEST(InterpreterTest, TestExpression) { + std::string test = "print(10 + (40 + (20 - 30) - 10) + 50);"; Scanner scan(test); std::vector tokens = scan.scanTokens(); Parser p(tokens); @@ -25,8 +22,8 @@ TEST(InterpreterTest, TestInterpreterExpression) { ASSERT_EQ(result, "80.000000\n"); } -TEST(InterpreterTest, TestInterpreterUnaryExpression) { - std::string test = "!true;"; +TEST(InterpreterTest, TestUnaryExpression) { + std::string test = "print(!true);"; Scanner scan(test); std::vector tokens = scan.scanTokens(); Parser p(tokens); @@ -37,8 +34,8 @@ TEST(InterpreterTest, TestInterpreterUnaryExpression) { ASSERT_EQ(result, "false\n"); } -TEST(InterpreterTest, TestParserTernaryExpression) { - std::string test = "3 > 1 ? true : false;"; +TEST(InterpreterTest, TestTernaryExpression) { + std::string test = "print(3 > 1 ? true : false);"; Scanner scan(test); std::vector tokens = scan.scanTokens(); Parser p(tokens); @@ -49,28 +46,68 @@ TEST(InterpreterTest, TestParserTernaryExpression) { ASSERT_EQ(result, "true\n"); } -TEST(InterpreterTest, TestInterpreterVarStatement) { +TEST(InterpreterTest, TestVarStatement) { std::string test = "var a = 10; print(a);"; - std::string test_f = "a = 10"; // fails Scanner scan(test); - Scanner scan_f(test_f); std::vector token = scan.scanTokens(); - std::vector token_f = scan_f.scanTokens(); Parser parser(token); - Parser parser_f(token_f); - std::vector statement = parser.parse(); - std::stringstream buffer; - std::streambuf *sbuf = std::cout.rdbuf(); - std::cout.rdbuf(buffer.rdbuf()); + testing::internal::CaptureStdout(); Interpreter{}.interpret(statement); - std::cout.rdbuf(sbuf); - ASSERT_EQ(buffer.str(), "10.000000\n"); + std::string result = testing::internal::GetCapturedStdout(); + ASSERT_EQ(result, "10.000000\n"); +} + +TEST(InterpreterTest, TestIfStatement) { + std::string test = "var a = 10; if(a>=10){print(\"equal\");}"; + Scanner scan(test); + std::vector tokens = scan.scanTokens(); + Parser p(tokens); + std::vector statements = p.parse(); + testing::internal::CaptureStdout(); + Interpreter{}.interpret(statements); + std::string result = testing::internal::GetCapturedStdout(); + ASSERT_EQ(result, "equal\n"); +} + +TEST(InterpreterTest, TestIfElseStatement) { + std::string test = + "var a = 15; if(a==10){print(\"equal\");} else{print(\"Not equal\");}"; + Scanner scan(test); + std::vector tokens = scan.scanTokens(); + Parser p(tokens); + std::vector statements = p.parse(); + testing::internal::CaptureStdout(); + Interpreter{}.interpret(statements); + std::string result = testing::internal::GetCapturedStdout(); + ASSERT_EQ(result, "Not equal\n"); +} - EXPECT_THROW(parser_f.parse(), std::runtime_error); +TEST(InterpreterTest, TestWhileLoop) { + std::string test = "var a = 5; while(a > 0){ print(a); a = a-1;}"; + Scanner scan(test); + std::vector tokens = scan.scanTokens(); + Parser p(tokens); + std::vector statements = p.parse(); + testing::internal::CaptureStdout(); + Interpreter{}.interpret(statements); + std::string result = testing::internal::GetCapturedStdout(); + ASSERT_EQ(result, "5.000000\n4.000000\n3.000000\n2.000000\n1.000000\n"); +} + +TEST(InterpreterTest, TestForLoop) { + std::string test = "for(var a = 0; a<5; a=a+1){print(a);}"; + Scanner scan(test); + std::vector tokens = scan.scanTokens(); + Parser p(tokens); + std::vector statements = p.parse(); + testing::internal::CaptureStdout(); + Interpreter{}.interpret(statements); + std::string result = testing::internal::GetCapturedStdout(); + ASSERT_EQ(result, "0.000000\n1.000000\n2.000000\n3.000000\n4.000000\n"); } From 484e49e959df90a335a52a10143b354af1ff66a9 Mon Sep 17 00:00:00 2001 From: Rohith-Raju Date: Thu, 18 Apr 2024 12:54:42 +0530 Subject: [PATCH 2/5] update(func): introduce functions - WIP --- include/CruxCallable.h | 19 ++++++++++++++++++ include/Expr.h | 17 +++++++++++++--- include/Interpreter.h | 8 +++++++- include/NativeFunction.h | 23 ++++++++++++++++++++++ include/Parser.h | 5 +++++ include/Statement.h | 15 +++++++++++++- include/utls/Object.h | 12 ++++++------ src/Expr.cpp | 9 +++++++++ src/Interpreter.cpp | 42 +++++++++++++++++++++++++++++++++++++++- src/Parser.cpp | 35 ++++++++++++++++++++++++++++++++- src/Statement.cpp | 6 ++++++ src/utls/Object.cpp | 10 ++++++++++ test/lib | 2 +- 13 files changed, 189 insertions(+), 14 deletions(-) create mode 100644 include/CruxCallable.h create mode 100644 include/NativeFunction.h diff --git a/include/CruxCallable.h b/include/CruxCallable.h new file mode 100644 index 0000000..5465ed7 --- /dev/null +++ b/include/CruxCallable.h @@ -0,0 +1,19 @@ +// +// Cruxcallable.h contains function definations that is overridden +// by CruxFunction and Crux Class +// +// +#pragma once + +#include "utls/Object.h" +#include +#include + +class Interpreter; + +class CruxCallable { +public: + virtual int arity() = 0; + virtual Object call(Interpreter *interpreter, std::vector) = 0; + virtual std::string str() = 0; +}; diff --git a/include/Expr.h b/include/Expr.h index 5b53b98..71611ce 100644 --- a/include/Expr.h +++ b/include/Expr.h @@ -1,11 +1,10 @@ // -// Created by Rohith on 1/25/24. -// #ifndef CRUX_EXPR_H #define CRUX_EXPR_H #include "Token.h" +#include enum ExprType { ExprType_Binary, @@ -15,7 +14,8 @@ enum ExprType { ExprType_Ternary, ExprType_Variable, ExprType_Assignment, - ExprType_Logical + ExprType_Logical, + ExprType_Call }; class Expr { @@ -38,6 +38,17 @@ class Binary : public Expr { ~Binary(); }; +class Call : public Expr { +public: + Expr *callee; + Token *paren; + std::vector arguments; + + Call(Expr *callee, Token *paren, std::vector arguments); + + ~Call(); +}; + class Grouping : public Expr { public: Expr *expression; diff --git a/include/Interpreter.h b/include/Interpreter.h index a44caff..5d70b0c 100644 --- a/include/Interpreter.h +++ b/include/Interpreter.h @@ -14,7 +14,7 @@ class Interpreter { private: - Environment *environment = new Environment(); + static Environment *environment; void excecute(Statement *stmnt); @@ -33,8 +33,12 @@ class Interpreter { bool checkCompatibility(Token *op, Object left, Object right); public: + static Environment *globals; + bool isBreakUsed = false; + Interpreter(); + void interpret(std::vector &statements); void visitPrintStmnt(Print *stmnt); @@ -57,6 +61,8 @@ class Interpreter { Object visitGroupExp(Grouping *expr); + Object visitCall(Call *stmnt); + Object visitUnaryExp(Unary *expr); Object visitBinaryExp(Binary *expr); diff --git a/include/NativeFunction.h b/include/NativeFunction.h new file mode 100644 index 0000000..943134d --- /dev/null +++ b/include/NativeFunction.h @@ -0,0 +1,23 @@ +#pragma once + +#include "CruxCallable.h" +#include +#include +#include +class Interpreter; + +class ClockFunction : public CruxCallable { + virtual int arity() override { return 0; } + + virtual Object call(Interpreter *interpreter, std::vector) override { + auto millisec_since_epoch = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + Object value((double)millisec_since_epoch / 1000.0); + return value; + } + + virtual std::string str() override { return ""; } +}; diff --git a/include/Parser.h b/include/Parser.h index c272958..e1f594a 100644 --- a/include/Parser.h +++ b/include/Parser.h @@ -30,8 +30,12 @@ class Parser { Expr *term(); Expr *factor(); Expr *unary(); + Expr *call(); Expr *primary(); + // Expr helper functions + Expr *finishCall(Expr *expr); + // Statement helper functions Statement *statement(); Statement *printStatement(); @@ -40,6 +44,7 @@ class Parser { Statement *forStatement(); Statement *breakStatement(); Statement *expressionStatement(); + Statement *function(std::string str); std::vector blockStatement(); // Variable stuff diff --git a/include/Statement.h b/include/Statement.h index 680f85f..90e731a 100644 --- a/include/Statement.h +++ b/include/Statement.h @@ -6,6 +6,7 @@ #define CRUX_STATEMENT_H #include "Expr.h" +#include "Token.h" #include enum Statement_type { @@ -15,7 +16,8 @@ enum Statement_type { StmntBlock_type, StmntIf_type, StmntWhile_type, - StmntBreak_type + StmntBreak_type, + StmntFunc_type }; class Statement { @@ -76,4 +78,15 @@ class Break : public Statement { Break(bool breakSet); }; +class Function : public Statement { +public: + Token *name; + std::vector params; + std::vector body; + + Function(Token *name, std::vector params, + std::vector body); + ~Function(); +}; + #endif diff --git a/include/utls/Object.h b/include/utls/Object.h index 8900e83..7a4f5f5 100644 --- a/include/utls/Object.h +++ b/include/utls/Object.h @@ -7,22 +7,22 @@ #include -enum ObjType { - nullptr_type, - num_type, - string_type, - bool_type, -}; +class CruxCallable; + +enum ObjType { nullptr_type, num_type, string_type, bool_type, function_type }; class Object { public: ObjType type; double num_literal; bool bool_literal; std::string string_literal; + CruxCallable *function; + Object(); Object(bool type); Object(double type); Object(std::string type); + Object(CruxCallable *func); std::string str(); }; #endif // CRUX_OBJECT_H diff --git a/src/Expr.cpp b/src/Expr.cpp index 7e451d4..076a451 100644 --- a/src/Expr.cpp +++ b/src/Expr.cpp @@ -3,6 +3,7 @@ // #include "Expr.h" +#include Expr::Expr(ExprType type) : type(type) {} Token *op = new Token(STAR, "*", Object(), 1); @@ -23,6 +24,14 @@ Grouping::Grouping(Expr *expression) Grouping::~Grouping() { delete expression; } +Call::Call(Expr *callee, Token *op, std::vector arguments) + : Expr(ExprType_Call), callee(callee), paren(op), arguments(arguments) {} + +Call::~Call() { + delete callee; + delete paren; +} + Unary::Unary(Token *op, Expr *right) : Expr(ExprType_Unary), op(op), right(right) {} diff --git a/src/Interpreter.cpp b/src/Interpreter.cpp index 6630405..c9de5ed 100644 --- a/src/Interpreter.cpp +++ b/src/Interpreter.cpp @@ -1,6 +1,7 @@ // #include "Interpreter.h" +#include "CruxCallable.h" #include "Error.h" #include "Expr.h" #include "Statement.h" @@ -8,9 +9,18 @@ #include "utls/Object.h" #include "utls/RuntimeError.h" #include +#include #include #include +Environment *Interpreter::environment; +Environment *Interpreter::globals; + +Interpreter::Interpreter() { + globals = new Environment(); + environment = globals; +} + void Interpreter::excecute(Statement *stmnt) { switch (stmnt->type) { case StmntPrint_type: @@ -52,6 +62,8 @@ Object Interpreter::evaluate(Expr *expr) { return visitVariableExp((Variable *)expr); case ExprType_Assignment: return visitAssignment((Assignment *)expr); + case ExprType_Call: + return visitCall((Call *)expr); } return Object(); } @@ -183,6 +195,31 @@ Object Interpreter::visitGroupExp(Grouping *expr) { return evaluate(expr->expression); } +Object Interpreter::visitCall(Call *stmnt) { + Object callee = evaluate(stmnt->callee); + + std::vector arguments; + + for (auto args : stmnt->arguments) { + arguments.push_back(args); + } + + if (callee.type != function_type) { + throw RuntimeError(*stmnt->paren, "call only allowed on functions"); + } + + CruxCallable *function = callee.function; + + if (arguments.size() != function->arity()) { + std::stringstream ss; + ss << "Expected " << function->arity() << " arguments but got " + << arguments.size(); + RuntimeError(*stmnt->paren, ss.str()); + } + + return function->call(this, arguments); +} + Object Interpreter::visitUnaryExp(Unary *expr) { Object right = evaluate(expr->right); switch (expr->op->type) { @@ -257,4 +294,7 @@ Object Interpreter::visitTernaryExp(Ternary *expr) { return Object(); } -Interpreter::~Interpreter() { delete environment; } +Interpreter::~Interpreter() { + delete environment; + delete globals; +} diff --git a/src/Parser.cpp b/src/Parser.cpp index a648133..0c4a7c6 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -270,9 +270,39 @@ Expr *Parser::unary() { Expr *right = primary(); return new Unary(op, right); } - return primary(); + return call(); } +Expr *Parser::call() { + Expr *expr = primary(); + + while (true) { + if (match(LEFT_PAREN)) { + expr = finishCall(expr); + } else { + break; + } + } + return expr; +} + +Expr *Parser::finishCall(Expr *expr) { + std::vector arguments; + if (!check(RIGHT_PAREN)) { + do { + if (arguments.size() >= 255) { + error(peek(), "Can't have more than 255 arguments."); + } + arguments.push_back(expression()); + } while (COMMA); + } + consume(RIGHT_PAREN, "Expected ')' after arguments"); + + return new Call(expr, new Token(previous()), arguments); +} + +Statement *Parser::function(std::string str) {} + Expr *Parser::primary() { if (match(FALSE)) return new Literal(new Object(false)); @@ -283,6 +313,9 @@ Expr *Parser::primary() { if (match(NIL)) return new Literal(new Object()); + if (match(FUN)) + return (Expr *)function("function"); + if (match(NUMBER)) { return new Literal(new Object(previous().literal)); } diff --git a/src/Statement.cpp b/src/Statement.cpp index 0fb61b5..67e5822 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -46,3 +46,9 @@ While::~While() { Break::Break(bool isBrkPre) : Statement(StmntBreak_type), isBreakPresent(isBrkPre) {} + +Function::Function(Token *name, std::vector params, + std::vector body) + : Statement(StmntFunc_type), name(name), params(params), body(body) {} + +Function::~Function() { delete name; } diff --git a/src/utls/Object.cpp b/src/utls/Object.cpp index cd5306b..fb0c4e2 100644 --- a/src/utls/Object.cpp +++ b/src/utls/Object.cpp @@ -2,6 +2,7 @@ // Created by Rohith on 2/20/24. // #include "utls/Object.h" +#include "CruxCallable.h" Object::Object() { type = nullptr_type; } @@ -20,6 +21,11 @@ Object::Object(std::string type) { string_literal = type; } +Object::Object(CruxCallable *type) { + this->type = function_type; + function = type; +} + std::string Object::str() { if (type == string_type) { return string_literal; @@ -33,5 +39,9 @@ std::string Object::str() { if (type == bool_type) { return bool_literal ? "true" : "false"; } + if (type == function_type) { + return function->str(); + } + return ""; } diff --git a/test/lib b/test/lib index 5197b1a..5a37b51 160000 --- a/test/lib +++ b/test/lib @@ -1 +1 @@ -Subproject commit 5197b1a8e6a1ef9f214f4aa537b0be17cbf91946 +Subproject commit 5a37b517ad4ab6738556f0284c256cae1466c5b4 From e3965db49ef6a3f7b7672511a6bc440d5ca3291e Mon Sep 17 00:00:00 2001 From: Rohith-Raju Date: Thu, 2 May 2024 18:48:28 +0530 Subject: [PATCH 3/5] update(function): add support for functions --- include/Function.h | 16 ++++++++++++++++ include/Interpreter.h | 10 +++++----- include/Statement.h | 1 - include/env/Env.h | 8 +++++++- src/CMakeLists.txt | 1 + src/Function.cpp | 26 ++++++++++++++++++++++++++ src/Interpreter.cpp | 31 ++++++++++++++++++++++--------- src/Parser.cpp | 26 +++++++++++++++++++++----- src/Statement.cpp | 2 -- src/env/Env.cpp | 29 +++++++++++++++++++++++++---- test/TestInterpreter.cpp | 8 +++----- test/lib | 2 +- 12 files changed, 127 insertions(+), 33 deletions(-) create mode 100644 include/Function.h create mode 100644 src/Function.cpp diff --git a/include/Function.h b/include/Function.h new file mode 100644 index 0000000..bae1fd8 --- /dev/null +++ b/include/Function.h @@ -0,0 +1,16 @@ +#ifndef FUNCTION_H +#define FUNCTION_H + +#include "CruxCallable.h" +#include "Statement.h" + +class CruxFunction : public CruxCallable { +public: + Function *declaration; + CruxFunction(Function *declaration); + virtual int arity() override; + virtual Object call(Interpreter *interpreter, std::vector) override; + virtual std::string str() override; +}; + +#endif // FUNCTION_H diff --git a/include/Interpreter.h b/include/Interpreter.h index 5d70b0c..ffc7817 100644 --- a/include/Interpreter.h +++ b/include/Interpreter.h @@ -18,8 +18,6 @@ class Interpreter { void excecute(Statement *stmnt); - void excecuteBlock(std::vector stmnts, Environment *env); - Object evaluate(Expr *expr); bool isTruthy(Object right); @@ -35,9 +33,11 @@ class Interpreter { public: static Environment *globals; + Interpreter(); + bool isBreakUsed = false; - Interpreter(); + void excecuteBlock(std::vector stmnts, Environment *env); void interpret(std::vector &statements); @@ -53,6 +53,8 @@ class Interpreter { void visitWhileStmnt(While *stmnt); + void visitFuncStmnt(Function *stmnt); + Object visitAssignment(Assignment *expr); Object visitLogicalExp(Logical *expr); @@ -70,8 +72,6 @@ class Interpreter { Object visitTernaryExp(Ternary *expr); Object visitVariableExp(Variable *expr); - - ~Interpreter(); }; #endif // CRUX_INTERPRETER_H diff --git a/include/Statement.h b/include/Statement.h index 90e731a..3ec9332 100644 --- a/include/Statement.h +++ b/include/Statement.h @@ -86,7 +86,6 @@ class Function : public Statement { Function(Token *name, std::vector params, std::vector body); - ~Function(); }; #endif diff --git a/include/env/Env.h b/include/env/Env.h index e93ce78..d8a6b83 100644 --- a/include/env/Env.h +++ b/include/env/Env.h @@ -2,9 +2,11 @@ // Created by rohith on 26/03/2024 // +#ifndef ENV_H +#define ENV_H + #include "Token.h" #include "utls/Object.h" -#include #include class Environment { @@ -16,7 +18,11 @@ class Environment { Environment(); ~Environment(); Environment(Environment *enclosing); + void define(Token *tkn, Object value); void define(std::string name, Object value); void assign(Token *name, Object value); Object get(Token *name); + void deepClean(Environment *enclosing); }; + +#endif diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 1dbf997..f91bca0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -6,6 +6,7 @@ set(Sources Error.cpp AstPrinter.cpp Interpreter.cpp Statement.cpp + Function.cpp env/Env.cpp ) diff --git a/src/Function.cpp b/src/Function.cpp new file mode 100644 index 0000000..8de56eb --- /dev/null +++ b/src/Function.cpp @@ -0,0 +1,26 @@ +// +// created by rohith on 20-04-2024 +// + +#include "Function.h" +#include "Interpreter.h" +#include "env/Env.h" +#include "utls/Object.h" +#include + +CruxFunction::CruxFunction(Function *declaration) : declaration(declaration) {} + +int CruxFunction::arity() { return declaration->params.size(); } + +Object CruxFunction::call(Interpreter *interpreter, std::vector args) { + Environment *env = new Environment(interpreter->globals); + for (int i = 0; i < declaration->params.size(); i++) { + env->define(declaration->params[i], args[i]); + } + interpreter->excecuteBlock(declaration->body, env); + return Object(); +} + +std::string CruxFunction::str() { + return "<\"fn" + declaration->name->lexeme + ">\""; +} diff --git a/src/Interpreter.cpp b/src/Interpreter.cpp index c9de5ed..280fa07 100644 --- a/src/Interpreter.cpp +++ b/src/Interpreter.cpp @@ -4,8 +4,11 @@ #include "CruxCallable.h" #include "Error.h" #include "Expr.h" +#include "Function.h" +#include "NativeFunction.h" #include "Statement.h" #include "Token.h" +#include "env/Env.h" #include "utls/Object.h" #include "utls/RuntimeError.h" #include @@ -17,8 +20,12 @@ Environment *Interpreter::environment; Environment *Interpreter::globals; Interpreter::Interpreter() { - globals = new Environment(); - environment = globals; + if (!environment) { + globals = new Environment(); + environment = globals; + Object clock(new ClockFunction()); + environment->define("clock", clock); + } } void Interpreter::excecute(Statement *stmnt) { @@ -41,6 +48,9 @@ void Interpreter::excecute(Statement *stmnt) { case StmntWhile_type: visitWhileStmnt((While *)stmnt); break; + case StmntFunc_type: + visitFuncStmnt((Function *)stmnt); + break; case StmntBreak_type: isBreakUsed = true; } @@ -152,8 +162,16 @@ void Interpreter::visitWhileStmnt(While *stmnt) { break; } } + +void Interpreter::visitFuncStmnt(Function *stmnt) { + Object declaration(new CruxFunction(stmnt)); + environment->define(stmnt->name, declaration); +} + void Interpreter::visitBlockStmnt(Block *stmnt) { - excecuteBlock(stmnt->stmnt, new Environment(environment)); + Environment *locals = new Environment(environment); + excecuteBlock(stmnt->stmnt, locals); + delete locals; } void Interpreter::excecuteBlock(std::vector stmnts, @@ -201,7 +219,7 @@ Object Interpreter::visitCall(Call *stmnt) { std::vector arguments; for (auto args : stmnt->arguments) { - arguments.push_back(args); + arguments.push_back(evaluate(args)); } if (callee.type != function_type) { @@ -293,8 +311,3 @@ Object Interpreter::visitTernaryExp(Ternary *expr) { } return Object(); } - -Interpreter::~Interpreter() { - delete environment; - delete globals; -} diff --git a/src/Parser.cpp b/src/Parser.cpp index 0c4a7c6..d1bf464 100644 --- a/src/Parser.cpp +++ b/src/Parser.cpp @@ -26,6 +26,8 @@ std::vector Parser::parse() { } Statement *Parser::declaration() { + if (match(FUN)) + return function("function"); if (match(VAR)) return varDeclaration(); return statement(); @@ -294,14 +296,31 @@ Expr *Parser::finishCall(Expr *expr) { error(peek(), "Can't have more than 255 arguments."); } arguments.push_back(expression()); - } while (COMMA); + } while (match(COMMA)); } consume(RIGHT_PAREN, "Expected ')' after arguments"); return new Call(expr, new Token(previous()), arguments); } -Statement *Parser::function(std::string str) {} +Statement *Parser::function(std::string kind) { + Token *name = new Token(consume(IDENTIFIER, "Expect" + kind + "name")); + std::vector params; + consume(LEFT_PAREN, "Expect ( after function name"); + if (!check(RIGHT_PAREN)) { + do { + if (params.size() >= 225) { + Token tkn = peek(); + crux::error(tkn, "Can't have more than 255 parameters"); + } + params.push_back(new Token(consume(IDENTIFIER, "Expect parameter name"))); + } while (match(COMMA)); + } + consume(RIGHT_PAREN, "Expect ')' after parameters"); + consume(LEFT_BRACE, "Expect '{' before body"); + std::vector body = blockStatement(); + return new Function(name, params, body); +} Expr *Parser::primary() { if (match(FALSE)) @@ -313,9 +332,6 @@ Expr *Parser::primary() { if (match(NIL)) return new Literal(new Object()); - if (match(FUN)) - return (Expr *)function("function"); - if (match(NUMBER)) { return new Literal(new Object(previous().literal)); } diff --git a/src/Statement.cpp b/src/Statement.cpp index 67e5822..4f51e00 100644 --- a/src/Statement.cpp +++ b/src/Statement.cpp @@ -50,5 +50,3 @@ Break::Break(bool isBrkPre) Function::Function(Token *name, std::vector params, std::vector body) : Statement(StmntFunc_type), name(name), params(params), body(body) {} - -Function::~Function() { delete name; } diff --git a/src/env/Env.cpp b/src/env/Env.cpp index 505a1d4..67dc977 100644 --- a/src/env/Env.cpp +++ b/src/env/Env.cpp @@ -3,15 +3,38 @@ // #include "env/Env.h" +#include "Token.h" #include "utls/Object.h" #include "utls/RuntimeError.h" Environment::Environment() : enclosing(nullptr) {} +Environment::~Environment() { + if (enclosing != nullptr) { + deepClean(enclosing); + } + values.clear(); + enclosing = nullptr; +} + +void Environment::deepClean(Environment *enclosing) { + if (enclosing != nullptr) + deepClean(enclosing); + + enclosing->values.clear(); + delete enclosing; + enclosing = nullptr; + return; +} + Environment::Environment(Environment *enclosing) : enclosing(enclosing) {} +void Environment::define(Token *tkn, Object value) { + define(tkn->lexeme, value); +} + void Environment::define(std::string name, Object value) { - values.insert({name, value}); + values.emplace(name, value); } void Environment::assign(Token *name, Object value) { @@ -34,7 +57,7 @@ Object Environment::get(Token *name) { if (obj.type == nullptr_type) throw RuntimeError(*name, "Uninitalized variable " + name->lexeme + " can't be computed"); - return values[name->lexeme]; + return obj; } if (enclosing != nullptr) { @@ -42,5 +65,3 @@ Object Environment::get(Token *name) { } throw RuntimeError(*name, "Unexpected variable " + name->lexeme); } - -Environment::~Environment() { delete enclosing; } diff --git a/test/TestInterpreter.cpp b/test/TestInterpreter.cpp index 9b6329e..b92c6f7 100644 --- a/test/TestInterpreter.cpp +++ b/test/TestInterpreter.cpp @@ -1,6 +1,4 @@ // -// Created by Rohith on 3/8/24 -// #include "Expr.h" #include "Interpreter.h" #include "Parser.h" @@ -35,6 +33,7 @@ TEST(InterpreterTest, TestUnaryExpression) { } TEST(InterpreterTest, TestTernaryExpression) { + std::string test = "print(3 > 1 ? true : false);"; Scanner scan(test); std::vector tokens = scan.scanTokens(); @@ -47,7 +46,6 @@ TEST(InterpreterTest, TestTernaryExpression) { } TEST(InterpreterTest, TestVarStatement) { - std::string test = "var a = 10; print(a);"; Scanner scan(test); @@ -60,7 +58,6 @@ TEST(InterpreterTest, TestVarStatement) { testing::internal::CaptureStdout(); Interpreter{}.interpret(statement); std::string result = testing::internal::GetCapturedStdout(); - ASSERT_EQ(result, "10.000000\n"); } TEST(InterpreterTest, TestIfStatement) { @@ -83,7 +80,8 @@ TEST(InterpreterTest, TestIfElseStatement) { Parser p(tokens); std::vector statements = p.parse(); testing::internal::CaptureStdout(); - Interpreter{}.interpret(statements); + Interpreter interpreter = Interpreter(); + interpreter.interpret(statements); std::string result = testing::internal::GetCapturedStdout(); ASSERT_EQ(result, "Not equal\n"); } diff --git a/test/lib b/test/lib index 5a37b51..d83fee1 160000 --- a/test/lib +++ b/test/lib @@ -1 +1 @@ -Subproject commit 5a37b517ad4ab6738556f0284c256cae1466c5b4 +Subproject commit d83fee138a9ae6cb7c03688a2d08d4043a39815d From 353300db3c2904b0230349cae07693f4b7917766 Mon Sep 17 00:00:00 2001 From: Rohith-Raju Date: Wed, 15 May 2024 14:56:27 +0530 Subject: [PATCH 4/5] update(function): fix recursion and repl state --- include/Interpreter.h | 12 +++++++--- include/Parser.h | 3 ++- include/Return.h | 6 +++++ include/Statement.h | 10 ++++++++- include/env/Env.h | 2 +- main.cpp | 47 ++++++++++++++++++++++++++++++++++++---- src/Function.cpp | 4 ++-- src/Interpreter.cpp | 29 ++++++++++++++++++------- src/Parser.cpp | 12 ++++++++++ src/Statement.cpp | 3 +++ src/env/Env.cpp | 20 ++++++++--------- test/TestInterpreter.cpp | 36 +++++++++++++++++++++++++----- test/lib | 2 +- 13 files changed, 148 insertions(+), 38 deletions(-) create mode 100644 include/Return.h diff --git a/include/Interpreter.h b/include/Interpreter.h index ffc7817..346468f 100644 --- a/include/Interpreter.h +++ b/include/Interpreter.h @@ -14,7 +14,7 @@ class Interpreter { private: - static Environment *environment; + Environment *environment; void excecute(Statement *stmnt); @@ -31,13 +31,17 @@ class Interpreter { bool checkCompatibility(Token *op, Object left, Object right); public: - static Environment *globals; + Environment *globals; Interpreter(); bool isBreakUsed = false; - void excecuteBlock(std::vector stmnts, Environment *env); + bool isReturnUsed = false; + + Object returnObj; + + Object excecuteBlock(std::vector stmnts, Environment *env); void interpret(std::vector &statements); @@ -55,6 +59,8 @@ class Interpreter { void visitFuncStmnt(Function *stmnt); + void visitReturnStmnt(Return *stmnt); + Object visitAssignment(Assignment *expr); Object visitLogicalExp(Logical *expr); diff --git a/include/Parser.h b/include/Parser.h index e1f594a..11e7b4c 100644 --- a/include/Parser.h +++ b/include/Parser.h @@ -45,13 +45,14 @@ class Parser { Statement *breakStatement(); Statement *expressionStatement(); Statement *function(std::string str); + Statement *returnStatement(); std::vector blockStatement(); // Variable stuff Statement *declaration(); Statement *varDeclaration(); - // helper functions + // Helper functions Expr *equality(); bool check(TokenType type); Token advance(); diff --git a/include/Return.h b/include/Return.h new file mode 100644 index 0000000..c027a45 --- /dev/null +++ b/include/Return.h @@ -0,0 +1,6 @@ +#include "utls/Object.h" + +struct ReturnValue { + Object value; + ReturnValue(Object value) : value(value){}; +}; diff --git a/include/Statement.h b/include/Statement.h index 3ec9332..aae81ef 100644 --- a/include/Statement.h +++ b/include/Statement.h @@ -17,7 +17,8 @@ enum Statement_type { StmntIf_type, StmntWhile_type, StmntBreak_type, - StmntFunc_type + StmntFunc_type, + StmntReturn_type }; class Statement { @@ -88,4 +89,11 @@ class Function : public Statement { std::vector body); }; +class Return : public Statement { +public: + Token *keyword; + Expr *value; + Return(Token *keyword, Expr *value); +}; + #endif diff --git a/include/env/Env.h b/include/env/Env.h index d8a6b83..94bb0b0 100644 --- a/include/env/Env.h +++ b/include/env/Env.h @@ -16,7 +16,7 @@ class Environment { public: Environment(); - ~Environment(); + //~Environment(); Environment(Environment *enclosing); void define(Token *tkn, Object value); void define(std::string name, Object value); diff --git a/main.cpp b/main.cpp index affeef8..7cbb047 100644 --- a/main.cpp +++ b/main.cpp @@ -2,12 +2,42 @@ #include "Interpreter.h" #include "Scanner.h" #include +#include +#include +#include #include #include +#include +#include +#include #include +namespace fs = std::filesystem; + void runCode(std::string source); +std::string readFile(fs::path path) { + if (path.extension() != ".crux") { + std::cerr << "Filename " << path.filename() + << " should end with the extension \".crux\" \n"; + exit(64); + } + + std::ostringstream data; + std::fstream file(path, std::ios_base::in); + if (!file.is_open()) { + std::cerr << "File could't be found or opened \n"; + exit(64); + } + data << file.rdbuf(); + return data.str(); +} + +void runFile(std::string path) { + fs::path fPath(path); + runCode(readFile(fPath)); +} + void runPromt() { for (;;) { std::cout << "> "; @@ -25,16 +55,25 @@ void runPromt() { } } +Interpreter interpreter{}; + void runCode(std::string source) { Scanner scanner(source); std::vector tokens = scanner.scanTokens(); Parser parser(tokens); std::vector expression = parser.parse(); - std::unique_ptr interpreter = std::make_unique(); - interpreter->interpret(expression); + interpreter.interpret(expression); } -int main() { - runPromt(); +int main(int argc, char *argv[]) { + if (argc > 2) { + std::cout << "usage: crux