Program Listing for File ASTNode.cpp¶
↰ Return to documentation for file (src/ASTNode.cpp)
//
// Created by Michal Young on 9/12/18.
//
#include <string>
#include "ASTNode.h"
#include "quack_class.h"
#include "quack_param.h"
#include "quack_method.h"
#include "exceptions.h"
#include "keywords.h"
#include "compiler_utils.h"
namespace AST {
unsigned long ASTNode::label_cnt_ = 0;
unsigned long ASTNode::var_cnt_ = 0;
std::string ASTNode::generate_temp_var(const std::string &var_to_store,
CodeGen::Settings settings,
unsigned indent_lvl, bool is_lhs) const {
std::string var_name = define_new_temp_var();
PRINT_INDENT(indent_lvl);
settings.fout_ << type_->generated_object_type_name() << " " << (is_lhs?"* ":"") << var_name
<< " = " << (is_lhs ? "&(":"") << var_to_store << (is_lhs?")":"") << ";\n";
if (!is_lhs)
return var_name;
return "(*" + var_name + ")";
}
void ASTNode::generate_eval_branch(CodeGen::Settings settings, const unsigned indent_lvl,
const std::string &true_label, const std::string &false_label){
if (auto bool_lit = dynamic_cast<BoolLit*>(this)) {
if (bool_lit->value_)
generate_goto(settings, indent_lvl, true_label);
else
generate_goto(settings, indent_lvl, false_label);
return;
}
if (auto bool_op = dynamic_cast<BoolOp*>(this))
return bool_op->generate_eval_bool_op(settings, indent_lvl, true_label, false_label);
std::string gen_var = this->generate_code(settings, indent_lvl, false);
PRINT_INDENT(indent_lvl);
settings.fout_ << "if(" GENERATED_LIT_TRUE " == " << gen_var << ") { goto "
<< true_label << "; }\n";
if (false_label != GENERATED_NO_JUMP)
generate_goto(settings, indent_lvl, false_label, true);
}
// bool Typing::check_type_name_exists(const std::string &type_name) const {
// if (!type_name.empty() && !Quack::Class::Container::singleton()->exists(type_name)) {
// throw UnknownTypeException(type_name);
// }
// return true;
// }
bool Typing::update_inferred_type(TypeCheck::Settings &settings, Quack::Class *inferred_type,
bool is_field) {
// Handle the initial typing
bool success = configure_initial_typing(inferred_type);
type_ = type_->least_common_ancestor(inferred_type);
success = success && expr_->update_inferred_type(settings, type_, is_field);
type_ = Quack::Class::least_common_ancestor(type_, expr_->get_node_type());
if (type_ == BASE_CLASS) {
std::string msg = "Unable to reconcile types " + type_name_ + " and " + inferred_type->name_;
throw TypeInferenceException("TypingError", msg);
}
success = success && verify_typing();
return success;
}
bool Typing::configure_initial_typing(Quack::Class *other_type) {
// Handle the initial typing
if (type_ == BASE_CLASS) {
if (!type_name_.empty()) {
type_ = Quack::Class::Container::singleton()->get(type_name_);
if (type_ == OBJECT_NOT_FOUND)
throw TypeInferenceException("TypingError", "Unknown type name \"" + type_name_ + "\"");
} else {
type_ = other_type;
}
}
//
// if (type_ == BASE_CLASS)
// throw TypeInferenceException("TypingError", "Unable to resolve initial type");
return true;
}
bool Typing::verify_typing() {
if (!expr_->get_node_type()->is_subtype(type_)) {
std::string msg = "Unable to cast type " + expr_->get_node_type()->name_
+ " to " + type_->name_;
throw TypeInferenceException("TypingError", msg);
}
if (type_ == BASE_CLASS)
type_ = expr_->get_node_type();
return true;
}
bool If::perform_type_inference(TypeCheck::Settings &settings, Quack::Class *) {
// cond_->set_node_type(Quack::Class::Container::Bool());
bool success = cond_->perform_type_inference(settings, nullptr);
if (cond_->get_node_type() != Quack::Class::Container::Bool())
throw TypeInferenceException("IfCondType", "If conditional not of type Bool");
success = success && truepart_->perform_type_inference(settings);
success = success && falsepart_->perform_type_inference(settings);
return success;
}
bool IntLit::perform_type_inference(TypeCheck::Settings &settings, Quack::Class *) {
set_node_type(Quack::Class::Container::Int());
return true;
}
bool BoolLit::perform_type_inference(TypeCheck::Settings &settings, Quack::Class *) {
set_node_type(Quack::Class::Container::Bool());
return true;
}
bool NothingLit::perform_type_inference(TypeCheck::Settings &settings, Quack::Class *) {
set_node_type(Quack::Class::Container::Nothing());
return true;
}
bool StrLit::perform_type_inference(TypeCheck::Settings &settings, Quack::Class *) {
set_node_type(Quack::Class::Container::Str());
return true;
}
bool While::perform_type_inference(TypeCheck::Settings &settings, Quack::Class *) {
bool success = cond_->perform_type_inference(settings, nullptr);
if (cond_->get_node_type() != Quack::Class::Container::Bool())
throw TypeInferenceException("WhileCondType", "While conditional not of type Bool");
success = success && body_->perform_type_inference(settings);
return success;
}
bool FunctionCall::perform_type_inference(TypeCheck::Settings &settings,
Quack::Class * parent_type) {
// Only need to check node type once
Quack::Method * method;
if (parent_type == nullptr) {
Quack::Class * obj_class = Quack::Class::Container::singleton()->get(ident_);
if (obj_class == OBJECT_NOT_FOUND)
throw UnknownConstructorException(ident_);
method = obj_class->get_constructor();
} else {
method = parent_type->get_method(ident_);
if (method == OBJECT_NOT_FOUND) {
std::string msg = "Unknown method \"" + ident_ + "\" in cls \"" + parent_type->name_ + "\"";
throw TypeInferenceException("MethodError", msg);
}
}
// Verify that argument count is correct
Quack::Param::Container* params = method->params_;
if (params->count() != args_->count())
throw TypeInferenceException("FunctionCall", "Wrong arg count for method " + ident_);
// Iterate through the arguments and set the
for (unsigned i = 0; i < args_->count(); i++) {
ASTNode* arg = args_->args_[i];
Quack::Param * param = (*params)[i];
if (arg->get_node_type() == BASE_CLASS)
arg->set_node_type(param->type_);
if (!arg->get_node_type()->is_subtype(param->type_))
throw TypeInferenceException("FunctionCall", "Param " + param->name_ + " type error");
arg->perform_type_inference(settings, nullptr);
}
Quack::Class * rtrn_type = method->return_type_;
if (rtrn_type != nullptr) {
if (get_node_type() == BASE_CLASS) {
set_node_type(rtrn_type);
} else {
set_node_type(rtrn_type->least_common_ancestor(get_node_type()));
if (get_node_type() == BASE_CLASS)
throw TypeInferenceException("FunctionCall", "Unable to reconcile FunctionCall return");
}
}
return true;
}
bool Return::perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) {
type_ = settings.return_type_;
if (settings.is_constructor_) {
bool success = right_->perform_type_inference(settings, parent_type);
if (right_->get_node_type() != settings.this_class_)
throw TypeInferenceException("InvalidReturn", "Constructor return must match class");
return success;
}
if ((type_ == nullptr || right_ == nullptr) && (type_ != nullptr || right_ != nullptr))
throw TypeInferenceException("ReturnNothing", "Mismatch of return " CLASS_NOTHING);
right_->perform_type_inference(settings, nullptr);
Quack::Class * right_type = right_->get_node_type();
if (right_type == nullptr)
throw TypeCheckerException("ReturnType", "Return has no type");
if (!right_type->is_subtype(type_))
throw TypeCheckerException("ReturnType", "Invalid return type \"" + right_type->name_ + "\"");
return true;
}
std::string Return::generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const {
if (is_lhs)
throw std::runtime_error("Return cannot be on left hand side");
std::string temp_var_name = right_->generate_code(settings, indent_lvl, is_lhs);
PRINT_INDENT(indent_lvl);
settings.fout_ << "return "
<< "(" << settings.return_type_->generated_object_type_name() << ")"
<< "(" << temp_var_name << ");\n";
return NO_RETURN_VAR;
}
bool UniOp::perform_type_inference(TypeCheck::Settings &settings, Quack::Class *) {
right_->perform_type_inference(settings, nullptr);
type_ = right_->get_node_type();
if (type_ == BASE_CLASS)
throw TypeInferenceException("UniOp", "Unary operator called on statement of no type");
if (opsym == UNARY_OP_NEG && type_ != Quack::Class::Container::Int()) {
std::string msg = "Operator \"" + opsym + "\" does not match type " + type_->name_;
throw TypeInferenceException("UniOp", msg);
}
return true;
}
std::string UniOp::generate_code(CodeGen::Settings &settings, unsigned indent_lvl, bool) const {
if (opsym != UNARY_OP_NEG)
throw std::runtime_error("Only unary operation supported is \"" UNARY_OP_NEG "\"");
Quack::Class * int_class = Quack::Class::Container::Int();
auto * left = new IntLit(0);
left->set_node_type(int_class);
BinOp bin_op(opsym, left, right_);
bin_op.set_node_type(int_class);
std::string var_out = bin_op.generate_code(settings, indent_lvl, false);
// Release memory to prevent getting deleted
bin_op.right_ = nullptr;
return var_out;
}
bool BinOp::perform_type_inference(TypeCheck::Settings &settings, Quack::Class * parent_type) {
bool success = left_->perform_type_inference(settings, nullptr);
// Check the method information
std::string msg, method_name = op_lookup(opsym);
Quack::Class * l_type = left_->get_node_type();
Quack::Method* method = l_type->get_method(method_name);
if (method == OBJECT_NOT_FOUND) {
msg = "Operator \"" + opsym + "\" does not exist for class " + l_type->name_;
throw TypeInferenceException("BinOp", msg);
}
if (method->params_->count() != 1) {
msg = "Binary operator \"" + opsym + "\" for class \"" + l_type->name_
+ "\" should take exactly one argument";
throw TypeInferenceException("BinOp", msg);
}
// Verify the right type is valid
Quack::Param* param = (*method->params_)[0];
success = success && right_->perform_type_inference(settings, parent_type);
Quack::Class * r_type = right_->get_node_type();
if (!r_type->is_subtype(param->type_)) {
msg = "Invalid right type \"" + r_type->name_ + "\" for operator \"" + opsym + "\"";
throw TypeInferenceException("BinOp", msg);
}
if (type_ == nullptr)
type_ = method->return_type_;
else
type_ = type_->least_common_ancestor(method->return_type_);
// Reconcile binary operator return type
if (type_ == BASE_CLASS) {
msg = "Invalid return type for binary operator \"" + opsym + "\"";
throw TypeInferenceException("BinOp", msg);
}
return success;
}
bool Ident::update_inferred_type(TypeCheck::Settings &settings, Quack::Class *inferred_type,
bool is_field) {
Symbol * sym = settings.st_->get(text_, is_field);
assert(sym != OBJECT_NOT_FOUND);
if (((is_field && settings.is_constructor_) || !is_field) && sym->get_type() == BASE_CLASS)
settings.st_->update(sym, inferred_type);
else
settings.st_->update(sym, inferred_type->least_common_ancestor(sym->get_type()));
type_ = (type_ == BASE_CLASS) ? sym->get_type() : sym->get_type()->least_common_ancestor(type_);
if (type_ == BASE_CLASS)
throw TypeInferenceException("TypeUpdateFail",
"Type inference failed for variable " + text_);
return true;
}
bool Ident::perform_type_inference(TypeCheck::Settings &settings, Quack::Class *parent_type) {
// If the identifier is this, then mark as the type of this
if (settings.this_class_ != nullptr && text_ == OBJECT_SELF) {
Quack::Class * tc = settings.this_class_;
type_ = (type_ == nullptr) ? tc : type_->least_common_ancestor(tc);
if (type_ == nullptr)
throw TypeInferenceException("ThisError", "Invalid type for \"" OBJECT_SELF "\" class");
return true;
}
if (parent_type != nullptr && !parent_type->has_field(text_)) {
std::string msg = "Unknown field \"" + text_ + "\" for type \"" + parent_type->name_ + "\"";
throw TypeInferenceException("FieldError", msg);
}
Symbol * sym = settings.st_->get(text_, parent_type != nullptr);
type_ = (type_ == nullptr) ? sym->get_type() : type_->least_common_ancestor(sym->get_type());
return true;
}
bool ObjectCall::update_inferred_type(TypeCheck::Settings &settings, Quack::Class *inferred_type,
bool) {
if (auto obj = dynamic_cast<Ident *>(object_)) {
if (obj->text_ == OBJECT_SELF) {
if (auto next = dynamic_cast<Ident *>(next_)) {
if (settings.is_constructor_) {
// Only in a constructor can the field type be change
next->update_inferred_type(settings, inferred_type, true);
} else {
// Outside the constructor, use the field type
Symbol *sym = settings.st_->get(next->text_, true);
if (!inferred_type->is_subtype(sym->get_type()))
throw TypeInferenceException("InferenceError", "Type error for field " + next->text_);
next->update_inferred_type(settings, sym->get_type(), true);
}
type_ = next->get_node_type();
return true;
}
}
}
type_ = inferred_type;
return true;
}
bool ObjectCall::perform_type_inference(TypeCheck::Settings &settings, Quack::Class *){
bool success = true;
Quack::Class * obj_class;
// Process the object. Could be either a "this", symbol, or expression
if (auto obj = dynamic_cast<Ident *>(object_)) {
if (obj->text_ == OBJECT_SELF)
obj_class = settings.this_class_;
else
obj_class = settings.st_->get(obj->text_, false)->get_type();
} else {
// Type infer the object then pass that information to
success = object_->perform_type_inference(settings, nullptr);
obj_class = object_->get_node_type();
}
object_->set_node_type(obj_class);
// Infer the call then update the node type
success = success && next_->perform_type_inference(settings, obj_class);
if (type_ == nullptr)
type_ = next_->get_node_type();
else
type_ = type_->least_common_ancestor(next_->get_node_type());
// Some methods (e.g., print) return Nothing
// if (type_ == BASE_CLASS)
// throw TypeInferenceException("ObjectCall", "Unable to resolve object call");
return success;
}
const std::string ObjectCall::process_object_call(const std::string &left_obj,
CodeGen::Settings &settings,
unsigned indent_lvl, bool is_lhs) const {
// Use a dynamic cast to handle a method call, e.g. obj.<FuncName>(..)
if (auto func_call = dynamic_cast<FunctionCall*>(next_)) {
return func_call->generate_object_call(object_->get_node_type(), left_obj,
settings, indent_lvl, is_lhs);
}
// Use a dynamic cast to handle a field reference, e.g., obj.<FieldName>
// Store the field value in a temporary variable
if (auto ident = dynamic_cast<Ident*>(next_))
return generate_temp_var(left_obj + "->" + ident->text_, settings, indent_lvl, is_lhs);
// THe code should never get here. This indicates a logic error in the compiler
throw std::runtime_error("Unexpected bottoming out of ObjectCall code generation");
}
bool Typecase::perform_type_inference(TypeCheck::Settings &settings, Quack::Class *) {
// type case does not have a type
type_ = Quack::Class::Container::Nothing();
Quack::Class::Container * all_classes = Quack::Class::Container::singleton();
bool success = expr_->perform_type_inference(settings, nullptr);
for (auto * alt : *alts_) {
std::string new_sym_name = alt->type_names_[0];
std::string new_sym_type_name = alt->type_names_[1];
Quack::Class * new_sym_type = all_classes->get(new_sym_type_name);
if (new_sym_type == OBJECT_NOT_FOUND)
throw UnknownTypeException(new_sym_type_name);
// Check and configure the type for the typecase symbol
Symbol * sym = settings.st_->get(new_sym_name, false);
if (sym->get_type() != nullptr && !new_sym_type->is_subtype(sym->get_type())) {
std::string msg = "Cannot reconcile type for variable " + new_sym_name;
throw TypeInferenceException("TypecaseError", msg);
}
settings.st_->update(new_sym_name, false, new_sym_type);
success = success && alt->block_->perform_type_inference(settings);
// Verify the symbol name did not change after inference
if (sym->get_type() != new_sym_type) {
std::string msg = "Typecase var \"" + new_sym_name + "\" inferred type mismatch.";
throw TypeInferenceException("TypecaseMismatch", msg);
}
}
return success;
}
//====================================================================//
// Code Generation Related Method //
//====================================================================//
template<typename _T>
std::string Literal<_T>::generate_lit_code(CodeGen::Settings &settings, unsigned indent_lvl,
const std::string &gen_func_name_) const {
std::ostringstream ss;
ss << gen_func_name_ << "(" << value_ << ")";
return generate_temp_var(ss.str(), settings, indent_lvl, false);
}
std::string Typing::generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const {
std::string gen_var = expr_->generate_code(settings, indent_lvl, is_lhs);
if (type_name_.empty())
return gen_var;
// std::string cast_var = "(" + type_->generated_object_type_name() + ")" + gen_var;
// return generate_temp_var(cast_var, settings, indent_lvl, is_lhs);
// Generate a cast temp variable
return generate_temp_var(gen_var, settings, indent_lvl, is_lhs);
}
std::string FunctionCall::generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const {
Quack::Class * q_class = Quack::Class::Container::singleton()->get(ident_);
Quack::Param::Container * params = q_class->get_constructor()->params_;
assert(q_class);
std::vector<std::string> * arg_vars = args_->generate_args(settings, indent_lvl);
std::ostringstream ss;
if (q_class != get_node_type())
ss << "(" << get_node_type()->generated_object_type_name() << ")";
ss << q_class->generated_constructor_name() << "(";
assert(arg_vars->size() == params->count());
for (unsigned i = 0; i < arg_vars->size(); i++) {
if (i != 0)
ss << ", ";
ss << "(" << (*params)[i]->type_->generated_object_type_name() << ")" << (*arg_vars)[i];
}
ss << ")";
delete arg_vars;
return generate_temp_var(ss.str(), settings, indent_lvl, is_lhs);
}
std::string FunctionCall::generate_object_call(Quack::Class * obj_type, std::string object_name,
CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const {
std::vector<std::string>* func_tmp_args = args_->generate_args(settings, indent_lvl);
Quack::Method * method = obj_type->get_method(ident_);
std::ostringstream ss;
ss << object_name << "->" << GENERATED_CLASS_FIELD << "->" << ident_ << "("
<< "(" << method->obj_class_->generated_object_type_name() << ")" << object_name;
Quack::Param::Container * params = method->params_;
assert(func_tmp_args->size() == params->count());
for (unsigned i = 0; i < params->count(); i ++) {
Quack::Class * param_type = (*params)[i]->type_;
ss << ", " << "(" << param_type->generated_object_type_name() << ")" << (*func_tmp_args)[i];
}
ss << ")";
delete func_tmp_args;
return generate_temp_var(ss.str(), settings, indent_lvl, is_lhs);
}
std::string Assn::generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const {
if (is_lhs)
throw std::runtime_error("Cannot have assignment on LHS");
std::string rhs_var = rhs_->generate_code(settings, indent_lvl, false);
std::string lhs_var = lhs_->generate_code(settings, indent_lvl, true);
PRINT_INDENT(indent_lvl);
settings.fout_ << lhs_var << " = "
<< "(" << lhs_->get_node_type()->generated_object_type_name() << ")"
<< "(" << rhs_var << ");\n";
return NO_RETURN_VAR;
}
bool BoolOp::perform_type_inference(TypeCheck::Settings &settings, Quack::Class *) {
bool success = left_->perform_type_inference(settings, nullptr);
Quack::Class * bool_class = Quack::Class::Container::Bool();
std::string msg;
if (left_->get_node_type() != bool_class) {
msg = "Invalid left type \"" + left_->get_node_type()->name_ + "\" for op " + opsym + "";
throw TypeInferenceException("BinOp", msg);
}
// Check the method information
if (opsym == METHOD_AND || opsym == METHOD_OR) {
if (right_ == nullptr) {
msg = "Right child missing for binary op " + opsym + "";
throw TypeInferenceException("BinOp", msg);
}
success = success && right_->perform_type_inference(settings, nullptr);
if (right_->get_node_type() != bool_class) {
msg = "Invalid left type \"" + right_->get_node_type()->name_ + "\" for op " + opsym + "";
throw TypeInferenceException("BinOp", msg);
}
} else if (opsym == METHOD_NOT) {
// Nothing to do since single OP
} else {
throw TypeInferenceException("BoolOp", "Boolean operator \"" + opsym + "\" does not exist");
}
if (type_ == nullptr)
type_ = bool_class;
else
type_ = type_->least_common_ancestor(bool_class);
// Reconcile binary operator return type
if (type_ != bool_class) {
msg = "Invalid return type for Boolean operator \"" + opsym + "\"";
throw TypeInferenceException("BoolOp", msg);
}
return success;
}
std::string Typecase::generate_code(CodeGen::Settings &settings, unsigned indent_lvl,
bool is_lhs) const {
if (is_lhs)
throw std::runtime_error("Cannot have typecase on LHS");
// End of the TypeCase
std::string end_typecase = define_new_label("end_typecase");
// Build the label set
std::vector<std::string> labels;
labels.reserve(alts_->size() + 1);
for (auto * alt : *alts_)
labels.emplace_back(define_new_label("typecase_" + alt->type_names_[1] + "_"));
labels.emplace_back(end_typecase);
generate_one_line_comment(settings, indent_lvl, "Typecase START");
std::string typecase_var = expr_->generate_code(settings, indent_lvl, false);
for (unsigned i = 0; i < alts_->size(); i++) {
TypeAlternative * alt = (*alts_)[i];
std::string tc_name = alt->type_names_[1];
generate_one_line_comment(settings, indent_lvl, "Typecase Type - " + tc_name);
// Start the typecase check for this type
generate_label(settings, indent_lvl, labels[i], true);
Quack::Class * typecase_class = Quack::Class::Container::singleton()->get(tc_name);
// Go To Next typecase check
PRINT_INDENT(indent_lvl);
settings.fout_ << "if(!" << GENERATED_IS_SUBTYPE_FUNC << "("
<< "(" << Quack::Class::Container::Obj()->generated_clazz_type_name() << ")"
<< typecase_var << "->"
<< GENERATED_CLASS_FIELD << ", "
<< "(" << Quack::Class::Container::Obj()->generated_clazz_type_name() << ")"
<< "(&" << typecase_class->generated_clazz_obj_struct_name() << ")"
<< ")) { goto " << labels[i+1] << "; }\n";
// Set assign the expression
auto * var = new Ident(alt->type_names_[0].c_str());
Quack::Class * var_class = settings.st_->get(var->text_, false)->get_type();
var->set_node_type(var_class);
auto * typing = new Typing(var, "");
typing->set_node_type(var_class);
auto * other_var = new Ident(typecase_var.c_str());
Assn assn(typing, other_var);
assn.generate_code(settings, indent_lvl+1, false);
// Prevent an issue where the rhs is deleted
assn.rhs_ = nullptr;
alt->block_->generate_code(settings, indent_lvl);
// End fhe type case
generate_goto(settings, indent_lvl, end_typecase, true);
}
generate_label(settings, indent_lvl, end_typecase, true);
generate_one_line_comment(settings, indent_lvl, "Typecase END");
return NO_RETURN_VAR;
}
}