Program Listing for File quack_class.h

Return to documentation for file (src/quack_class.h)

//
// Created by Zayd Hammoudeh on 10/4/18.
//

#ifndef PROJECT02_QUACK_CLASSES_H
#define PROJECT02_QUACK_CLASSES_H

#include <map>
#include <cstring>
#include <vector>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <fstream>

#include "container_templates.h"
#include "quack_method.h"
#include "quack_field.h"
#include "keywords.h"

// Forward declaration
namespace CodeGen{ class Gen; }

namespace Quack {

  // Forward declaration
  class TypeChecker;

  class Class {
    friend class TypeChecker;
    friend class CodeGen::Gen;
   public:

    class Container : public MapContainer<Class> {
     public:
      static Container* singleton();
      static void reset() {
        Container* classes = singleton();
        for (auto &pair : *classes)
          delete pair.second;
        classes->clear();
      }
      const void print_original_src(unsigned int indent_depth) override {
        auto * print_class = new Container();

        for (const auto &pair : objs_) {
          if (pair.first == CLASS_OBJ || pair.first == CLASS_INT
              || pair.first == CLASS_STR || pair.first == CLASS_BOOL)
            continue;
          print_class->add(pair.second);
        }
        print_class->MapContainer<Class>::print_original_src_(indent_depth, "\n\n");
      }
      static Class* Int() {
        return singleton()->get(CLASS_INT);
      }
      static Class* Bool() {
        return singleton()->get(CLASS_BOOL);
      }
      static Class* Nothing() {
        return singleton()->get(CLASS_NOTHING);
      }
      static Class* Str() {
        return singleton()->get(CLASS_STR);
      }
      static Class* Obj() {
        return singleton()->get(CLASS_OBJ);
      }

      Container(Container const&) = delete;       // Don't Implement
      Container& operator=(Container const&) = delete;  // Don't implement
     private:
      Container() : MapContainer<Class>() {};          // Don't implement
    };

