Program Listing for File ASTNode.h¶
↰ Return to documentation for file (src/ASTNode.h)
#include <utility>
//
// Created by Michal Young on 9/12/18.
//
#ifndef ASTNODE_H
#define ASTNODE_H
#include <assert.h>
#include <string>
#include <sstream>
#include <vector>
#include <iostream>
#include <fstream>
#include <iomanip>
#include "keywords.h"
#include "initialized_list.h"
#include "exceptions.h"
#include "symbol_table.h"
#include "compiler_utils.h"
#include "code_gen_utils.h"
#define NO_RETURN_VAR ""
#define PRINT_INDENT(a) (settings.fout_ << AST::ASTNode::indent_str(a))
#define PADDING_WIDTH 4
// Forward declaration
namespace Quack { class Class; }
namespace AST {
// Abstract syntax tree. ASTNode is abstract base class for all other nodes.
// This is not the final AST for Quack ... it's a quick-and-dirty version
// that I copied over from the calculator example, and it isn't even the
// final version from the calculator example.
struct ASTNode {
virtual ~ASTNode() = default;
virtual void print_original_src(unsigned int indent_depth = 0) = 0;
virtual bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) = 0;
virtual void update_initialized_list(InitializedList &inits, bool is_constructor) {}
virtual bool perform_type_inference(TypeCheck::Settings &settings,
Quack::Class * parent_type) = 0;
virtual bool update_inferred_type(TypeCheck::Settings &settings, Quack::Class *inferred_type,
bool is_field) {
std::string msg = "Unpredicted node type encountered in inferred type update";
throw TypeCheckerException("Unexpected", msg);
}
inline void set_node_type(Quack::Class * new_type) { type_ = new_type; }
inline Quack::Class* get_node_type() const { return type_; }
virtual std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const = 0;
void generate_eval_branch(CodeGen::Settings settings, const unsigned indent_lvl,
const std::string &true_label, const std::string &false_label);
static std::string indent_str(unsigned indent_level) {
return std::string(indent_level, '\t');
}
static const std::string define_new_label(const std::string &label_header) {
std::ostringstream ss;
ss << label_header << std::setfill('0') << std::setw(PADDING_WIDTH) << label_cnt_++;
return ss.str();
}
static void generate_label(CodeGen::Settings &settings, unsigned indent_lvl,
const std::string &label, bool add_new_line=false) {
PRINT_INDENT(indent_lvl);
settings.fout_ << label << ": ; /* Null statement */";
if (add_new_line)
settings.fout_ << "\n";
}
static void generate_goto(CodeGen::Settings &settings, unsigned indent_lvl,
const std::string &label, bool add_new_line=false) {
PRINT_INDENT(indent_lvl);
settings.fout_ << "goto " << label << ";";
if (add_new_line)
settings.fout_ << "\n";
}
static const std::string define_new_temp_var() {
std::ostringstream ss;
ss << TEMP_VAR_HEADER << std::setfill('0') << std::setw(PADDING_WIDTH) << var_cnt_++;
return ss.str();
}
std::string generate_temp_var(const std::string &var_to_store, CodeGen::Settings settings,
unsigned indent_lvl, bool is_lhs) const;
static void generate_one_line_comment(CodeGen::Settings settings, const unsigned indent_lvl,
const std::string &msg) {
PRINT_INDENT(indent_lvl);
settings.fout_ << "/* " << msg << " */\n";
}
virtual bool contains_return_all_paths() { return false; }
protected:
Quack::Class * type_ = nullptr;
static unsigned long label_cnt_;
static unsigned long var_cnt_;
};
/* A block is a sequence of statements or expressions.
* For simplicity we'll just make it a sequence of ASTNode,
* and leave it to the parser to build valid structures.
*/
class Block {
public:
Block() : stmts_{std::vector<ASTNode *>()} {}
~Block() {
for (const auto &ptr : stmts_)
delete ptr;
}
void append(ASTNode *stmt) { stmts_.push_back(stmt); }
void print_original_src(unsigned int indent_depth) {
bool is_first = true;
std::string indent_str = std::string(indent_depth, '\t');
for (const auto &stmt : stmts_) {
if (!is_first)
std::cout << "\n";
is_first = false;
std::cout << indent_str;
stmt->print_original_src(indent_depth);
std::cout << ";";
}
}
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) {
bool success = true;
for (auto &stmt : stmts_)
success = success && stmt->check_initialize_before_use(inits, all_inits, is_method);
return success;
}
bool perform_type_inference(TypeCheck::Settings &settings) {
for (auto * stmt : stmts_)
stmt->perform_type_inference(settings, nullptr);
return true;
}
void generate_code(CodeGen::Settings &settings, unsigned indent_lvl = 0) {
std::string indent_str = AST::ASTNode::indent_str(indent_lvl);
for (auto * stmt : stmts_)
stmt->generate_code(settings, indent_lvl + 1, false);
}
bool contains_return_all_paths() {
for (auto * stmt : stmts_)
if (stmt->contains_return_all_paths())
return true;
return false;
}
bool empty() { return stmts_.empty(); }
private:
std::vector<ASTNode *> stmts_;
};
class If : public ASTNode {
public:
explicit If(ASTNode *cond, Block* truepart, Block* falsepart) :
cond_{cond}, truepart_{truepart}, falsepart_{falsepart} {};
~If() {
delete cond_;
delete truepart_;
delete falsepart_;
}
void print_original_src(unsigned int indent_depth = 0) override {
std::string indent_str = std::string(indent_depth, '\t');
std::cout << "if ";
cond_->print_original_src();
std::cout << " {\n";
truepart_->print_original_src(indent_depth + 1);
std::cout << (!truepart_->empty() ? "\n" : "") << indent_str << "}";
if (!falsepart_->empty()) {
std::cout << " else {\n";
falsepart_->print_original_src(indent_depth + 1);
std::cout << "\n" << indent_str << "}";
}
}
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
InitializedList false_lits = inits;
bool success = cond_->check_initialize_before_use(inits, all_inits, is_method)
&& truepart_->check_initialize_before_use(inits, all_inits, is_method)
&& falsepart_->check_initialize_before_use(false_lits, all_inits, is_method);
// This is used in the constructor to make sure fields initialized on all paths
if (all_inits != nullptr) {
*all_inits = inits;
all_inits->var_union(false_lits);
}
inits.var_intersect(false_lits);
return success;
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override {
if (is_lhs)
throw std::runtime_error("LHS is not possible in IF block");
std::string if_label = define_new_label("if");
std::string else_label = define_new_label("else");
std::string end_if_label = define_new_label("end_if");
cond_->generate_eval_branch(settings, indent_lvl, if_label, else_label);
generate_one_line_comment(settings, indent_lvl, "True Part If");
generate_label(settings, indent_lvl, if_label, true);
truepart_->generate_code(settings, indent_lvl + 1);
generate_goto(settings, indent_lvl, end_if_label, true);
generate_one_line_comment(settings, indent_lvl, "False Part If");
generate_label(settings, indent_lvl, else_label, true);
if (falsepart_)
falsepart_->generate_code(settings, indent_lvl + 1);
generate_one_line_comment(settings, indent_lvl, "End If");
generate_label(settings, indent_lvl, end_if_label, true);
return NO_RETURN_VAR;
}
bool contains_return_all_paths() override {
return truepart_->contains_return_all_paths() && falsepart_->contains_return_all_paths();
}
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
private:
ASTNode *cond_; // The boolean expression to be evaluated
Block *truepart_; // Execute this block if the condition is true
Block *falsepart_; // Execute this block if the condition is false
};
/* Identifiers like x and literals like 42 are the
* leaves of the AST. A literal can only be evaluated
* for value_ (the 'eval' method), but an identifier
* can also be evaluated for location (when we want to
* store something in it).
*/
struct Ident : public ASTNode {
explicit Ident(const char* txt) : text_{txt} {}
void print_original_src(unsigned int indent_depth = 0) override { std::cout << text_; }
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
return check_ident_initialized(inits, all_inits, false);
}
bool check_ident_initialized(InitializedList &inits, InitializedList *all_inits,
bool is_field=false) {
if (text_ == OBJECT_SELF)
return true;
if (!inits.exists(text_, is_field)) {
throw UnitializedVarException(typeid(this).name(), text_, is_field);
// return false;
}
return true;
}
void update_initialized_list(InitializedList &inits, bool is_constructor) override {
add_identifier_to_initialized(inits, false);
}
void add_identifier_to_initialized(InitializedList &inits, bool is_field) {
inits.add(text_, is_field);
}
bool update_inferred_type(TypeCheck::Settings &settings, Quack::Class *inferred_type,
bool is_field) override;
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl ,
bool is_lhs) const override {
return text_;
}
const std::string text_;
};
template <typename _T>
struct Literal : public ASTNode {
explicit Literal(const _T &v) : value_{v} {}
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
return true;
}
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override{
throw AmbiguousInferenceException(typeid(this).name(), "Not able to infer type for literal");
}
const _T value_;
protected:
std::string generate_lit_code(CodeGen::Settings &settings, unsigned indent_lvl,
const std::string &gen_func_name_) const;
};
struct IntLit : public Literal<int>{
explicit IntLit(int v) : Literal(v) {};
void print_original_src(unsigned int indent_depth = 0) override {
std::cout << std::to_string(value_);
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override {
return generate_lit_code(settings, indent_lvl, GENERATE_LIT_INT_FUNC);
}
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
};
struct BoolLit : public Literal<bool>{
explicit BoolLit(bool v) : Literal(v) {};
void print_original_src(unsigned int indent_depth = 0) override {
std::cout << (value_ ? "true" : "false");
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override {
if (value_)
return GENERATED_LIT_TRUE;
return GENERATED_LIT_FALSE;
}
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
};
struct NothingLit : public Literal<std::string>{
NothingLit() : Literal<std::string>("") {};
void print_original_src(unsigned int indent_depth = 0) override {
std::cout << GENERATED_LIT_NONE;
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override {
return GENERATED_LIT_NONE;
}
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
};
struct StrLit : public Literal<std::string> {
explicit StrLit(const char* v) : Literal<std::string>(std::string(v)) {}
void print_original_src(unsigned int indent_depth = 0) override {
std::cout << "\"" << value_ << "\"";
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override {
std::ostringstream ss;
ss << GENERATE_LIT_STRING_FUNC << "(\"" << value_ << "\")";
return generate_temp_var(ss.str(), settings, indent_lvl, false);
}
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
};
struct Return : public ASTNode {
ASTNode* right_;
explicit Return(ASTNode* right) : right_(right) {}
~Return() {
delete right_;
}
void print_original_src(unsigned int indent_depth = 0) override {
std::cout << "return ";
right_->print_original_src();
}
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
return right_->check_initialize_before_use(inits, all_inits, is_method);
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override;
bool contains_return_all_paths() override { return true; }
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
};
struct While : public ASTNode {
ASTNode* cond_;
Block* body_;
explicit While(ASTNode* cond, Block* body) : cond_(cond), body_(body) {};
~While() {
delete cond_;
delete body_;
}
void print_original_src(unsigned int indent_depth = 0) override {
std::string indent_str = std::string(indent_depth, '\t');
std::cout << "While ";
cond_->print_original_src();
std::cout << " {\n";
body_->print_original_src(indent_depth + 1);
if (!body_->empty())
std::cout << "\n";
std::cout << indent_str << "}";
}
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
bool success = cond_->check_initialize_before_use(inits, all_inits, is_method);
InitializedList body_temp_list = inits;
success = success && body_->check_initialize_before_use(body_temp_list, all_inits, is_method);
if (all_inits != nullptr)
all_inits->var_union(body_temp_list);
return success;
}
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override{
if (is_lhs)
throw std::runtime_error("While loop cannot be on LHS");
std::string test_cond_label = define_new_label("test_cond");
std::string loop_again_label = define_new_label("loop_again");
std::string end_while_label = define_new_label("end_while");
generate_one_line_comment(settings, indent_lvl, "WHILE Loop Start");
generate_goto(settings, indent_lvl, test_cond_label, true);
generate_label(settings, indent_lvl, loop_again_label, true);
// Body of the loop is a simple block
body_->generate_code(settings, indent_lvl + 1);
generate_label(settings, indent_lvl, test_cond_label, true);
// Checks while condition
cond_->generate_eval_branch(settings, indent_lvl, loop_again_label, end_while_label);
generate_label(settings, indent_lvl, end_while_label, true);
// Comment for clarity. Delete if cluttering
generate_one_line_comment(settings, indent_lvl, "END WHILE Loop");
return NO_RETURN_VAR;
}
};
struct RhsArgs : public ASTNode {
std::vector<ASTNode*> args_;
RhsArgs() {}
~RhsArgs() {
for (const auto &arg: args_)
delete arg;
}
unsigned long count() { return args_.size(); }
// /**
// * Checks whether there are any input arguments
// * @return True if there are no arguments
// */
// bool empty() { return args_.empty(); }
void add(ASTNode* new_node) { args_.emplace_back(new_node); }
void print_original_src(unsigned int indent_depth) override {
bool is_first = true;
for (const auto &arg : args_) {
if (!is_first)
std::cout << ", ";
is_first = false;
arg->print_original_src(indent_depth);
}
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override {
throw std::runtime_error("Cannot generate RHS args similar to normal args");
}
std::vector<std::string>* generate_args(CodeGen::Settings &settings,
unsigned indent_lvl) const {
std::vector<std::string> * gen_args = new std::vector<std::string>();
for (auto * arg: args_) {
if (auto arg_cast = dynamic_cast<Ident *>(arg)) {
gen_args->emplace_back(arg_cast->text_);
} else {
// Cannot have ARGS on LHS even if incoming is LHS
std::string temp_var = arg->generate_code(settings, indent_lvl, false);
gen_args->emplace_back(temp_var);
}
}
return gen_args;
}
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override{
std::string msg = "Type inference not valid for right hand side args";
throw TypeInferenceException("UnexpectedStateReached", msg);
}
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
bool success = true;
for (auto &arg : args_)
success = success && arg->check_initialize_before_use(inits, all_inits, is_method);
return success;
}
};
struct FunctionCall : public ASTNode {
const std::string ident_;
RhsArgs* args_;
FunctionCall(const char* ident, RhsArgs* args) : ident_(ident), args_(args) {}
~FunctionCall() {
delete args_;
}
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
return args_->check_initialize_before_use(inits, all_inits, is_method);
}
void print_original_src(unsigned int indent_depth) override {
std::cout << ident_ << "(";
args_->print_original_src(indent_depth);
std::cout << ")";
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override;
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
std::string generate_object_call(Quack::Class * obj_type, std::string object_name,
CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const;
};
struct ObjectCall : public ASTNode {
ASTNode* object_;
ASTNode* next_;
ObjectCall(ASTNode* object, ASTNode* next) : object_(object), next_(next) {}
~ObjectCall() {
delete object_;
delete next_;
}
void print_original_src(unsigned int indent_depth) override {
object_->print_original_src(indent_depth);
std::cout << ".";
next_->print_original_src(indent_depth);
}
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
if (auto obj = dynamic_cast<Ident*>(object_)) {
if (all_inits != nullptr && obj->text_ == OBJECT_SELF) {
if (auto next = dynamic_cast<Ident *>(next_))
return next->check_ident_initialized(inits, all_inits, true);
} else if(is_method && obj->text_ == OBJECT_SELF) {
// No need to check fields for this class outside the constructor
return true;
}
}
bool success = object_->check_initialize_before_use(inits, all_inits, is_method);
if (dynamic_cast<Ident*>(next_) != nullptr)
return success;
return success && next_->check_initialize_before_use(inits, all_inits, is_method);
}
void update_initialized_list(InitializedList &inits, bool is_field) override {
if (auto obj = dynamic_cast<Ident*>(object_))
if (is_field && obj->text_ == OBJECT_SELF)
if (auto next = dynamic_cast<Ident*>(next_))
next->add_identifier_to_initialized(inits, true);
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override {
// Handle the bottom out of the recursion
if (auto obj = dynamic_cast<Ident*>(object_))
return process_object_call(obj->text_, settings, indent_lvl, is_lhs);
std::string left_obj = object_->generate_code(settings, indent_lvl, is_lhs);
return process_object_call(left_obj, settings, indent_lvl, is_lhs);
}
const std::string process_object_call(const std::string &left_obj, CodeGen::Settings &settings,
unsigned indent_lvl, bool is_lhs) const;
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
bool update_inferred_type(TypeCheck::Settings &settings, Quack::Class *inferred_type,
bool is_field) override;
};
struct BinOp : public ASTNode {
std::string opsym;
ASTNode *left_;
ASTNode *right_;
BinOp(const std::string &sym, ASTNode *l, ASTNode *r) : opsym{sym}, left_{l}, right_{r} {};
~BinOp() {
delete left_;
delete right_;
}
void print_original_src(unsigned int indent_depth = 0) override {
std::cout << "(";
left_->print_original_src();
std::cout << " " << opsym << " ";
right_->print_original_src();
std::cout << ")";
}
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
return left_->check_initialize_before_use(inits, all_inits, is_method)
&& (opsym == UNARY_OP_NOT
|| (right_ && right_->check_initialize_before_use(inits, all_inits, is_method)));
}
static const std::string op_lookup(const std::string &op) {
if (op == "+")
return METHOD_ADD;
else if (op == "-")
return METHOD_SUBTRACT;
else if (op == "*")
return METHOD_MULTIPLY;
else if (op == "/")
return METHOD_DIVIDE;
else if (op == ">=")
return METHOD_GEQ;
else if (op == ">")
return METHOD_GT;
else if (op == "<=")
return METHOD_LEQ;
else if (op == "<")
return METHOD_LT;
else if (op == "==")
return METHOD_EQUALITY;
throw UnknownBinOpException(op);
}
virtual std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override {
if (is_lhs)
throw std::runtime_error("Boolean operator cannot be on LHS");
// Create the ObjectCall stand-in AST node
RhsArgs args;
args.add(right_);
args.args_[0]->set_node_type(right_->get_node_type());
FunctionCall func_call(op_lookup(opsym).c_str(), &args);
func_call.set_node_type(this->type_);
ObjectCall obj_call(left_, &func_call);
obj_call.set_node_type(this->type_);
std::string obj_out = obj_call.generate_code(settings, indent_lvl, is_lhs);
// Clean up the memory to prevent deleting memory accidentally
args.args_[0] = nullptr;
obj_call.object_ = nullptr;
obj_call.next_ = nullptr;
func_call.args_ = nullptr;
// No deletion needed. Relies on the destructor of ObjectCall which is on the stack
return obj_out;
}
virtual bool perform_type_inference(TypeCheck::Settings &settings,
Quack::Class * parent_type) override;
};
struct BoolOp : public BinOp {
BoolOp(const std::string &sym, ASTNode *l, ASTNode *r) : BinOp(sym, l, r) {};
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override {
if (is_lhs)
throw std::runtime_error("BoolOp cannot be a left hand side");
if (opsym == UNARY_OP_NOT) {
generate_one_line_comment(settings, indent_lvl, "NOT Start");
std::string op_var = left_->generate_code(settings, indent_lvl, is_lhs);
std::string gen_var = "(" + op_var + " == " + GENERATED_LIT_FALSE + ")";
return generate_temp_var(gen_var, settings, indent_lvl, false);
}
// Variable that will store the evaluated result
std::string eval_bool = generate_temp_var(GENERATED_LIT_FALSE, settings, indent_lvl, false);
// Labels for jumping
std::string bool_halfway = define_new_label(opsym + "_HALFWAY");
std::string bool_true = define_new_label(opsym + "_TRUE");
std::string bool_end = define_new_label(opsym + "_END");
// Left Side of Boolean
generate_one_line_comment(settings, indent_lvl, opsym + " Left Condition");
if (opsym == METHOD_AND) {
left_->generate_eval_branch(settings, indent_lvl + 1, bool_halfway, bool_end);
} else if (opsym == METHOD_OR) {
left_->generate_eval_branch(settings, indent_lvl + 1, bool_true, bool_halfway);
} else {
throw std::runtime_error("Unknown Boolean operator \"" + opsym + "\"");
}
generate_label(settings, indent_lvl, bool_halfway);
generate_one_line_comment(settings, indent_lvl, opsym + " Right Condition");
right_->generate_eval_branch(settings, indent_lvl + 1, bool_true, bool_end);
// Short Circuit True
generate_one_line_comment(settings, indent_lvl, "Boolean Get True");
generate_label(settings, indent_lvl, bool_true, true);
PRINT_INDENT(indent_lvl);
settings.fout_ << eval_bool << " = " << GENERATED_LIT_TRUE << ";\n";
// End Boolean
generate_label(settings, indent_lvl, bool_end, true);
generate_one_line_comment(settings, indent_lvl, opsym + " End");
return eval_bool;
}
void generate_eval_bool_op(CodeGen::Settings settings, const unsigned indent_lvl,
const std::string &true_label, const std::string &false_label) {
if (opsym == UNARY_OP_NOT) {
return left_->generate_eval_branch(settings, indent_lvl, false_label, true_label);
}
std::string halfway_label = define_new_label("halfway");
if (opsym == METHOD_AND) {
generate_one_line_comment(settings, indent_lvl, "Generate AND");
left_->generate_eval_branch(settings, indent_lvl + 1, halfway_label, false_label);
generate_label(settings, indent_lvl, halfway_label, true);
} else if (opsym == METHOD_OR) {
generate_one_line_comment(settings, indent_lvl, "Generate OR");
left_->generate_eval_branch(settings, indent_lvl + 1, true_label, halfway_label);
generate_label(settings, indent_lvl, halfway_label, true);
} else {
throw std::runtime_error("Unknown Boolean operator " + opsym);
}
right_->generate_eval_branch(settings, indent_lvl + 1, true_label, false_label);
}
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class *parent_type) override;
};
struct UniOp : public ASTNode {
std::string opsym;
ASTNode *right_;
UniOp(const std::string &sym, ASTNode *r) : opsym{std::move(sym)}, right_{r} {};
~UniOp() { delete right_; }
void print_original_src(unsigned int indent_depth = 0) override {
std::cout << "(" << opsym << " ";
right_->print_original_src();
std::cout << ")";
}
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
return right_->check_initialize_before_use(inits, all_inits, is_method);
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override;
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
};
struct Typing : public ASTNode {
Typing(ASTNode* expr, const std::string &type_name) : expr_(expr), type_name_(type_name) {}
~Typing() {
delete expr_;
}
ASTNode* expr_;
std::string type_name_;
void print_original_src(unsigned int indent_depth) override {
expr_->print_original_src(indent_depth);
if (!type_name_.empty())
std::cout << " : " << type_name_;
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override;
// /**
// * Helper function used to check if the specified type name actually exists.
// *
// * @param type_name Name of the type used
// * @return True if the type_name_ is a valid class.
// */
// bool check_type_name_exists(const std::string &type_name) const;
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
// if (!check_type_name_exists(type_name_))
// return false;
return expr_->check_initialize_before_use(inits, all_inits, is_method);
}
void update_initialized_list(InitializedList &inits, bool is_constructor) override {
expr_->update_initialized_list(inits, is_constructor);
}
bool update_inferred_type(TypeCheck::Settings &settings, Quack::Class *inferred_type,
bool is_field) override;
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override{
configure_initial_typing(BASE_CLASS);
bool success = expr_->perform_type_inference(settings, parent_type);
return success && verify_typing();
}
private:
bool configure_initial_typing(Quack::Class* other_type);
bool verify_typing();
};
class TypeAlternative {
public:
TypeAlternative(const std::string &t1, const std::string &t2, Block* block)
: type_names_{t1,t2}, block_(block) {}
~TypeAlternative() { delete block_; }
void print_original_src(unsigned int indent_depth) {
std::cout << type_names_[0] << " : " << type_names_[1] << " {\n";
block_->print_original_src(indent_depth + 1);
std::cout << "\n" << std::string(indent_depth, '\t') << "}";
}
std::string type_names_[2];
Block* block_;
};
struct Assn : public ASTNode {
Typing* lhs_;
ASTNode* rhs_;
Assn(Typing* lhs, ASTNode* rhs) : lhs_(lhs), rhs_(rhs) {};
~Assn() {
delete lhs_;
delete rhs_;
}
void print_original_src(unsigned int indent_depth = 0) override {
lhs_->print_original_src(indent_depth);
std::cout << " = ";
rhs_->print_original_src(indent_depth);
}
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
bool success = rhs_->check_initialize_before_use(inits, all_inits, is_method);
if (!success)
return false;
bool is_constructor = (all_inits != nullptr);
lhs_->update_initialized_list(inits, is_constructor);
if (is_constructor)
all_inits->var_union(inits);
return lhs_->check_initialize_before_use(inits, all_inits, is_method);
}
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override{
bool success = rhs_->perform_type_inference(settings, nullptr);
success = success && lhs_->update_inferred_type(settings, rhs_->get_node_type(), false);
success = success && lhs_->perform_type_inference(settings, nullptr);
return success;
}
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override;
};
struct Typecase : public ASTNode {
Typecase(ASTNode* expr, std::vector<TypeAlternative*>* alts) : expr_(expr), alts_(alts) {}
~Typecase() {
delete expr_;
for (const auto &alt : *alts_)
delete alt;
delete alts_;
}
void print_original_src(unsigned int indent_depth) override {
std::string indent_str = std::string(indent_depth, '\t');
std::cout << KEY_TYPECASE << " ";
expr_->print_original_src(0);
std::cout << " {\n";
bool is_first = true;
// Print the alternate blocks
for (const auto &alt : *alts_) {
if (!is_first)
std::cout << "\n";
is_first = false;
std::cout << indent_str << "\t";
alt->print_original_src(indent_depth + 1);
}
if (!alts_->empty())
std::cout << "\n";
std::cout << indent_str << "}";
}
bool perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) override;
bool check_initialize_before_use(InitializedList &inits, InitializedList *all_inits,
bool is_method) override {
bool success = expr_->check_initialize_before_use(inits, all_inits, false);
all_inits->var_union(inits);
for (unsigned i = 0; i < alts_->size(); i++) {
TypeAlternative * alt = (*alts_)[i];
InitializedList type_init(inits);
// Add the typed object
type_init.add(alt->type_names_[0], false);
success = success && alt->block_->check_initialize_before_use(type_init, all_inits, false);
// Update the initialized variable lists.
if (i == 0)
inits = type_init;
// Removed as no effect. Anyway not correct as may not match any typecase
// else
// inits.var_intersect(type_init);
all_inits->var_union(type_init);
}
return success;
}
bool contains_return_all_paths() override { return false; }
std::string generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const override;
private:
ASTNode* expr_;
std::vector<TypeAlternative*>* alts_;
};
}
#endif //ASTNODE_H