GSC Interpreter
A Turing-complete interpreter developed for a compiler course
Loading...
Searching...
No Matches
interpreter.cpp
Go to the documentation of this file.
1#include "gsc/interpreter.hpp"
2#include "gsc/error.hpp"
3#include <cassert>
4#include <iostream>
5
6void Interpreter::interpret(
7 const std::vector<std::shared_ptr<Stmt>> &statements) {
8 try {
9 for (const std::shared_ptr<Stmt> &stmt : statements) {
10 execute(stmt);
11 }
12 } catch (RuntimeError &error) {
13 runtimeError(error);
14 }
15}
16
17std::any Interpreter::evaluate(std::shared_ptr<Expr> expr) {
18 return expr->accept(*this);
19}
20
21void Interpreter::execute(std::shared_ptr<Stmt> stmt) { stmt->accept(*this); }
22
23void Interpreter::executeBlock(
24 const std::vector<std::shared_ptr<Stmt>> statements,
25 std::shared_ptr<Environment> environment) {
26 std::shared_ptr<Environment> previous = this->environment;
27 try {
28 this->environment = environment;
29
30 for (const std::shared_ptr<Stmt> &stmt : statements) {
31 execute(stmt);
32 }
33 } catch (...) {
34 this->environment = previous;
35 throw; // Re-throw the exception to be handled by the caller
36 }
37
38 this->environment = previous;
39}
40
41template <class... N>
42void Interpreter::checkNumberOperands(const Token &op, N... operands) {
43 if (((operands.type() != typeid(int)) && ...)) {
44 throw RuntimeError(std::make_shared<Token>(op),
45 "Operands must be numbers.");
46 }
47}
48
49bool Interpreter::isTruthy(const std::any &value) const {
50 if (value.type() == typeid(std::nullptr_t)) {
51 return false;
52 } else if (value.type() == typeid(int)) {
53 return std::any_cast<int>(value) != 0; // Non-zero integers are truthy
54 } else if (value.type() == typeid(std::string)) {
55 return !std::any_cast<std::string>(value)
56 .empty(); // Non-empty strings are truthy
57 } else if (value.type() == typeid(bool)) {
58 return std::any_cast<bool>(value);
59 }
60 return true; // All other values are truthy
61}
62
63bool Interpreter::isEqual(const std::any &a, const std::any &b) const {
64 if (a.type() != b.type()) {
65 return false;
66 } else if (a.type() == typeid(std::nullptr_t)) {
67 return true; // Both are nil
68 } else if (a.type() == typeid(bool)) {
69 return std::any_cast<bool>(a) == std::any_cast<bool>(b);
70 } else if (a.type() == typeid(int)) {
71 return std::any_cast<int>(a) == std::any_cast<int>(b);
72 } else if (a.type() == typeid(std::string)) {
73 return std::any_cast<std::string>(a) == std::any_cast<std::string>(b);
74 } else {
75 return false; // Unsupported types
76 }
77}
78
79std::string Interpreter::stringify(const std::any &value) const {
80 if (value.type() == typeid(std::nullptr_t)) {
81 return "nil";
82 } else if (value.type() == typeid(bool)) {
83 return std::any_cast<bool>(value) ? "true" : "false";
84 } else if (value.type() == typeid(int)) {
85 return std::to_string(std::any_cast<int>(value));
86 } else if (value.type() == typeid(std::string)) {
87 return std::any_cast<std::string>(value);
88 } else if (value.type() == typeid(const char *)) {
89 return std::any_cast<const char *>(value);
90 }
91 return "Internal error (interpreter): object type not recognized"; // Unsupported
92 // types
93}
94
95std::any Interpreter::visitGroupingExpr(std::shared_ptr<Grouping> expr) {
96 return evaluate(expr->getExpression());
97}
98
99std::any Interpreter::visitLiteralExpr(std::shared_ptr<Literal> expr) {
100 return expr->getValue();
101}
102
103std::any Interpreter::visitUnaryExpr(std::shared_ptr<Unary> expr) {
104 std::any right = evaluate(expr->getRight());
105 Token op = expr->getOp();
106
107 switch (op.getType()) {
108 case TokenType::MINUS:
109 checkNumberOperands(op, right);
110 return -std::any_cast<int>(right);
111 case TokenType::BANG:
112 return !isTruthy(right);
113 default:
114 // This should never be reached, but just in case
115 assert(false && "Unknown unary operator");
116 return {};
117 }
118}
119
120std::any Interpreter::visitBinaryExpr(std::shared_ptr<Binary> expr) {
121 std::any left = evaluate(expr->getLeft());
122 std::any right = evaluate(expr->getRight());
123 Token op = expr->getOp();
124
125 switch (op.getType()) {
126 case TokenType::PLUS:
127 if (left.type() == typeid(int) && right.type() == typeid(int)) {
128 return std::any_cast<int>(left) + std::any_cast<int>(right);
129 } else if (left.type() == typeid(std::string) &&
130 right.type() == typeid(std::string)) {
131 return std::any_cast<std::string>(left) +
132 std::any_cast<std::string>(right);
133 } else {
134 throw RuntimeError(std::make_shared<Token>(op),
135 "Operands must be two numbers or two strings.");
136 }
137 case TokenType::MINUS:
138 checkNumberOperands(op, left, right);
139 return std::any_cast<int>(left) - std::any_cast<int>(right);
140 case TokenType::STAR:
141 checkNumberOperands(op, left, right);
142 return std::any_cast<int>(left) * std::any_cast<int>(right);
143 case TokenType::SLASH:
144 checkNumberOperands(op, left, right);
145 if (std::any_cast<int>(right) == 0) {
146 throw RuntimeError(std::make_shared<Token>(op), "Division by zero.");
147 }
148 return std::any_cast<int>(left) / std::any_cast<int>(right);
149 case TokenType::GREATER:
150 checkNumberOperands(op, left, right);
151 return std::any_cast<int>(left) > std::any_cast<int>(right);
152 case TokenType::GREATER_EQUAL:
153 checkNumberOperands(op, left, right);
154 return std::any_cast<int>(left) >= std::any_cast<int>(right);
155 case TokenType::LESS:
156 checkNumberOperands(op, left, right);
157 return std::any_cast<int>(left) < std::any_cast<int>(right);
158 case TokenType::LESS_EQUAL:
159 checkNumberOperands(op, left, right);
160 return std::any_cast<int>(left) <= std::any_cast<int>(right);
161 case TokenType::EQUAL_EQUAL:
162 return isEqual(left, right);
163 case TokenType::BANG_EQUAL:
164 return !isEqual(left, right);
165 default:
166 // This should never be reached, but just in case
167 assert(false && "Unknown binary operator");
168 return {};
169 }
170}
171
172std::any Interpreter::visitLogicalExpr(std::shared_ptr<Logical> expr) {
173 std::any left = evaluate(expr->getLeft());
174 Token op = expr->getOp();
175
176 // Short-circuit evaluation
177 if ((op.getType() == TokenType::OR && isTruthy(left)) ||
178 (op.getType() == TokenType::AND && !isTruthy(left))) {
179 return left;
180 }
181
182 return evaluate(expr->getRight());
183}
184
185std::any Interpreter::visitAssignExpr(std::shared_ptr<Assign> expr) {
186 std::any value = evaluate(expr->getValue());
187 environment->assign(expr->getName(), value);
188 return value;
189}
190
191std::any Interpreter::visitVariableExpr(std::shared_ptr<Variable> expr) {
192 return environment->get(expr->getName());
193}
194
195std::any Interpreter::visitBlockStmt(std::shared_ptr<Block> stmt) {
196 executeBlock(stmt->getStatements(),
197 std::make_shared<Environment>(environment));
198 return {};
199}
200
201std::any Interpreter::visitExpressionStmt(std::shared_ptr<Expression> stmt) {
202 evaluate(stmt->getExpression());
203 return {};
204}
205
206std::any Interpreter::visitPrintStmt(std::shared_ptr<Print> stmt) {
207 std::any value = evaluate(stmt->getExpression());
208 std::cout << stringify(value) << std::endl;
209 return {};
210}
211
212std::any Interpreter::visitIfStmt(std::shared_ptr<If> stmt) {
213 std::any condition = evaluate(stmt->getCondition());
214 if (isTruthy(condition)) {
215 execute(stmt->getThenBranch());
216 } else if (stmt->getElseBranch()) {
217 execute(stmt->getElseBranch());
218 }
219 return {};
220}
221
222std::any Interpreter::visitWhileStmt(std::shared_ptr<While> stmt) {
223 while (isTruthy(evaluate(stmt->getCondition()))) {
224 execute(stmt->getBody());
225 }
226 return {};
227}
228
229std::any Interpreter::visitVarStmt(std::shared_ptr<Var> stmt) {
230 std::any value = nullptr;
231 if (stmt->getInitializer()) {
232 value = evaluate(stmt->getInitializer());
233 }
234 environment->define(stmt->getName().getLexeme(), std::move(value));
235 return {};
236}