    Class(char* name, char * super_type, Param::Container* params,
          AST::Block* constructor, Method::Container* methods)
      : name_(name), super_type_name_(strcmp(super_type, "") == 0 ? CLASS_OBJ : super_type),
        super_(OBJECT_NOT_FOUND), methods_(methods), gen_methods_(nullptr), gen_fields_(nullptr) {

      fields_ = new Field::Container();

      // Constructor has same function name as the class
      constructor_ = new Method(name, this->name_, params, constructor);
      constructor_->return_type_ = this;

      for (const auto &method_info : *methods_) {
        Quack::Method * method = method_info.second;
        method->obj_class_ = this;
//        if (name_ == method->name_)
//          throw ParserException("Method name cannot match class name for class " + name_);
      }

//      Container* classes = Container::singleton();
//      if (classes->exists(name))
//        throw("Duplicate class named \"" + name_ + "\"");

//      if (name_ == CLASS_NOTHING)
//        throw ParserException("Invalid class name \"" + name_ + "\"");
    }
    virtual ~Class() {
      delete constructor_;
      delete methods_;
      delete fields_;

      delete gen_methods_;
      delete gen_fields_;
    }
    bool has_field(const std::string &name) {
      if (fields_->exists(name) || (super_ && super_->has_field(name)))
        return true;
      return false;
    }
    bool has_method(const std::string &name) {
      if (methods_->exists(name) || (super_ && super_->has_method(name)))
        return true;
      return false;
    }
    Method* get_method(const std::string &name) {
      if (methods_->exists(name))
        return methods_->get(name);
      if (super_ == BASE_CLASS)
        return OBJECT_NOT_FOUND;
      return super_->get_method(name);
    }
    static void check_well_formed_hierarchy() {
      for (auto & class_pair : *Container::singleton()) {
        Class* quack_class = class_pair.second;

        std::vector<Class*> super_list; // Compiler needs this because cant pass a temp by reference
        Class* base_parent = quack_class->has_no_cyclic_inheritance(super_list);

        if (base_parent != BASE_CLASS) {
          std::string str = "Class " + quack_class->name_ + " has a cyclic inheritance";
          throw CyclicInheritenceException("CyclicInheritance", str);
        }
      }

      // Check that the return type of inherited methods is subtype of super method
      for (auto & class_pair : *Container::singleton()) {
        Quack::Class * q_class = class_pair.second;
        if (q_class->super_ == BASE_CLASS)
          continue;

        for (auto &method_info : *q_class->methods_) {
          Method * method = method_info.second;
          if (!q_class->super_->has_method(method->name_))
            continue;

          Method * super_method = q_class->super_->get_method(method->name_);

          // Args count must match
          if (method->params_->count() != super_method->params_->count())
            throw InheritedMethodParamCountException(q_class->name_, method->name_);

          Quack::Class * method_rtype = method->return_type_;
          Quack::Class * super_rtype = super_method->return_type_;

          if (!method_rtype->is_subtype(super_rtype))
            throw InheritedMethodReturnTypeException(q_class->name_, method->name_);

          for (unsigned i = 0; i < method->params_->count(); i++) {
            Quack::Class * meth_param_type = (*method->params_)[i]->type_;
            Quack::Class * super_param_type = (*super_method->params_)[i]->type_;
            if (!super_param_type->is_subtype(meth_param_type)) {
              throw InheritedMethodParamTypeException(q_class->name_, method->name_,
                                                      (*method->params_)[i]->name_);
            }
          }
        }
      }
    }
    Class* has_no_cyclic_inheritance(std::vector<Class *> &all_super) {
      if (super_ == BASE_CLASS)
        return BASE_CLASS;

      // If the class appears in a super, then there is a cycle
      if (std::find(all_super.begin(), all_super.end(), this) != all_super.end())
        return this;

      all_super.emplace_back(this);
      return super_->has_no_cyclic_inheritance(all_super);
    }
    Class* least_common_ancestor(Class * other) {
      return Class::least_common_ancestor(this, other);
    }
    static Class* least_common_ancestor(Class* c1, Class* c2) {
      assert(c1 != nullptr && c2 != nullptr);
      if (c1 == c2)
        return c1;

      std::vector<std::vector<Class*>> class_paths(2);
      Class* classes[] = {c1, c2};
      // Build the list of paths
      for (int i = 0; i < 2; i++) {
        Class * q_class = classes[i];
        do {
          class_paths[i].emplace_back(q_class);
          q_class = q_class->super_;
        } while (q_class != BASE_CLASS);
      }

      // iterate through the lists in reverse to find last class both share
      unsigned long size0 = class_paths[0].size();
      unsigned long size1 = class_paths[1].size();
      auto min_len = std::min<unsigned long>(size0, size1);
      Class * last_shared = BASE_CLASS;
      for (unsigned long i = 1; i <= min_len; i++) {
        if (class_paths[0][size0 - i] != class_paths[1][size1 - i]) {
          assert(last_shared != BASE_CLASS);
          return last_shared;
        }

        last_shared = class_paths[0][size0 - i];
      }
      // One is a proper subset of the other
      return last_shared;
    }
    bool is_type(const std::string &name) {
      if (name_ == name)
        return true;
      if (super_)
        return super_->is_type(name);
      return false;
    }
    bool is_subtype(Class * other_type) {
      if (other_type == BASE_CLASS)
        return true;

      Class * super = this;
      while (super != BASE_CLASS) {
        if (super == other_type)
          return true;
        super = super->super_;
      }
      return false;
    }
    void initial_type_check() {
      configure_super_class();

      configure_method_params(*constructor_->params_);
      for (auto &method_pair : *methods_) {
        Method * method = method_pair.second;
        configure_method_params(*method->params_);

        if (Quack::Class::Container::singleton()->get(method->name_) != OBJECT_NOT_FOUND)
          throw MethodClassNameCollisionException(method->name_);

        // Check and configure the method return type.
//        if (method->return_type_name_ == CLASS_NOTHING) {
//          method->return_type_ = BASE_CLASS;
//        } else {
        method->return_type_ = Container::singleton()->get(method->return_type_name_);
        if (method->return_type_ == OBJECT_NOT_FOUND) {
          throw UnknownTypeException("Class: " + this->name_ + ", method " + method->name_
                                   + ", unknown return type \"" + method->return_type_name_ +
                                   "\"");
        }
//        }
      }
    }
    const std::string name_;
    static std::string generated_method_name(Class* q_class, Method * method) {
      return q_class->name_ + "_method_" + method->name_;
    }
//    /**
//     * Accessor for all the methods in the class.
//     * @return Methods in the class.
//     */
//    inline const Method::Container* methods() const { return methods_; }
    void print_original_src(unsigned int indent_depth) {
      std::string indent_str = std::string(indent_depth, '\t');
      std::cout << indent_str << KEY_CLASS << " " << name_ << "(";

      constructor_->params_->print_original_src(0);
      std::cout << ")";
      if (!super_type_name_.empty())
        std::cout << " " << KEY_EXTENDS << " " << super_type_name_;
      std::cout << " {\n";

      constructor_->print_original_src(indent_depth + 1);
      if (!constructor_->block_->empty() && !methods_->empty()) {
        std::cout << "\n\n";
      }
      methods_->print_original_src(indent_depth + 1);

      std::cout << "\n" << indent_str << "}";
    }
    virtual bool is_user_class() const { return true; }
    Method* get_constructor() { return constructor_; }
    const std::string generated_object_type_name() const {
      return "obj_" + name_;
    }
    const std::string generated_clazz_type_name() const {
      return "class_" + name_;
    }
    const std::string generated_struct_clazz_name() const {
      return generated_clazz_type_name() + "_struct";
    }
    const std::string generated_constructor_name() const {
      return "new_" + name_;
    }
    const std::string generated_clazz_obj_struct_name() const {
      return this->generated_clazz_obj_name() + "_struct";
    }
    void generate_code(CodeGen::Settings settings) {
      assert(this->is_user_class());

      settings.fout_ << "/*======================= " << name_ << " =======================*/\n"
                     << "/* Typedefs Required for Separation of class and object structs */\n"
                     << "struct " << generated_struct_clazz_name() << ";\n"
                     << "typedef struct " << generated_struct_clazz_name()
                     << "* " << generated_clazz_type_name() << ";\n"
                     << std::endl;

      generate_object_struct(settings);
      settings.fout_ << "\n";
      generate_clazz_struct(settings);

      generate_all_prototypes(settings);

      generate_clazz_object(settings);

      generate_constructor(settings);
      generate_methods(settings);

      settings.fout_ << std::endl;
    }
   private:
    void generate_clazz_struct(CodeGen::Settings settings) {
      settings.fout_ << "struct " << generated_struct_clazz_name() << " {";

      // Put constructor function pointer
      std::string indent = AST::ASTNode::indent_str(1);
      settings.fout_ << "\n" << indent << Container::Obj()->generated_clazz_type_name() << " "
                     << GENERATED_SUPER_FIELD << ";";

      settings.fout_ << "\n" << indent << generated_object_type_name()
                     << " (*" << METHOD_CONSTRUCTOR << ")(";
      constructor_->params_->generate_code(settings, false, false);
      settings.fout_ << ");";

      // Function pointers for all other methods
      build_generated_methods(this);
      for (auto method_info : *gen_methods_) {
        settings.fout_ << "\n" << indent
                       << method_info.second->return_type_->generated_object_type_name()
                       << " (*" << method_info.second->name_ << ")(";

        // Parameters - Use the implicit object then the parameter list
        settings.fout_ << method_info.first->generated_object_type_name();
        method_info.second->params_->generate_code(settings, false);

        settings.fout_ << ");";
      }
      settings.fout_ << "\n};\n";

    }
    void generate_object_struct(CodeGen::Settings settings) {
      settings.fout_ << "typedef struct " << generated_malloc_obj_name() << " {";
      // ToDo Add super
      // Method object field
      settings.fout_ << "\n" << AST::ASTNode::indent_str(1)
                     << generated_clazz_type_name() << " " << GENERATED_CLASS_FIELD << ";";

      build_generated_fields(this);
      for (auto field_info : *gen_fields_) {
        settings.fout_ << "\n" << AST::ASTNode::indent_str(1)
                       << field_info.second->type_->generated_object_type_name() << " "
                       << field_info.second->name_ << ";";
      }
      settings.fout_ << "\n} * " << generated_object_type_name() << ";\n";
    }
    const std::string generated_clazz_obj_name() const {
      return "the_class_" + name_;
    }
    const std::string generated_malloc_obj_name() const {
      return "obj_" + name_ + "_struct";
    }
    void generate_method_prototype(CodeGen::Settings settings, Method* method,
                                   bool is_constructor=false) {
      settings.fout_ << method->return_type_->generated_object_type_name() << " ";

      if (is_constructor)
        settings.fout_ << generated_constructor_name();
      else
        settings.fout_ << generated_method_name(this, method);

      settings.fout_ << "(";

      if (!is_constructor) {
        settings.fout_ << generated_object_type_name() << " " << OBJECT_SELF;
      }

      method->params_->generate_code(settings, true, !is_constructor);
      settings.fout_ << ")";
    }
    void generate_all_prototypes(CodeGen::Settings settings) {
      settings.fout_ << "\n";

      generate_method_prototype(settings, constructor_, true);
      settings.fout_ << ";\n";

      for (const auto &method : *methods_) {
        generate_method_prototype(settings, method.second);
        settings.fout_ << ";\n";
      }
    }
    void generate_clazz_object(CodeGen::Settings settings) {
      std::string class_obj_struct = generated_clazz_obj_struct_name();

      settings.fout_ << "\nstruct " << generated_struct_clazz_name() << " "
                     << class_obj_struct << " = {";

      std::string indent_str = AST::ASTNode::indent_str(1);

      std::string super_obj_struct = super_->generated_clazz_obj_struct_name();
      // Include in the super reference a cast to prevent compiler warnings
      settings.fout_ << "\n" << indent_str
                     << "(" << Quack::Class::Container::Obj()->generated_clazz_type_name() << ")"
                     << "&" << super_obj_struct;

      settings.fout_ << ",\n" << indent_str << generated_constructor_name();

      build_generated_methods(this);
      for (auto method_info : *gen_methods_) {
        settings.fout_ << ",\n" << indent_str
                       << generated_method_name(method_info.first, method_info.second);
      }

      settings.fout_ << "\n};\n\n"
                     << generated_clazz_type_name() << " " << generated_clazz_obj_name()
                     << " = &" << class_obj_struct << ";";
    }
    static void generate_symbol_table(CodeGen::Settings settings, unsigned indent_lvl,
                                      Method * method) {
      std::string indent_str = AST::ASTNode::indent_str(indent_lvl);

      for (const auto &symbol_info : *method->symbol_table_) {
        Symbol * sym = symbol_info.second;
        if (sym->is_field_ || method->params_->get(sym->name_) || sym->name_ == OBJECT_SELF)
          continue;

        settings.fout_ << indent_str << sym->get_type()->generated_object_type_name()
                       << " " << sym->name_ << ";\n";
      }
    }
    void generate_constructor(CodeGen::Settings settings) {
      settings.return_type_ = this;
      settings.st_ = constructor_->symbol_table_;

      settings.fout_ << "\n";
      generate_method_prototype(settings, constructor_, true);
      settings.fout_ << " {";

      // Allocate the memory for the object itself
      std::string indent_str = AST::ASTNode::indent_str(1);
      settings.fout_ << "\n" << indent_str << generated_object_type_name() << " " << OBJECT_SELF
                     << " = (" << generated_object_type_name() << ")malloc(sizeof(struct "
                     << generated_malloc_obj_name() << "));\n";

      // Define the object that will store the class methods
      settings.fout_ << indent_str << OBJECT_SELF << "->" << GENERATED_CLASS_FIELD
                     << " = " << generated_clazz_obj_name() << ";";

      generate_symbol_table(settings, 1, constructor_);
      settings.fout_ << "\n" << AST::ASTNode::indent_str(1) << "/* Method statements */\n";
      constructor_->block_->generate_code(settings, 0);

      settings.fout_ << "\n" << indent_str << "return " << OBJECT_SELF << ";";
      settings.fout_ << "\n}\n";

      settings.return_type_ = nullptr;
      settings.st_ = nullptr;
    }
    void generate_methods(CodeGen::Settings settings) {
      for (const auto &method_info : *methods_) {
        Method * method = method_info.second;

        settings.return_type_ = method->return_type_;
        settings.st_ = method->symbol_table_;

        // Define function header
        settings.fout_ << "\n";
        generate_method_prototype(settings, method);
        settings.fout_ << " {\n";

        generate_symbol_table(settings, 1, method);

        method->block_->generate_code(settings, 0);

        settings.fout_ << "}\n";
      }
      settings.return_type_ = nullptr;
      settings.st_ = nullptr;
    }
    template <typename _S>
    class GenObjContainer : public std::vector<std::pair<Class *, _S*>> {};
    void configure_super_class() {
      // Object is the top of the object hierarchy so handle specially.
      if (this->name_ == CLASS_OBJ) {
        super_ = BASE_CLASS;
        return;
      }

      std::string super_name = (super_type_name_.empty()) ? CLASS_OBJ : super_type_name_;

      Container* classes = Container::singleton();
      if (!classes->exists(super_type_name_)) {
        std::string msg = "For class, \"" + name_ + "\", unknown super class: " + super_type_name_;
        throw ClassHierarchyException("UnknownSuper", msg);
      }
      super_ = classes->get(super_name);
    }
    void configure_method_params(Param::Container &params) {
      for (auto &param : params) {
        if (param->type_name_ == CLASS_NOTHING) {
          std::string msg = "Parameter " + param->name_ + " cannot have type \"" CLASS_NOTHING "\"";
          throw ClassHierarchyException("NothingParam", msg);
        }

        Class* type_class = Container::singleton()->get(param->type_name_);
        if (type_class == OBJECT_NOT_FOUND)
          throw UnknownTypeException(param->type_name_);
        param->type_ = type_class;
      }
    }
    const std::string super_type_name_;
    Class *super_;
    Method* constructor_;
    static GenObjContainer<Method>* build_generated_methods(Class * q_class) {
      q_class->gen_methods_ =  build_generated_list<Method>(q_class, q_class->gen_methods_,
                                                            q_class->methods_,
                                                            Class::build_generated_methods);
      return q_class->gen_methods_;
    }
    static GenObjContainer<Field>* build_generated_fields(Class * q_class) {
      q_class->gen_fields_ = build_generated_list<Field>(q_class, q_class->gen_fields_,
                                                         q_class->fields_,
                                                         Class::build_generated_fields);
      return q_class->gen_fields_;
    }
    template <typename _T>
    static GenObjContainer<_T>* build_generated_list(Class * q_class, GenObjContainer<_T>* gen_vec,
                                                     MapContainer<_T>* container,
                                                     GenObjContainer<_T>* (*gen_func)(Class*)) {
      // Repeat building the object container multiple times for the same object
      if (gen_vec)
        return gen_vec;

      // Get all objects from the super class
      if (q_class->super_) {
        auto * temp_gen_fields = gen_func(q_class->super_);
        gen_vec = new GenObjContainer<_T>(*temp_gen_fields);
      } else {
        gen_vec = new GenObjContainer<_T>();
      }

      // Add remaining objects in this class
      unsigned long start_size = gen_vec->size();
      for (const auto &obj_pair : *container) {
        _T * obj = obj_pair.second;
        bool found = false;
        // "Override" the existing object in the vector
        for (unsigned i = 0; i < gen_vec->size(); i++) {
          if ((*gen_vec)[i].second->name_ == obj->name_) {
            (*gen_vec)[i] = std::pair<Class*, _T*>(q_class, obj);
            found = true;
            break;
          }
        }
        // If not found, then add this item to the list
        if (found)
          continue;
        gen_vec->emplace_back(std::pair<Class *, _T*>(q_class, obj));
      }

      // Sort only the stuff that is added
      std::sort(gen_vec->begin() + start_size, gen_vec->end(), template_pair_less_than<_T>);
      return gen_vec;
    }
    template <typename _T>
    static bool template_pair_less_than(std::pair<Class*, _T*> a, std::pair<Class*, _T*> b) {
      return a.second->name_ < b.second->name_;
    }
   protected:
    Method::Container* methods_;
    Field::Container* fields_;
    GenObjContainer<Method>* gen_methods_;
    GenObjContainer<Field>* gen_fields_;
    void add_binop_method(const std::string &method_name, const std::string &return_type,
                          const std::string &param_type) {

      auto * params = new Param::Container();
      params->add(new Param(FIELD_OTHER_LIT_NAME, param_type));

      Method * method = new Method(method_name, return_type, params, new AST::Block());
      method->init_list_ = new InitializedList();
      method->obj_class_ = this;

      methods_->add(method);
    }

