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);
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};
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{};
std::cout << printer.print(expr) << '\n';

View File

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

View File

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

View File

@ -2,101 +2,176 @@
#include "../tokenizer.hh"
#include <cassert>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <sstream>
Expr::Expr(ExprType type, ExprVariant value) : type{type}, value{value} {}
// Copy constructor
Expr::Expr(const Expr &other) : 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);
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);
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);
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);
std::shared_ptr<_Unary> ptr = std::get<std::shared_ptr<_Unary>>(other.value);
value = std::make_shared<_Unary>(*ptr);
break;
}
}
}
// Move constructor
Expr::Expr(const Expr &&other) : type{other.type}, value{std::move(other.value)} {}
// 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) {
Expr::Expr(const Expr &&other) : type{other.type}, value{std::move(other.value)} {
switch(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>>(other.value);
ptr.reset();
break;
}
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>>(other.value);
ptr.reset();
break;
}
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);
}
}
std::shared_ptr<_Literal> ptr = std::get<std::shared_ptr<_Literal>>(other.value);
ptr.reset();
break;
}
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>>(other.value);
ptr.reset();
break;
}
}
}
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);
// Copy assignment
Expr &Expr::operator=(const Expr &other) {
if (this == &other) {
return *this;
}
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 <memory>
#include <string>
#include <variant>
struct Expr;
struct Binary;
struct Grouping;
struct Literal;
struct Unary;
struct _Binary;
struct _Grouping;
struct _Literal;
struct _Unary;
enum class ExprType {
BINARY,
@ -20,64 +19,57 @@ enum class ExprType {
};
using ExprVariant = std::variant<
std::shared_ptr<Binary>,
std::shared_ptr<Grouping>,
std::shared_ptr<Literal>,
std::shared_ptr<Unary>
std::shared_ptr<_Binary>,
std::shared_ptr<_Grouping>,
std::shared_ptr<_Literal>,
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 {
Expr(ExprType type, ExprVariant value);
// Copy constructor
Expr(const Expr &other);
// Move constructor
Expr(const Expr &&other);
// Binary expressoin
Expr(const Expr &left, Token op, const Expr &right);
// Copy assignment
Expr &operator=(const Expr &other);
// Literal expression
Expr(Object value);
// 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);
// Move assignment
Expr &operator=(const Expr &&other);
ExprType type;
ExprVariant value;
};
struct Binary {
Binary(const Expr &left, Token op, const Expr &right) : left{left}, op{op}, right{right} {}
struct _Binary {
_Binary(const Expr &left, Token op, const Expr &right) : left{left}, op{op}, right{right} {}
Expr left;
Token op;
Expr right;
};
struct Grouping {
Grouping(const Expr &expr) : expr{expr} {}
struct _Grouping {
_Grouping(const Expr &expr) : expr{expr} {}
Expr expr;
};
struct Literal {
Literal(Object value) : value{value} {}
struct _Literal {
_Literal(Object value) : value{value} {}
Object value;
};
struct Unary {
Unary(Token op, const Expr &right) : op{op}, right{right} {}
struct _Unary {
_Unary(Token op, const Expr &right) : op{op}, right{right} {}
Token op;
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

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