Add expression evaluation
This commit is contained in:
168
cclox_src/parser/interpreter.cc
Normal file
168
cclox_src/parser/interpreter.cc
Normal file
@@ -0,0 +1,168 @@
|
||||
#include "interpreter.hh"
|
||||
#include "expr.hh"
|
||||
#include "../error_handler.hh"
|
||||
#include "../tokenizer.hh"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
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<std::shared_ptr<_Binary>>(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<double>(left.value);
|
||||
double right_val = std::get<double>(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<std::string>(left.value);
|
||||
std::string right_val = std::get<std::string>(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<bool>(left.value);
|
||||
bool right_val = std::get<bool>(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<std::shared_ptr<_Grouping>>(expr.value);
|
||||
assert(ptr != nullptr);
|
||||
return evaluate(ptr->expr);
|
||||
}
|
||||
|
||||
Object AstInterpreter::evaluate_literal_expression(const Expr &expr) {
|
||||
std::shared_ptr<_Literal> ptr = std::get<std::shared_ptr<_Literal>>(expr.value);
|
||||
assert(ptr != nullptr);
|
||||
return ptr->value;
|
||||
}
|
||||
|
||||
Object AstInterpreter::evaluate_unary_expression(const Expr &expr) {
|
||||
std::shared_ptr<_Unary> ptr = std::get<std::shared_ptr<_Unary>>(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<double>(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<bool>(object.value);
|
||||
return value;
|
||||
}
|
||||
default: return true;
|
||||
}
|
||||
}
|
||||
21
cclox_src/parser/interpreter.hh
Normal file
21
cclox_src/parser/interpreter.hh
Normal file
@@ -0,0 +1,21 @@
|
||||
#ifndef AST_INTERPRETER_HH
|
||||
#define AST_INTERPRETER_HH
|
||||
|
||||
#include "expr.hh"
|
||||
#include "../tokenizer.hh"
|
||||
#include <string>
|
||||
|
||||
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
|
||||
Reference in New Issue
Block a user