diff --git a/cclox_src/error_handler.cc b/cclox_src/error_handler.cc index a8ddc73..3e55057 100644 --- a/cclox_src/error_handler.cc +++ b/cclox_src/error_handler.cc @@ -14,6 +14,11 @@ void ErrorHandler::error(Token token, const std::string &message) { } } +void ErrorHandler::runtime_error(const RuntimeException &exception) { + std::cout << "RuntimeException [" << exception.token.line << "]: " << exception.what() << '\n'; + had_runtime_error = true; +} + void ErrorHandler::report(int line, const std::string &where, const std::string &message) { std::cout << "[line " << line << "] Error" << where << ": " << message << '\n'; had_error = true; diff --git a/cclox_src/error_handler.hh b/cclox_src/error_handler.hh index 971afe3..1fe9246 100644 --- a/cclox_src/error_handler.hh +++ b/cclox_src/error_handler.hh @@ -2,15 +2,29 @@ #define ERROR_HANDLER_HH #include "tokenizer.hh" +#include #include +struct RuntimeException : public std::exception { + RuntimeException(Token token, const std::string& message) : token{token}, message{message} {} + const char* what() const noexcept override { + return message.c_str(); + } + + Token token; +private: + std::string message; +}; + struct ErrorHandler { ErrorHandler() : had_error{false} {}; void error(int line, const std::string &message); void error(Token token, const std::string &message); + void runtime_error(const RuntimeException &exception); void report(int line, const std::string &where, const std::string &message); bool had_error; + bool had_runtime_error; }; #endif diff --git a/cclox_src/interpreter.cc b/cclox_src/interpreter.cc index a9b60dc..69aaa2b 100644 --- a/cclox_src/interpreter.cc +++ b/cclox_src/interpreter.cc @@ -35,6 +35,9 @@ void run_file(const char *path) { if (error_handler.had_error) { exit(EX_DATAERR); } + if (error_handler.had_runtime_error) { + exit(EX_SOFTWARE); + } } } @@ -45,6 +48,7 @@ void run_prompt() { run(line); std::cout << PROMPT; error_handler.had_error = false; + error_handler.had_runtime_error = false; } } @@ -55,8 +59,13 @@ void run(const std::string &code) { Parser parser{tokens}; std::optional expression = parser.parse(); + static AstInterpreter interpreter{}; + + if (error_handler.had_error) { + return; + } + if (expression) { - AstPrinter printer{}; - std::cout << printer.print(*expression) << '\n'; + interpreter.interpret(*expression); } } diff --git a/cclox_src/parser.cc b/cclox_src/parser.cc index 22ea4db..eaf1c70 100644 --- a/cclox_src/parser.cc +++ b/cclox_src/parser.cc @@ -4,5 +4,6 @@ #include "parser/expr.cc" #include "parser/printer.cc" #include "parser/parser.cc" +#include "parser/interpreter.cc" #endif diff --git a/cclox_src/parser.hh b/cclox_src/parser.hh index ca0fc5d..1ef374d 100644 --- a/cclox_src/parser.hh +++ b/cclox_src/parser.hh @@ -4,5 +4,6 @@ #include "parser/expr.hh" #include "parser/printer.hh" #include "parser/parser.hh" +#include "parser/interpreter.hh" #endif diff --git a/cclox_src/parser/interpreter.cc b/cclox_src/parser/interpreter.cc new file mode 100644 index 0000000..696e601 --- /dev/null +++ b/cclox_src/parser/interpreter.cc @@ -0,0 +1,168 @@ +#include "interpreter.hh" +#include "expr.hh" +#include "../error_handler.hh" +#include "../tokenizer.hh" +#include +#include +#include +#include + +extern ErrorHandler error_handler; + +void AstInterpreter::interpret(const Expr &expression) { + try { + Object value = evaluate(expression); + std::cout << value.to_string() << '\n'; + } catch (RuntimeException e) { + error_handler.runtime_error(e); + } +} + +Object AstInterpreter::evaluate(const Expr &expr) { + switch (expr.type) { + case ExprType::BINARY: + return evaluate_binary_expression(expr); + case ExprType::GROUPING: + return evaluate_grouping_expression(expr); + case ExprType::LITERAL: + return evaluate_literal_expression(expr); + case ExprType::UNARY: + return evaluate_unary_expression(expr); + } +} + +Object AstInterpreter::evaluate_binary_expression(const Expr &expr) { + std::shared_ptr<_Binary> ptr = std::get>(expr.value); + assert(ptr != nullptr); + + Object left = evaluate(ptr->left); + Object right = evaluate(ptr->right); + + if (left.type == ObjectType::NUMBER && right.type == ObjectType::NUMBER) { + double left_val = std::get(left.value); + double right_val = std::get(right.value); + + switch (ptr->op.type) { + case TokenType::PLUS: return left_val + right_val; + case TokenType::MINUS: return left_val - right_val; + case TokenType::STAR: return left_val * right_val; + case TokenType::SLASH: { + if (right_val == 0) { + throw RuntimeException{ptr->op, "Division by zero."}; + } + return left_val / right_val; + } + + case TokenType::GREATER: return left_val > right_val; + case TokenType::GREATER_EQUAL: return left_val >= right_val; + case TokenType::LESS: return left_val < right_val; + case TokenType::LESS_EQUAL: return left_val <= right_val; + + case TokenType::BANG_EQUAL: return left_val != right_val; + case TokenType::EQUAL_EQUAL: return left_val == right_val; + + default: throw RuntimeException{ptr->op, "Operator " + ptr->op.lexeme + " is not supported for numbers."}; + } + } + + if (left.type == ObjectType::STRING_LIT) { + right = Object{StringObjectType::LITERAL, right.to_string()}; + } + + if (right.type == ObjectType::STRING_LIT) { + left = Object{StringObjectType::LITERAL, left.to_string()}; + } + + if (left.type == ObjectType::STRING_LIT && right.type == ObjectType::STRING_LIT) { + std::string left_val = std::get(left.value); + std::string right_val = std::get(right.value); + + switch (ptr->op.type) { + case TokenType::PLUS: return Object{StringObjectType::LITERAL, left_val + right_val}; + + case TokenType::BANG_EQUAL: return left_val != right_val; + case TokenType::EQUAL_EQUAL: return left_val == right_val; + + default: throw RuntimeException{ptr->op, "Operator " + ptr->op.lexeme + " is not supported for strings."}; + } + } + + if (left.type == ObjectType::BOOL && right.type == ObjectType::BOOL) { + bool left_val = std::get(left.value); + bool right_val = std::get(right.value); + + switch (ptr->op.type) { + case TokenType::BANG_EQUAL: return left_val != right_val; + case TokenType::EQUAL_EQUAL: return left_val == right_val; + + default: throw RuntimeException{ptr->op, "Operator " + ptr->op.lexeme + " is not supported for booleans."}; + } + } + + if (left.type == ObjectType::NIL && right.type == ObjectType::NIL) { + switch (ptr->op.type) { + case TokenType::BANG_EQUAL: return false; + case TokenType::EQUAL_EQUAL: return true; + + default: throw RuntimeException{ptr->op, "Operator " + ptr->op.lexeme + " is not supported for null."}; + } + } + + if (left.type == ObjectType::NIL) { + switch (ptr->op.type) { + case TokenType::BANG_EQUAL: return true; + case TokenType::EQUAL_EQUAL: return false; + + default: break; + } + } + + throw RuntimeException{ptr->op, "Operator " + ptr->op.lexeme + " is not supported for operands " + left.to_string() + " and " + right.to_string() + "."}; +} + +Object AstInterpreter::evaluate_grouping_expression(const Expr &expr) { + std::shared_ptr<_Grouping> ptr = std::get>(expr.value); + assert(ptr != nullptr); + return evaluate(ptr->expr); +} + +Object AstInterpreter::evaluate_literal_expression(const Expr &expr) { + std::shared_ptr<_Literal> ptr = std::get>(expr.value); + assert(ptr != nullptr); + return ptr->value; +} + +Object AstInterpreter::evaluate_unary_expression(const Expr &expr) { + std::shared_ptr<_Unary> ptr = std::get>(expr.value); + assert(ptr != nullptr); + + Object right = evaluate(ptr->right); + + switch (ptr->op.type) { + case TokenType::MINUS: { + switch (right.type) { + case ObjectType::NUMBER: { + double value = std::get(right.value); + return -value; + } + default: throw RuntimeException{ptr->op, "Operand to unary operator - must be a number."}; + } + } + case TokenType::BANG: + return !is_truthy(right); + default: break; + } + + return Object{}; +} + +bool AstInterpreter::is_truthy(const Object &object) { + switch (object.type) { + case ObjectType::NIL: return false; + case ObjectType::BOOL: { + bool value = std::get(object.value); + return value; + } + default: return true; + } +} diff --git a/cclox_src/parser/interpreter.hh b/cclox_src/parser/interpreter.hh new file mode 100644 index 0000000..b718d4f --- /dev/null +++ b/cclox_src/parser/interpreter.hh @@ -0,0 +1,21 @@ +#ifndef AST_INTERPRETER_HH +#define AST_INTERPRETER_HH + +#include "expr.hh" +#include "../tokenizer.hh" +#include + +struct AstInterpreter { + void interpret(const Expr &expression); + +private: + Object evaluate(const Expr &expr); + Object evaluate_binary_expression(const Expr &expr); + Object evaluate_grouping_expression(const Expr &expr); + Object evaluate_literal_expression(const Expr &expr); + Object evaluate_unary_expression(const Expr &expr); + bool is_truthy(const Object &object); + bool is_equal(const Object &left, const Object &right); +}; + +#endif diff --git a/cclox_src/scanner/object.cc b/cclox_src/scanner/object.cc index 006d661..864d1a1 100644 --- a/cclox_src/scanner/object.cc +++ b/cclox_src/scanner/object.cc @@ -21,6 +21,35 @@ std::string Object::to_string() { } } +bool Object::operator==(const Object &other) { + if (type != other.type) { + return false; + } + + switch (type) { + case ObjectType::NIL: return true; + case ObjectType::BOOL: { + bool left = std::get(value); + bool right = std::get(other.value); + + return left == right; + } + case ObjectType::IDENTIFIER: + case ObjectType::STRING_LIT: { + std::string left = std::get(value); + std::string right = std::get(other.value); + + return left == right; + } + case ObjectType::NUMBER: { + double left = std::get(value); + double right = std::get(other.value); + + return left == right; + } + } +} + std::ostream &operator<<(std::ostream &os, const ObjectType &type) { switch (type) { case ObjectType::NIL: @@ -64,3 +93,7 @@ std::ostream &operator<<(std::ostream &os, const Object &obj) { return os; } + +bool Object::operator!=(const Object &other) { + return !operator==(other); +} diff --git a/cclox_src/scanner/object.hh b/cclox_src/scanner/object.hh index 76511d2..ee919e9 100644 --- a/cclox_src/scanner/object.hh +++ b/cclox_src/scanner/object.hh @@ -30,6 +30,8 @@ struct Object { }, value{std::move(value)} {}; std::string to_string(); + bool operator==(const Object &other); + bool operator!=(const Object &other); ObjectType type; std::variant value;