Start adding statements
This commit is contained in:
parent
2690c07220
commit
4f39417d3e
@ -2,10 +2,10 @@
|
||||
#include "error_handler.hh"
|
||||
#include "tokenizer.hh"
|
||||
#include "parser.hh"
|
||||
#include <vector>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <sysexits.h>
|
||||
|
||||
#define PROMPT "> "
|
||||
@ -57,15 +57,12 @@ void run(const std::string &code) {
|
||||
std::vector<Token> tokens = scanner.scan_tokens();
|
||||
|
||||
Parser parser{tokens};
|
||||
std::optional<Expr> expression = parser.parse();
|
||||
|
||||
static AstInterpreter interpreter{};
|
||||
std::vector<Stmt> statements = parser.parse();
|
||||
|
||||
if (error_handler.had_error) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (expression) {
|
||||
interpreter.interpret(*expression);
|
||||
}
|
||||
static AstInterpreter interpreter{};
|
||||
interpreter.interpret(statements);
|
||||
}
|
||||
|
@ -1,8 +1,10 @@
|
||||
#ifndef PARSER_CC
|
||||
#define PARSER_CC
|
||||
#ifndef MAIN_PARSER_CC
|
||||
#define MAIN_PARSER_CC
|
||||
|
||||
#include "parser/expr.cc"
|
||||
#include "parser/stmt.cc"
|
||||
#include "parser/parser.cc"
|
||||
#include "parser/environment.cc"
|
||||
#include "parser/ast_printer.cc"
|
||||
#include "parser/ast_interpreter.cc"
|
||||
|
||||
|
@ -2,7 +2,9 @@
|
||||
#define MAIN_PARSER_HH
|
||||
|
||||
#include "parser/expr.hh"
|
||||
#include "parser/stmt.hh"
|
||||
#include "parser/parser.hh"
|
||||
#include "parser/environment.hh"
|
||||
#include "parser/ast_printer.hh"
|
||||
#include "parser/ast_interpreter.hh"
|
||||
|
||||
|
@ -1,33 +1,73 @@
|
||||
#include "ast_interpreter.hh"
|
||||
#include "expr.hh"
|
||||
#include "stmt.hh"
|
||||
#include "../error_handler.hh"
|
||||
#include "../tokenizer.hh"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
|
||||
extern ErrorHandler error_handler;
|
||||
|
||||
void AstInterpreter::interpret(const Expr &expression) {
|
||||
void AstInterpreter::interpret(const std::vector<Stmt> &statements) {
|
||||
try {
|
||||
Object value = evaluate(expression);
|
||||
std::cout << value.to_string() << '\n';
|
||||
for (const Stmt & stmt : statements) {
|
||||
execute(stmt);
|
||||
}
|
||||
} catch (RuntimeException e) {
|
||||
error_handler.runtime_error(e);
|
||||
}
|
||||
}
|
||||
|
||||
void AstInterpreter::execute(const Stmt &stmt) {
|
||||
switch (stmt.type) {
|
||||
case StmtType::EXPRESSION: return execute_expression_statement(stmt);
|
||||
case StmtType::PRINT: return execute_print_statement(stmt);
|
||||
case StmtType::VAR: return execute_var_statement(stmt);
|
||||
}
|
||||
}
|
||||
|
||||
void AstInterpreter::execute_expression_statement(const Stmt &stmt) {
|
||||
std::shared_ptr<_Expression> ptr = std::get<std::shared_ptr<_Expression>>(stmt.value);
|
||||
assert(ptr != nullptr);
|
||||
evaluate(ptr->expr);
|
||||
}
|
||||
|
||||
void AstInterpreter::execute_print_statement(const Stmt &stmt) {
|
||||
std::shared_ptr<_Print> ptr = std::get<std::shared_ptr<_Print>>(stmt.value);
|
||||
assert(ptr != nullptr);
|
||||
Object value{evaluate(ptr->expr)};
|
||||
std::cout << value << '\n';
|
||||
}
|
||||
|
||||
void AstInterpreter::execute_var_statement(const Stmt &stmt) {
|
||||
Object value{};
|
||||
std::shared_ptr<_Var> ptr = std::get<std::shared_ptr<_Var>>(stmt.value);
|
||||
assert(ptr != nullptr);
|
||||
|
||||
if (ptr->expr.type == ExprType::LITERAL) {
|
||||
std::shared_ptr<_Literal> eptr = std::get<std::shared_ptr<_Literal>>(ptr->expr.value);
|
||||
assert(eptr != nullptr);
|
||||
if (eptr->value.type == ObjectType::NIL) {
|
||||
environment.define(ptr->name.lexeme, value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
value = evaluate(ptr->expr);
|
||||
environment.define(ptr->name.lexeme, value);
|
||||
}
|
||||
|
||||
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);
|
||||
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);
|
||||
case ExprType::VARIABLE: return evaluate_variable_expression(expr);
|
||||
case ExprType::ASSIGN: return evaluate_assignment_expression(expr);
|
||||
}
|
||||
}
|
||||
|
||||
@ -156,6 +196,20 @@ Object AstInterpreter::evaluate_unary_expression(const Expr &expr) {
|
||||
return Object{};
|
||||
}
|
||||
|
||||
Object AstInterpreter::evaluate_variable_expression(const Expr &expr) {
|
||||
std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(expr.value);
|
||||
assert(ptr != nullptr);
|
||||
return environment.get(ptr->name);
|
||||
}
|
||||
|
||||
Object AstInterpreter::evaluate_assignment_expression(const Expr &expr) {
|
||||
std::shared_ptr<_Assign> ptr = std::get<std::shared_ptr<_Assign>>(expr.value);
|
||||
assert(ptr != nullptr);
|
||||
Object value = evaluate(ptr->value);
|
||||
environment.assign(ptr->name, value);
|
||||
return value;
|
||||
}
|
||||
|
||||
bool AstInterpreter::is_truthy(const Object &object) {
|
||||
switch (object.type) {
|
||||
case ObjectType::NIL: return false;
|
||||
|
@ -2,20 +2,31 @@
|
||||
#define AST_INTERPRETER_HH
|
||||
|
||||
#include "expr.hh"
|
||||
#include "stmt.hh"
|
||||
#include "environment.hh"
|
||||
#include "../tokenizer.hh"
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
struct AstInterpreter {
|
||||
void interpret(const Expr &expression);
|
||||
void interpret(const std::vector<Stmt> &statements);
|
||||
|
||||
private:
|
||||
void execute(const Stmt &stmt);
|
||||
void execute_expression_statement(const Stmt &stmt);
|
||||
void execute_print_statement(const Stmt &stmt);
|
||||
void execute_var_statement(const Stmt &stmt);
|
||||
|
||||
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);
|
||||
Object evaluate_variable_expression(const Expr &expr);
|
||||
Object evaluate_assignment_expression(const Expr &expr);
|
||||
bool is_truthy(const Object &object);
|
||||
bool is_equal(const Object &left, const Object &right);
|
||||
|
||||
Environment environment{};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -7,24 +7,34 @@
|
||||
std::string AstPrinter::print(const Expr &expr) {
|
||||
switch (expr.type) {
|
||||
case ExprType::BINARY: {
|
||||
std::shared_ptr<_Binary> binary = std::get<std::shared_ptr<_Binary>>(expr.value);
|
||||
assert(binary != nullptr);
|
||||
return parenthesize(binary->op.lexeme, {&binary->left, &binary->right});
|
||||
std::shared_ptr<_Binary> ptr = std::get<std::shared_ptr<_Binary>>(expr.value);
|
||||
assert(ptr != nullptr);
|
||||
return parenthesize(ptr->op.lexeme, {&ptr->left, &ptr->right});
|
||||
}
|
||||
case ExprType::GROUPING: {
|
||||
std::shared_ptr<_Grouping> group = std::get<std::shared_ptr<_Grouping>>(expr.value);
|
||||
assert(group != nullptr);
|
||||
return parenthesize("group", {&group->expr});
|
||||
std::shared_ptr<_Grouping> ptr = std::get<std::shared_ptr<_Grouping>>(expr.value);
|
||||
assert(ptr != nullptr);
|
||||
return parenthesize("group", {&ptr->expr});
|
||||
}
|
||||
case ExprType::LITERAL: {
|
||||
std::shared_ptr<_Literal> literal = std::get<std::shared_ptr<_Literal>>(expr.value);
|
||||
assert(literal != nullptr);
|
||||
return literal->value.to_string();
|
||||
std::shared_ptr<_Literal> ptr = std::get<std::shared_ptr<_Literal>>(expr.value);
|
||||
assert(ptr != nullptr);
|
||||
return ptr->value.to_string();
|
||||
}
|
||||
case ExprType::UNARY: {
|
||||
std::shared_ptr<_Unary> unary = std::get<std::shared_ptr<_Unary>>(expr.value);
|
||||
assert(unary != nullptr);
|
||||
return parenthesize(unary->op.lexeme, {&unary->right});
|
||||
std::shared_ptr<_Unary> ptr = std::get<std::shared_ptr<_Unary>>(expr.value);
|
||||
assert(ptr != nullptr);
|
||||
return parenthesize(ptr->op.lexeme, {&ptr->right});
|
||||
}
|
||||
case ExprType::VARIABLE: {
|
||||
std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(expr.value);
|
||||
assert(ptr != nullptr);
|
||||
return ptr->name.lexeme;
|
||||
}
|
||||
case ExprType::ASSIGN: {
|
||||
std::shared_ptr<_Assign> ptr = std::get<std::shared_ptr<_Assign>>(expr.value);
|
||||
assert(ptr != nullptr);
|
||||
return ptr->name.lexeme;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
64
cclox_src/parser/environment.cc
Normal file
64
cclox_src/parser/environment.cc
Normal file
@ -0,0 +1,64 @@
|
||||
#include "environment.hh"
|
||||
#include "../error_handler.hh"
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
Environment::Environment(const Environment &other) : enclosing{other.enclosing}, values{other.values} {}
|
||||
|
||||
Environment::Environment(Environment &&other) noexcept : enclosing{std::move(other.enclosing)}, values{std::move(other.values)} {
|
||||
other.enclosing.reset();
|
||||
}
|
||||
|
||||
Environment &Environment::operator=(const Environment &other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (enclosing) {
|
||||
enclosing.reset();
|
||||
}
|
||||
|
||||
enclosing = other.enclosing;
|
||||
values = other.values;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Environment &Environment::operator=(Environment &&other) noexcept {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
if (enclosing) {
|
||||
enclosing.reset();
|
||||
}
|
||||
|
||||
enclosing = std::move(other.enclosing);
|
||||
values = std::move(other.values);
|
||||
|
||||
other.enclosing.reset();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Environment::define(const std::string &name, const Object &value) {
|
||||
values.insert(std::pair(name, value));
|
||||
}
|
||||
|
||||
void Environment::assign(const Token &name, const Object &value) {
|
||||
if (values.find(name.lexeme) != values.end()) {
|
||||
values.at(name.lexeme) = value;
|
||||
return;
|
||||
}
|
||||
|
||||
throw RuntimeException(name, "Undefined variable '" + name.lexeme + "'.");
|
||||
}
|
||||
|
||||
Object Environment::get(const Token &name) {
|
||||
if (values.find(name.lexeme) != values.end()) {
|
||||
return values.at(name.lexeme);
|
||||
}
|
||||
|
||||
throw RuntimeException(name, "Undefined variable '" + name.lexeme + "'.");
|
||||
}
|
28
cclox_src/parser/environment.hh
Normal file
28
cclox_src/parser/environment.hh
Normal file
@ -0,0 +1,28 @@
|
||||
#ifndef ENVIRONMENT_HH
|
||||
#define ENVIRONMENT_HH
|
||||
|
||||
#include "../tokenizer.hh"
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
struct Environment {
|
||||
Environment() : enclosing{nullptr}, values{} {}
|
||||
Environment(std::shared_ptr<Environment> enclosing) : enclosing{enclosing}, values{} {}
|
||||
Environment(const Environment &other);
|
||||
Environment(Environment &&other) noexcept;
|
||||
|
||||
Environment &operator=(const Environment &other);
|
||||
Environment &operator=(Environment &&other) noexcept;
|
||||
|
||||
~Environment() = default;
|
||||
|
||||
void define(const std::string &name, const Object &value);
|
||||
void assign(const Token &name, const Object &value);
|
||||
Object get(const Token &name);
|
||||
private:
|
||||
std::shared_ptr<Environment> enclosing;
|
||||
std::unordered_map<std::string, Object> values;
|
||||
};
|
||||
|
||||
#endif
|
@ -33,11 +33,23 @@ Expr::Expr(const Expr &other) : type{other.type} {
|
||||
value = std::make_shared<_Unary>(*ptr);
|
||||
break;
|
||||
}
|
||||
case ExprType::VARIABLE: {
|
||||
std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::make_shared<_Variable>(*ptr);
|
||||
break;
|
||||
}
|
||||
case ExprType::ASSIGN: {
|
||||
std::shared_ptr<_Assign> ptr = std::get<std::shared_ptr<_Assign>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::make_shared<_Assign>(*ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move constructor
|
||||
Expr::Expr(const Expr &&other) : type{other.type}, value{std::move(other.value)} {
|
||||
Expr::Expr(Expr &&other) : type{other.type}, value{std::move(other.value)} {
|
||||
switch(type) {
|
||||
case ExprType::BINARY: {
|
||||
std::shared_ptr<_Binary> ptr = std::get<std::shared_ptr<_Binary>>(other.value);
|
||||
@ -63,6 +75,18 @@ Expr::Expr(const Expr &&other) : type{other.type}, value{std::move(other.value)}
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case ExprType::VARIABLE: {
|
||||
std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case ExprType::ASSIGN: {
|
||||
std::shared_ptr<_Assign> ptr = std::get<std::shared_ptr<_Assign>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -97,6 +121,18 @@ Expr &Expr::operator=(const Expr &other) {
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case ExprType::VARIABLE: {
|
||||
std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case ExprType::ASSIGN: {
|
||||
std::shared_ptr<_Assign> ptr = std::get<std::shared_ptr<_Assign>>(value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
type = other.type;
|
||||
@ -126,13 +162,25 @@ Expr &Expr::operator=(const Expr &other) {
|
||||
value = std::make_shared<_Unary>(*ptr);
|
||||
break;
|
||||
}
|
||||
case ExprType::VARIABLE: {
|
||||
std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::make_shared<_Variable>(*ptr);
|
||||
break;
|
||||
}
|
||||
case ExprType::ASSIGN: {
|
||||
std::shared_ptr<_Assign> ptr = std::get<std::shared_ptr<_Assign>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::make_shared<_Assign>(*ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Move assignment
|
||||
Expr &Expr::operator=(const Expr &&other) {
|
||||
Expr &Expr::operator=(Expr &&other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
@ -162,6 +210,18 @@ Expr &Expr::operator=(const Expr &&other) {
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case ExprType::VARIABLE: {
|
||||
std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case ExprType::ASSIGN: {
|
||||
std::shared_ptr<_Assign> ptr = std::get<std::shared_ptr<_Assign>>(value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
type = other.type;
|
||||
@ -195,6 +255,20 @@ Expr &Expr::operator=(const Expr &&other) {
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case ExprType::VARIABLE: {
|
||||
std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::move(other.value);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case ExprType::ASSIGN: {
|
||||
std::shared_ptr<_Assign> ptr = std::get<std::shared_ptr<_Assign>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::move(other.value);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
|
@ -10,25 +10,33 @@ struct _Binary;
|
||||
struct _Grouping;
|
||||
struct _Literal;
|
||||
struct _Unary;
|
||||
struct _Variable;
|
||||
struct _Assign;
|
||||
|
||||
enum class ExprType {
|
||||
BINARY,
|
||||
GROUPING,
|
||||
LITERAL,
|
||||
UNARY,
|
||||
VARIABLE,
|
||||
ASSIGN,
|
||||
};
|
||||
|
||||
using ExprVariant = std::variant<
|
||||
std::shared_ptr<_Binary>,
|
||||
std::shared_ptr<_Grouping>,
|
||||
std::shared_ptr<_Literal>,
|
||||
std::shared_ptr<_Unary>
|
||||
std::shared_ptr<_Unary>,
|
||||
std::shared_ptr<_Variable>,
|
||||
std::shared_ptr<_Assign>
|
||||
>;
|
||||
|
||||
#define Binary(LEFT, OP, RIGHT) (Expr{ExprType::BINARY, std::make_shared<_Binary>(LEFT, OP, RIGHT)})
|
||||
#define Grouping(EXPR) (Expr{ExprType::GROUPING, std::make_shared<_Grouping>(EXPR)})
|
||||
#define Literal(VALUE) (Expr{ExprType::LITERAL, std::make_shared<_Literal>(VALUE)})
|
||||
#define Unary(OP, RIGHT) (Expr{ExprType::UNARY, std::make_shared<_Unary>(OP, RIGHT)})
|
||||
#define Variable(NAME) (Expr{ExprType::VARIABLE, std::make_shared<_Variable>(NAME)})
|
||||
#define Assign(NAME, VALUE) (Expr{ExprType::ASSIGN, std::make_shared<_Assign>(NAME, VALUE)})
|
||||
|
||||
struct Expr {
|
||||
Expr(ExprType type, ExprVariant value);
|
||||
@ -37,13 +45,13 @@ struct Expr {
|
||||
Expr(const Expr &other);
|
||||
|
||||
// Move constructor
|
||||
Expr(const Expr &&other);
|
||||
Expr(Expr &&other);
|
||||
|
||||
// Copy assignment
|
||||
Expr &operator=(const Expr &other);
|
||||
|
||||
// Move assignment
|
||||
Expr &operator=(const Expr &&other);
|
||||
Expr &operator=(Expr &&other);
|
||||
|
||||
ExprType type;
|
||||
ExprVariant value;
|
||||
@ -72,4 +80,15 @@ struct _Unary {
|
||||
Expr right;
|
||||
};
|
||||
|
||||
struct _Variable {
|
||||
_Variable(Token name) : name{name} {}
|
||||
Token name;
|
||||
};
|
||||
|
||||
struct _Assign {
|
||||
_Assign(Token name, const Expr &value) : name{name}, value{value} {}
|
||||
Token name;
|
||||
Expr value;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
@ -1,21 +1,88 @@
|
||||
#include "parser.hh"
|
||||
#include "expr.hh"
|
||||
#include "stmt.hh"
|
||||
#include "../error_handler.hh"
|
||||
#include "../tokenizer.hh"
|
||||
#include <optional>
|
||||
#include <cassert>
|
||||
|
||||
extern ErrorHandler error_handler;
|
||||
|
||||
std::optional<Expr> Parser::parse() {
|
||||
std::vector<Stmt> Parser::parse() {
|
||||
std::vector<Stmt> statements{};
|
||||
while (!is_at_end()) {
|
||||
statements.push_back(declaration());
|
||||
}
|
||||
|
||||
return statements;
|
||||
}
|
||||
|
||||
Stmt Parser::declaration() {
|
||||
try {
|
||||
return expression();
|
||||
if (match({TokenType::VAR})) {
|
||||
return var_declaration();
|
||||
}
|
||||
|
||||
return statement();
|
||||
} catch (ParseException e) {
|
||||
return std::nullopt;
|
||||
synchronize();
|
||||
return Expression(Literal(Object{}));
|
||||
}
|
||||
}
|
||||
|
||||
Stmt Parser::var_declaration() {
|
||||
Token name = consume(TokenType::IDENTIFIER, "Expect variable name.");
|
||||
Expr initializer = Literal(Object{});
|
||||
|
||||
if (match({TokenType::EQUAL})) {
|
||||
initializer = expression();
|
||||
}
|
||||
|
||||
consume(TokenType::SEMICOLON, "Expect ';' after variable declaration");
|
||||
return Var(name, initializer);
|
||||
}
|
||||
|
||||
Stmt Parser::statement() {
|
||||
if (match({TokenType::PRINT})) {
|
||||
return print_statement();
|
||||
}
|
||||
|
||||
return expression_statement();
|
||||
}
|
||||
|
||||
Stmt Parser::print_statement() {
|
||||
Expr value{expression()};
|
||||
consume(TokenType::SEMICOLON, "Expect ';' after value.");
|
||||
return Print(value);
|
||||
}
|
||||
|
||||
Stmt Parser::expression_statement() {
|
||||
Expr value{expression()};
|
||||
consume(TokenType::SEMICOLON, "Expect ';' after value.");
|
||||
return Expression(value);
|
||||
}
|
||||
|
||||
Expr Parser::expression() {
|
||||
return equality();
|
||||
return assignment();
|
||||
}
|
||||
|
||||
Expr Parser::assignment() {
|
||||
Expr expr = equality();
|
||||
|
||||
if (match({TokenType::EQUAL})) {
|
||||
Token equals = previous();
|
||||
Expr value = assignment();
|
||||
|
||||
if (expr.type == ExprType::VARIABLE) {
|
||||
std::shared_ptr<_Variable> ptr = std::get<std::shared_ptr<_Variable>>(expr.value);
|
||||
assert(ptr != nullptr);
|
||||
Token name = ptr->name;
|
||||
return Assign(name, value);
|
||||
}
|
||||
|
||||
error_handler.error(equals, "Invalid assignment target.");
|
||||
}
|
||||
|
||||
return expr;
|
||||
}
|
||||
|
||||
Expr Parser::equality() {
|
||||
@ -94,6 +161,10 @@ Expr Parser::primary() {
|
||||
return Literal(previous().literal);
|
||||
}
|
||||
|
||||
if (match({TokenType::IDENTIFIER})) {
|
||||
return Variable(previous());
|
||||
}
|
||||
|
||||
if (match({TokenType::LEFT_PAREN})) {
|
||||
Expr expr = expression();
|
||||
consume(TokenType::RIGHT_PAREN, "Expect ')' after expression.");
|
||||
|
@ -2,10 +2,10 @@
|
||||
#define PARSER_HH
|
||||
|
||||
#include "expr.hh"
|
||||
#include "stmt.hh"
|
||||
#include "../tokenizer.hh"
|
||||
#include <cstddef>
|
||||
#include <exception>
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
|
||||
struct ParseException : public std::exception {
|
||||
@ -21,8 +21,14 @@ private:
|
||||
|
||||
struct Parser {
|
||||
Parser(std::vector<Token> tokens) : tokens{tokens} {}
|
||||
std::optional<Expr> parse();
|
||||
std::vector<Stmt> parse();
|
||||
Stmt declaration();
|
||||
Stmt var_declaration();
|
||||
Stmt statement();
|
||||
Stmt print_statement();
|
||||
Stmt expression_statement();
|
||||
Expr expression();
|
||||
Expr assignment();
|
||||
Expr equality();
|
||||
Expr comparison();
|
||||
Expr term();
|
||||
|
162
cclox_src/parser/stmt.cc
Normal file
162
cclox_src/parser/stmt.cc
Normal file
@ -0,0 +1,162 @@
|
||||
#include "stmt.hh"
|
||||
#include <cassert>
|
||||
#include <utility>
|
||||
|
||||
Stmt::Stmt(StmtType type, StmtVariant value) : type{type}, value{value} {}
|
||||
|
||||
// Copy constructor
|
||||
Stmt::Stmt(const Stmt &other) : type{other.type} {
|
||||
switch(type) {
|
||||
case StmtType::EXPRESSION: {
|
||||
std::shared_ptr<_Expression> ptr = std::get<std::shared_ptr<_Expression>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::make_shared<_Expression>(*ptr);
|
||||
break;
|
||||
}
|
||||
case StmtType::PRINT: {
|
||||
std::shared_ptr<_Print> ptr = std::get<std::shared_ptr<_Print>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::make_shared<_Print>(*ptr);
|
||||
break;
|
||||
}
|
||||
case StmtType::VAR: {
|
||||
std::shared_ptr<_Var> ptr = std::get<std::shared_ptr<_Var>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::make_shared<_Var>(*ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move constructor
|
||||
Stmt::Stmt(const Stmt &&other) : type{other.type}, value{std::move(other.value)} {
|
||||
switch(type) {
|
||||
case StmtType::EXPRESSION: {
|
||||
std::shared_ptr<_Expression> ptr = std::get<std::shared_ptr<_Expression>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case StmtType::PRINT: {
|
||||
std::shared_ptr<_Print> ptr = std::get<std::shared_ptr<_Print>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case StmtType::VAR: {
|
||||
std::shared_ptr<_Var> ptr = std::get<std::shared_ptr<_Var>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Copy assignment
|
||||
Stmt &Stmt::operator=(const Stmt &other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case StmtType::EXPRESSION: {
|
||||
std::shared_ptr<_Expression> ptr = std::get<std::shared_ptr<_Expression>>(value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case StmtType::PRINT: {
|
||||
std::shared_ptr<_Print> ptr = std::get<std::shared_ptr<_Print>>(value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case StmtType::VAR: {
|
||||
std::shared_ptr<_Var> ptr = std::get<std::shared_ptr<_Var>>(value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
type = other.type;
|
||||
|
||||
switch(type) {
|
||||
case StmtType::EXPRESSION: {
|
||||
std::shared_ptr<_Expression> ptr = std::get<std::shared_ptr<_Expression>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::make_shared<_Expression>(*ptr);
|
||||
break;
|
||||
}
|
||||
case StmtType::PRINT: {
|
||||
std::shared_ptr<_Print> ptr = std::get<std::shared_ptr<_Print>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::make_shared<_Print>(*ptr);
|
||||
break;
|
||||
}
|
||||
case StmtType::VAR: {
|
||||
std::shared_ptr<_Var> ptr = std::get<std::shared_ptr<_Var>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::make_shared<_Var>(*ptr);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// Move assignment
|
||||
Stmt &Stmt::operator=(const Stmt &&other) {
|
||||
if (this == &other) {
|
||||
return *this;
|
||||
}
|
||||
|
||||
switch(type) {
|
||||
case StmtType::EXPRESSION: {
|
||||
std::shared_ptr<_Expression> ptr = std::get<std::shared_ptr<_Expression>>(value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case StmtType::PRINT: {
|
||||
std::shared_ptr<_Print> ptr = std::get<std::shared_ptr<_Print>>(value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case StmtType::VAR: {
|
||||
std::shared_ptr<_Var> ptr = std::get<std::shared_ptr<_Var>>(value);
|
||||
assert(ptr != nullptr);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
type = other.type;
|
||||
|
||||
switch(type) {
|
||||
case StmtType::EXPRESSION: {
|
||||
std::shared_ptr<_Expression> ptr = std::get<std::shared_ptr<_Expression>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::move(other.value);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case StmtType::PRINT: {
|
||||
std::shared_ptr<_Print> ptr = std::get<std::shared_ptr<_Print>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::move(other.value);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
case StmtType::VAR: {
|
||||
std::shared_ptr<_Var> ptr = std::get<std::shared_ptr<_Var>>(other.value);
|
||||
assert(ptr != nullptr);
|
||||
value = std::move(other.value);
|
||||
ptr.reset();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
64
cclox_src/parser/stmt.hh
Normal file
64
cclox_src/parser/stmt.hh
Normal file
@ -0,0 +1,64 @@
|
||||
#ifndef STMT_HH
|
||||
#define STMT_HH
|
||||
|
||||
#include "expr.hh"
|
||||
#include <memory>
|
||||
#include <variant>
|
||||
|
||||
struct Stmt;
|
||||
struct _Expression;
|
||||
struct _Print;
|
||||
struct _Var;
|
||||
|
||||
enum class StmtType {
|
||||
EXPRESSION,
|
||||
PRINT,
|
||||
VAR,
|
||||
};
|
||||
|
||||
using StmtVariant = std::variant<
|
||||
std::shared_ptr<_Expression>,
|
||||
std::shared_ptr<_Print>,
|
||||
std::shared_ptr<_Var>
|
||||
>;
|
||||
|
||||
#define Expression(EXPR) (Stmt{StmtType::EXPRESSION, std::make_shared<_Expression>(EXPR)})
|
||||
#define Print(EXPR) (Stmt{StmtType::PRINT, std::make_shared<_Print>(EXPR)})
|
||||
#define Var(NAME, EXPR) (Stmt{StmtType::VAR, std::make_shared<_Var>(NAME, EXPR)})
|
||||
|
||||
struct Stmt {
|
||||
Stmt(StmtType type, StmtVariant value);
|
||||
|
||||
// Copy constructor
|
||||
Stmt(const Stmt &other);
|
||||
|
||||
// Move constructor
|
||||
Stmt(const Stmt &&other);
|
||||
|
||||
// Copy assignment
|
||||
Stmt &operator=(const Stmt &other);
|
||||
|
||||
// Move assignment
|
||||
Stmt &operator=(const Stmt &&other);
|
||||
|
||||
StmtType type;
|
||||
StmtVariant value;
|
||||
};
|
||||
|
||||
struct _Expression {
|
||||
_Expression(const Expr &expr) : expr{expr} {}
|
||||
Expr expr;
|
||||
};
|
||||
|
||||
struct _Print {
|
||||
_Print(const Expr &expr) : expr{expr} {}
|
||||
Expr expr;
|
||||
};
|
||||
|
||||
struct _Var {
|
||||
_Var(const Token &name, const Expr &expr) : name{name}, expr{expr} {}
|
||||
Token name;
|
||||
Expr expr;
|
||||
};
|
||||
|
||||
#endif
|
@ -2,7 +2,7 @@
|
||||
#include <cstddef>
|
||||
#include <string>
|
||||
|
||||
std::string Object::to_string() {
|
||||
std::string Object::to_string() const {
|
||||
switch (type) {
|
||||
case ObjectType::NIL:
|
||||
return "nil";
|
||||
@ -78,25 +78,7 @@ std::ostream &operator<<(std::ostream &os, const ObjectType &type) {
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const Object &obj) {
|
||||
os << "Object(" << obj.type;
|
||||
|
||||
switch (obj.type) {
|
||||
case ObjectType::NIL:
|
||||
break;
|
||||
case ObjectType::BOOL:
|
||||
os << ", " << std::to_string(std::get<bool>(obj.value));
|
||||
break;
|
||||
case ObjectType::IDENTIFIER:
|
||||
case ObjectType::STRING_LIT:
|
||||
os << ", " << std::get<std::string>(obj.value);
|
||||
break;
|
||||
case ObjectType::NUMBER:
|
||||
os << ", " << std::get<double>(obj.value);
|
||||
break;
|
||||
}
|
||||
|
||||
os << ')';
|
||||
|
||||
os << obj.to_string();
|
||||
return os;
|
||||
}
|
||||
|
||||
|
@ -29,7 +29,7 @@ struct Object {
|
||||
ObjectType::STRING_LIT
|
||||
},
|
||||
value{std::move(value)} {};
|
||||
std::string to_string();
|
||||
std::string to_string() const;
|
||||
bool operator==(const Object &other);
|
||||
bool operator!=(const Object &other);
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user