diff --git a/Expr.hpp b/Expr.hpp index 6868df2..7c722b7 100644 --- a/Expr.hpp +++ b/Expr.hpp @@ -44,6 +44,9 @@ class Get; template class Set; +template +class This; + template class Visitor { public: @@ -58,8 +61,8 @@ class Visitor { virtual R visitCallExpr(shared_ptr> expr) = 0; virtual R visitGetExpr(shared_ptr> expr) = 0; virtual R visitSetExpr(shared_ptr> expr) = 0; + virtual R visitThisExpr(shared_ptr> expr) = 0; // virtual string visitSuperExpr(Super& expr) = 0; - // virtual string visitThisExpr(This& expr) = 0; }; template @@ -225,12 +228,25 @@ class Set: object(object_), name(name_), value(value_) { } R accept(shared_ptr> visitor) override { return visitor->visitSetExpr(this->shared_from_this()); - }; + } shared_ptr> object; Token name; shared_ptr> value; }; +template +class This : + public Expr, + public std::enable_shared_from_this> +{ + public: + explicit This(Token keyword_): keyword(keyword_) { } + R accept(shared_ptr> visitor) override { + return visitor->visitThisExpr(this->shared_from_this()); + } + Token keyword; +}; + // template // class Super: public Expr { // public: @@ -241,14 +257,4 @@ class Set: // Token keyword; // Token method; // }; - -// template -// class This : public Expr{ -// public: -// explicit This(Token keyword); -// R accept(shared_ptr> visitor) override { -// return visitor->visitThisExpr(*this); -// }; -// Token keyword; -// }; #endif // EXPR_HPP_ diff --git a/Interpreter.cpp b/Interpreter.cpp index 59bd116..6d25afa 100644 --- a/Interpreter.cpp +++ b/Interpreter.cpp @@ -170,7 +170,7 @@ Object Interpreter::visitVariableExpr(shared_ptr> expr) { } Object Interpreter::lookUpVariable( - Token name, shared_ptr> expr) { + Token name, shared_ptr> expr) { auto distance = locals.find(expr); if (distance != locals.end()) { return environment->getAt(distance->second, name.lexeme); @@ -226,10 +226,13 @@ Object Interpreter::visitSetExpr(shared_ptr> expr) { } Object value = evaluate(expr->value); - value.instance->set(expr->name, value); + object.instance->set(expr->name, value); return value; } +Object Interpreter::visitThisExpr(shared_ptr> expr) { + return lookUpVariable(expr->keyword, expr); +} void Interpreter::visitExpressionStmt(const Expression& stmt) { evaluate(stmt.expression); diff --git a/Interpreter.hpp b/Interpreter.hpp index 7085fae..59baf7a 100644 --- a/Interpreter.hpp +++ b/Interpreter.hpp @@ -36,6 +36,7 @@ class Interpreter: Object visitCallExpr(shared_ptr> expr); Object visitGetExpr(shared_ptr> expr); Object visitSetExpr(shared_ptr> expr); + Object visitThisExpr(shared_ptr> expr); void visitExpressionStmt(const Expression& stmt); void visitPrintStmt(const Print& stmt); void visitVarStmt(const Var& stmt); @@ -62,7 +63,7 @@ class Interpreter: void checkNumberOperand(Token operation, Object operand); void checkNumberOperands(Token operation, Object left, Object right); string stringify(Object object); - Object lookUpVariable(Token name, shared_ptr> expr); + Object lookUpVariable(Token name, shared_ptr> expr); }; #endif // INTERPRETER_HPP_ diff --git a/LoxFunction.cpp b/LoxFunction.cpp index 7f73d9c..880af82 100644 --- a/LoxFunction.cpp +++ b/LoxFunction.cpp @@ -10,6 +10,7 @@ #include "./Token.hpp" #include "./ReturnError.hpp" #include "./LoxFunction.hpp" +#include "./LoxInstance.hpp" using std::shared_ptr; using std::vector; @@ -47,3 +48,9 @@ Object LoxFunction::call( string LoxFunction::toString() { return "name.lexeme + ">"; } + +shared_ptr LoxFunction::bind(shared_ptr instance) { + shared_ptr environment(new Environment(closure)); + environment->define("this", Object::make_instance_obj(instance)); + return shared_ptr (new LoxFunction(declaration, environment)); +} \ No newline at end of file diff --git a/LoxFunction.hpp b/LoxFunction.hpp index d3551fc..f0298f6 100644 --- a/LoxFunction.hpp +++ b/LoxFunction.hpp @@ -32,6 +32,7 @@ class LoxFunction: public LoxCallable { vector arguments ); + shared_ptr bind(shared_ptr instance); string toString(); }; diff --git a/LoxInstance.cpp b/LoxInstance.cpp index 663a210..1bfdbf9 100644 --- a/LoxInstance.cpp +++ b/LoxInstance.cpp @@ -23,7 +23,7 @@ Object LoxInstance::get(Token name) { shared_ptr method = klass.findMethod(name.lexeme); if (method != nullptr) { - return Object::make_fun_obj(method); + return Object::make_fun_obj(method->bind(shared_from_this())); } throw RuntimeError(name, diff --git a/LoxInstance.hpp b/LoxInstance.hpp index 7f836de..af4df65 100644 --- a/LoxInstance.hpp +++ b/LoxInstance.hpp @@ -4,6 +4,7 @@ #define LOXINSTANCE_HPP_ #include +#include #include #include "./LoxClass.hpp" #include "./Token.hpp" @@ -11,7 +12,7 @@ using std::string; using std::map; -class LoxInstance { +class LoxInstance: public std::enable_shared_from_this { public: LoxClass klass; map fields; diff --git a/Parser.cpp b/Parser.cpp index 6ffa81b..77cd76e 100644 --- a/Parser.cpp +++ b/Parser.cpp @@ -336,6 +336,10 @@ shared_ptr> Parser::primary() { new Literal(Object::make_str_obj(previous().literal.str))); } + if (match({ THIS })) { + return shared_ptr>(new This(previous())); + } + if (match({ IDENTIFIER })) { return shared_ptr>( new Variable(previous())); diff --git a/Resolver.cpp b/Resolver.cpp index 9820eb5..ccc591c 100644 --- a/Resolver.cpp +++ b/Resolver.cpp @@ -83,6 +83,16 @@ Object Resolver::visitSetExpr(shared_ptr> expr) { return Object::make_nil_obj(); } +Object Resolver::visitThisExpr(shared_ptr> expr) { + if (currentClass == CLASS_NONE) { + lox::error(expr->keyword.line, + "Cannot use 'this' outside of a class."); + return Object::make_nil_obj(); + } + resolveLocal(expr, expr->keyword); + return Object::make_nil_obj(); +} + void Resolver::visitExpressionStmt(const Expression& stmt) { resolve(stmt.expression); } @@ -106,13 +116,26 @@ void Resolver::visitBlockStmt(const Block& stmt) { } void Resolver::visitClassStmt(const Class& stmt) { + ClassType enclosingClass = currentClass; + currentClass = CLASS; + declare(stmt.name); define(stmt.name); + beginScope(); + auto back = scopes.back(); + back["this"] = true; + scopes.pop_back(); + scopes.emplace_back(back); + for (auto method : stmt.methods) { FunctionType declaration = METHOD; resolveFunction(method, declaration); } + + endScope(); + + currentClass = enclosingClass; } void Resolver::visitIfStmt(const If& stmt) { @@ -135,7 +158,7 @@ void Resolver::visitFunctionStmt(shared_ptr stmt) { } void Resolver::visitReturnStmt(const Return& stmt) { - if (currentFunction == NONE) { + if (currentFunction == FUNCTION_NONE) { lox::error(stmt.name.line, "Cannot return from top-level code."); } diff --git a/Resolver.hpp b/Resolver.hpp index 550a7f0..ccd1626 100644 --- a/Resolver.hpp +++ b/Resolver.hpp @@ -35,6 +35,7 @@ class Resolver: Object visitCallExpr(shared_ptr> expr); Object visitGetExpr(shared_ptr> expr); Object visitSetExpr(shared_ptr> expr); + Object visitThisExpr(shared_ptr> expr); void visitExpressionStmt(const Expression& stmt); void visitPrintStmt(const Print& stmt); void visitVarStmt(const Var& stmt); @@ -49,11 +50,16 @@ class Resolver: void resolve(shared_ptr> expr); private: enum FunctionType { - NONE, + FUNCTION_NONE, FUNCTION, METHOD }; - FunctionType currentFunction = NONE; + enum ClassType { + CLASS_NONE, + CLASS + }; + ClassType currentClass = CLASS_NONE; + FunctionType currentFunction = FUNCTION_NONE; void beginScope(); void endScope(); void declare(Token name); diff --git a/test.lox b/test.lox index 2d7df01..48bf5a4 100644 --- a/test.lox +++ b/test.lox @@ -1,7 +1,10 @@ -class Bacon { - eat() { - print "Crunch crunch crunch!"; +class Cake { + taste() { + var adjective = "delicious"; + print "The " + this.flavor + " cake is " + adjective + "!"; } } -Bacon().eat(); \ No newline at end of file +var cake = Cake(); +cake.flavor = "German chocolate"; +cake.taste(); \ No newline at end of file