Switch to macros for better readability and add printer.hh

This commit is contained in:
Abdelrahman Said 2025-06-28 18:27:00 +01:00
parent a6e129eb12
commit 1118e825f3
7 changed files with 237 additions and 101 deletions

View File

@ -18,13 +18,13 @@ int main(int argc, char *argv[]) {
// run_interpreter(argc, argv); // run_interpreter(argc, argv);
Token unary_op{TokenType::MINUS, "-", Object{}, 1}; Token unary_op{TokenType::MINUS, "-", Object{}, 1};
Expr left{unary_op, Expr{123}}; Expr left{Unary(unary_op, Literal(123))};
Token op{TokenType::STAR, "*", Object{}, 1}; Token op{TokenType::STAR, "*", Object{}, 1};
Expr right{Expr{45.67}, nullptr}; Expr right{Grouping(Literal(45.67))};
Expr expr{left, op, right}; Expr expr{Binary(left, op, right)};
AstPrinter printer{}; AstPrinter printer{};
std::cout << printer.print(expr) << '\n'; std::cout << printer.print(expr) << '\n';

View File

@ -2,5 +2,6 @@
#define PARSER_CC #define PARSER_CC
#include "parser/expr.cc" #include "parser/expr.cc"
#include "parser/printer.cc"
#endif #endif

View File

@ -2,5 +2,6 @@
#define PARSER_HH #define PARSER_HH
#include "parser/expr.hh" #include "parser/expr.hh"
#include "parser/printer.hh"
#endif #endif

View File

@ -2,101 +2,176 @@
#include "../tokenizer.hh" #include "../tokenizer.hh"
#include <cassert> #include <cassert>
#include <memory> #include <memory>
#include <string>
#include <utility> #include <utility>
#include <vector>
#include <sstream> Expr::Expr(ExprType type, ExprVariant value) : type{type}, value{value} {}
// Copy constructor // Copy constructor
Expr::Expr(const Expr &other) : type{other.type} { Expr::Expr(const Expr &other) : type{other.type} {
switch(type) { switch(type) {
case ExprType::BINARY: { case ExprType::BINARY: {
std::shared_ptr<Binary> ptr = std::get<std::shared_ptr<Binary>>(other.value); std::shared_ptr<_Binary> ptr = std::get<std::shared_ptr<_Binary>>(other.value);
value = std::make_shared<Binary>(*ptr); value = std::make_shared<_Binary>(*ptr);
break; break;
} }
case ExprType::GROUPING: { case ExprType::GROUPING: {
std::shared_ptr<Grouping> ptr = std::get<std::shared_ptr<Grouping>>(other.value); std::shared_ptr<_Grouping> ptr = std::get<std::shared_ptr<_Grouping>>(other.value);
value = std::make_shared<Grouping>(*ptr); value = std::make_shared<_Grouping>(*ptr);
break; break;
} }
case ExprType::LITERAL: { case ExprType::LITERAL: {
std::shared_ptr<Literal> ptr = std::get<std::shared_ptr<Literal>>(other.value); std::shared_ptr<_Literal> ptr = std::get<std::shared_ptr<_Literal>>(other.value);
value = std::make_shared<Literal>(*ptr); value = std::make_shared<_Literal>(*ptr);
break; break;
} }
case ExprType::UNARY: { case ExprType::UNARY: {
std::shared_ptr<Unary> ptr = std::get<std::shared_ptr<Unary>>(other.value); std::shared_ptr<_Unary> ptr = std::get<std::shared_ptr<_Unary>>(other.value);
value = std::make_shared<Unary>(*ptr); value = std::make_shared<_Unary>(*ptr);
break; break;
} }
} }
} }
// Move constructor // Move constructor
Expr::Expr(const Expr &&other) : type{other.type}, value{std::move(other.value)} {} Expr::Expr(const Expr &&other) : type{other.type}, value{std::move(other.value)} {
switch(type) {
// Binary expressoin
Expr::Expr(const Expr &left, Token op, const Expr &right)
: type{ExprType::BINARY}, value{std::make_shared<Binary>(left, op, right)} {}
// Literal expression
Expr::Expr(Object value)
: type{ExprType::LITERAL}, value{std::make_shared<Literal>(value)} {}
// Unary expressoin
Expr::Expr(Token op, const Expr &right)
: type{ExprType::UNARY}, value{std::make_shared<Unary>(op, right)} {}
// Group expression
// Has to take an extra parameter to avoid conflicting with copy constructor
Expr::Expr(const Expr &expr, void *ptr)
: type{ExprType::GROUPING}, value{std::make_shared<Grouping>(expr)} { (void)ptr; }
std::string AstPrinter::print(const Expr &expr) {
switch (expr.type) {
case ExprType::BINARY: { case ExprType::BINARY: {
std::shared_ptr<Binary> binary = std::get<std::shared_ptr<Binary>>(expr.value); std::shared_ptr<_Binary> ptr = std::get<std::shared_ptr<_Binary>>(other.value);
assert(binary != nullptr); ptr.reset();
return parenthesize(binary->op.lexeme, {&binary->left, &binary->right}); break;
} }
case ExprType::GROUPING: { case ExprType::GROUPING: {
std::shared_ptr<Grouping> group = std::get<std::shared_ptr<Grouping>>(expr.value); std::shared_ptr<_Grouping> ptr = std::get<std::shared_ptr<_Grouping>>(other.value);
assert(group != nullptr); ptr.reset();
return parenthesize("group", {&group->expr}); break;
} }
case ExprType::LITERAL: { case ExprType::LITERAL: {
std::shared_ptr<Literal> literal = std::get<std::shared_ptr<Literal>>(expr.value); std::shared_ptr<_Literal> ptr = std::get<std::shared_ptr<_Literal>>(other.value);
assert(literal != nullptr); ptr.reset();
switch (literal->value.type) { break;
case ObjectType::NIL:
return "nil";
case ObjectType::IDENTIFIER:
case ObjectType::STRING_LIT: {
std::string value = std::get<std::string>(literal->value.value);
return value;
}
case ObjectType::NUMBER: {
double value = std::get<double>(literal->value.value);
return std::to_string(value);
}
}
} }
case ExprType::UNARY: { case ExprType::UNARY: {
std::shared_ptr<Unary> unary = std::get<std::shared_ptr<Unary>>(expr.value); std::shared_ptr<_Unary> ptr = std::get<std::shared_ptr<_Unary>>(other.value);
assert(unary != nullptr); ptr.reset();
return parenthesize(unary->op.lexeme, {&unary->right}); break;
} }
} }
} }
std::string AstPrinter::parenthesize(const std::string &name, std::vector<const Expr *> exprs) { // Copy assignment
std::stringstream ss{}; Expr &Expr::operator=(const Expr &other) {
ss << '(' << name; if (this == &other) {
for (const Expr *expr : exprs) { return *this;
ss << ' ' << print(*expr);
} }
ss << ')';
return ss.str(); switch(type) {
case ExprType::BINARY: {
std::shared_ptr<_Binary> ptr = std::get<std::shared_ptr<_Binary>>(value);
ptr.reset();
break;
}
case ExprType::GROUPING: {
std::shared_ptr<_Grouping> ptr = std::get<std::shared_ptr<_Grouping>>(value);
ptr.reset();
break;
}
case ExprType::LITERAL: {
std::shared_ptr<_Literal> ptr = std::get<std::shared_ptr<_Literal>>(value);
ptr.reset();
break;
}
case ExprType::UNARY: {
std::shared_ptr<_Unary> ptr = std::get<std::shared_ptr<_Unary>>(value);
ptr.reset();
break;
}
}
type = other.type;
switch(type) {
case ExprType::BINARY: {
std::shared_ptr<_Binary> ptr = std::get<std::shared_ptr<_Binary>>(other.value);
value = std::make_shared<_Binary>(*ptr);
break;
}
case ExprType::GROUPING: {
std::shared_ptr<_Grouping> ptr = std::get<std::shared_ptr<_Grouping>>(other.value);
value = std::make_shared<_Grouping>(*ptr);
break;
}
case ExprType::LITERAL: {
std::shared_ptr<_Literal> ptr = std::get<std::shared_ptr<_Literal>>(other.value);
value = std::make_shared<_Literal>(*ptr);
break;
}
case ExprType::UNARY: {
std::shared_ptr<_Unary> ptr = std::get<std::shared_ptr<_Unary>>(other.value);
value = std::make_shared<_Unary>(*ptr);
break;
}
}
return *this;
}
// Move assignment
Expr &Expr::operator=(const Expr &&other) {
if (this == &other) {
return *this;
}
switch(type) {
case ExprType::BINARY: {
std::shared_ptr<_Binary> ptr = std::get<std::shared_ptr<_Binary>>(value);
ptr.reset();
break;
}
case ExprType::GROUPING: {
std::shared_ptr<_Grouping> ptr = std::get<std::shared_ptr<_Grouping>>(value);
ptr.reset();
break;
}
case ExprType::LITERAL: {
std::shared_ptr<_Literal> ptr = std::get<std::shared_ptr<_Literal>>(value);
ptr.reset();
break;
}
case ExprType::UNARY: {
std::shared_ptr<_Unary> ptr = std::get<std::shared_ptr<_Unary>>(value);
ptr.reset();
break;
}
}
type = other.type;
switch(type) {
case ExprType::BINARY: {
std::shared_ptr<_Binary> optr = std::get<std::shared_ptr<_Binary>>(other.value);
value = std::move(other.value);
optr.reset();
break;
}
case ExprType::GROUPING: {
std::shared_ptr<_Grouping> optr = std::get<std::shared_ptr<_Grouping>>(other.value);
value = std::move(other.value);
optr.reset();
break;
}
case ExprType::LITERAL: {
std::shared_ptr<_Literal> optr = std::get<std::shared_ptr<_Literal>>(other.value);
value = std::move(other.value);
optr.reset();
break;
}
case ExprType::UNARY: {
std::shared_ptr<_Unary> optr = std::get<std::shared_ptr<_Unary>>(other.value);
value = std::move(other.value);
optr.reset();
break;
}
}
return *this;
} }

View File

@ -3,14 +3,13 @@
#include "../tokenizer.hh" #include "../tokenizer.hh"
#include <memory> #include <memory>
#include <string>
#include <variant> #include <variant>
struct Expr; struct Expr;
struct Binary; struct _Binary;
struct Grouping; struct _Grouping;
struct Literal; struct _Literal;
struct Unary; struct _Unary;
enum class ExprType { enum class ExprType {
BINARY, BINARY,
@ -20,64 +19,57 @@ enum class ExprType {
}; };
using ExprVariant = std::variant< using ExprVariant = std::variant<
std::shared_ptr<Binary>, std::shared_ptr<_Binary>,
std::shared_ptr<Grouping>, std::shared_ptr<_Grouping>,
std::shared_ptr<Literal>, std::shared_ptr<_Literal>,
std::shared_ptr<Unary> std::shared_ptr<_Unary>
>; >;
#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)})
struct Expr { struct Expr {
Expr(ExprType type, ExprVariant value);
// Copy constructor // Copy constructor
Expr(const Expr &other); Expr(const Expr &other);
// Move constructor // Move constructor
Expr(const Expr &&other); Expr(const Expr &&other);
// Binary expressoin // Copy assignment
Expr(const Expr &left, Token op, const Expr &right); Expr &operator=(const Expr &other);
// Literal expression // Move assignment
Expr(Object value); Expr &operator=(const Expr &&other);
// Unary expressoin
Expr(Token op, const Expr &right);
// Group expression
// Has to take an extra parameter to avoid conflicting with copy constructor
Expr(const Expr &expr, void *ptr);
ExprType type; ExprType type;
ExprVariant value; ExprVariant value;
}; };
struct Binary { struct _Binary {
Binary(const Expr &left, Token op, const Expr &right) : left{left}, op{op}, right{right} {} _Binary(const Expr &left, Token op, const Expr &right) : left{left}, op{op}, right{right} {}
Expr left; Expr left;
Token op; Token op;
Expr right; Expr right;
}; };
struct Grouping { struct _Grouping {
Grouping(const Expr &expr) : expr{expr} {} _Grouping(const Expr &expr) : expr{expr} {}
Expr expr; Expr expr;
}; };
struct Literal { struct _Literal {
Literal(Object value) : value{value} {} _Literal(Object value) : value{value} {}
Object value; Object value;
}; };
struct Unary { struct _Unary {
Unary(Token op, const Expr &right) : op{op}, right{right} {} _Unary(Token op, const Expr &right) : op{op}, right{right} {}
Token op; Token op;
Expr right; Expr right;
}; };
class AstPrinter {
public:
std::string print(const Expr &expr);
private:
std::string parenthesize(const std::string &name, std::vector<const Expr *> exprs);
};
#endif #endif

View File

@ -0,0 +1,53 @@
#include "printer.hh"
#include "expr.hh"
#include <cassert>
#include <string>
#include <sstream>
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});
}
case ExprType::GROUPING: {
std::shared_ptr<_Grouping> group = std::get<std::shared_ptr<_Grouping>>(expr.value);
assert(group != nullptr);
return parenthesize("group", {&group->expr});
}
case ExprType::LITERAL: {
std::shared_ptr<_Literal> literal = std::get<std::shared_ptr<_Literal>>(expr.value);
assert(literal != nullptr);
switch (literal->value.type) {
case ObjectType::NIL:
return "nil";
case ObjectType::IDENTIFIER:
case ObjectType::STRING_LIT: {
std::string value = std::get<std::string>(literal->value.value);
return value;
}
case ObjectType::NUMBER: {
double value = std::get<double>(literal->value.value);
return std::to_string(value);
}
}
}
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::string AstPrinter::parenthesize(const std::string &name, std::vector<const Expr *> exprs) {
std::stringstream ss{};
ss << '(' << name;
for (const Expr *expr : exprs) {
ss << ' ' << print(*expr);
}
ss << ')';
return ss.str();
}

View File

@ -0,0 +1,14 @@
#ifndef PRINTER_HH
#define PRINTER_HH
#include "expr.hh"
#include <string>
class AstPrinter {
public:
std::string print(const Expr &expr);
private:
std::string parenthesize(const std::string &name, std::vector<const Expr *> exprs);
};
#endif