    void add_unary_op_method(const std::string &method_name, const std::string &return_type) {
      auto * params = new Param::Container();
      Method * method = new Method(method_name, return_type, params, new AST::Block());
      method->obj_class_ = this;
      methods_->add(method);
    }
  };

  struct ObjectClass : public Class {
    ObjectClass()
        : Class(strdup(CLASS_OBJ), strdup(""), new Param::Container(),
                new AST::Block(), new Method::Container()) {
      add_base_methods();
    }
    void add_base_methods() {
      add_binop_method(METHOD_EQUALITY, CLASS_BOOL, CLASS_OBJ);
      // Unary op ok to use since function takes no args
      add_unary_op_method(METHOD_PRINT, CLASS_OBJ);
      add_unary_op_method(METHOD_STR, CLASS_STR);
    }
    bool is_user_class() const override { return false; }
  };

  struct NothingClass : public Class {
    explicit NothingClass()
        : Class(strdup(CLASS_NOTHING), strdup(CLASS_OBJ), new Param::Container(),
                new AST::Block(), new Method::Container()) { }
    bool is_user_class() const override { return false; }
  };

  struct PrimitiveClass : public Class {
    explicit PrimitiveClass(char* name)
            : Class(strdup(name), strdup(CLASS_OBJ), new Param::Container(),
                    new AST::Block(), new Method::Container()) { }
    bool is_user_class() const override { return false; }
  };

