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