  struct IntClass : public PrimitiveClass {
    IntClass() : PrimitiveClass(strdup(CLASS_INT)) {
      add_unary_op_method(METHOD_STR, CLASS_STR);

      add_binop_method(METHOD_ADD, CLASS_INT, CLASS_INT);
      add_binop_method(METHOD_SUBTRACT, CLASS_INT, CLASS_INT);
      add_binop_method(METHOD_MULTIPLY, CLASS_INT, CLASS_INT);
      add_binop_method(METHOD_DIVIDE, CLASS_INT, CLASS_INT);

      add_binop_method(METHOD_EQUALITY, CLASS_BOOL, CLASS_OBJ);
      add_binop_method(METHOD_LEQ, CLASS_BOOL, CLASS_INT);
      add_binop_method(METHOD_LT, CLASS_BOOL, CLASS_INT);
      add_binop_method(METHOD_GEQ, CLASS_BOOL, CLASS_INT);
      add_binop_method(METHOD_GT, CLASS_BOOL, CLASS_INT);
    }
  };

  struct StringClass : public PrimitiveClass {
    StringClass() : PrimitiveClass(strdup(CLASS_STR)) {
      add_unary_op_method(METHOD_STR, CLASS_STR);

      add_binop_method(METHOD_ADD, CLASS_STR, CLASS_STR);

      add_binop_method(METHOD_EQUALITY, CLASS_BOOL, CLASS_OBJ);
      add_binop_method(METHOD_LEQ, CLASS_BOOL, CLASS_STR);
      add_binop_method(METHOD_LT, CLASS_BOOL, CLASS_STR);
      add_binop_method(METHOD_GEQ, CLASS_BOOL, CLASS_STR);
      add_binop_method(METHOD_GT, CLASS_BOOL, CLASS_STR);
    }
  };

  struct BooleanClass : public PrimitiveClass {
    BooleanClass() : PrimitiveClass(strdup(CLASS_BOOL)) {
      add_unary_op_method(METHOD_STR, CLASS_STR);

      add_binop_method(METHOD_EQUALITY, CLASS_BOOL, CLASS_OBJ);

//      add_binop_method(METHOD_OR, CLASS_BOOL, CLASS_BOOL);
//      add_binop_method(METHOD_AND, CLASS_BOOL, CLASS_BOOL);
    }
  };
}

#endif //PROJECT02_QUACK_CLASSES_H