diff --git a/assignments/PA4/bad_inherit.cl b/assignments/PA4/bad_inherit.cl new file mode 100644 index 0000000..7b631e0 --- /dev/null +++ b/assignments/PA4/bad_inherit.cl @@ -0,0 +1,37 @@ +(* The inheritance analysis has 2 stages, graph construction & cyclic checking. + it will abort if duplicate or undefined class definitions are found before do cyclic checking *) +-- uncomment the lines below to get convinced that 1st stage works +(* +class DupDef {}; + +class DupDef {}; + +class DupDef1 {}; + +class DupDef1 {}; + +class A inherits SCHEISSE{}; +*) +(* +class D inherits D {}; + +class Cycle1 inherits Cycle2 {}; +class Cycle2 inherits Cycle3 {}; +class Cycle3 inherits Cycle1 {}; + +class Cycle4 inherits Cycle3 {}; +*) +class Ok1{}; +class Ok2_1 inherits Ok1 {}; +class Ok2_2 inherits Ok1 {}; +class Ok3 inherits Ok2_1 {}; +class Ok4 inherits Ok2_2 {}; + + +class Main inherits IO { + main(): Object { + { + a(); + } + }; +}; \ No newline at end of file diff --git a/assignments/PA4/cool-tree.h b/assignments/PA4/cool-tree.h index d2312d2..6c4223d 100644 --- a/assignments/PA4/cool-tree.h +++ b/assignments/PA4/cool-tree.h @@ -161,6 +161,9 @@ public: } Class_ copy_Class_(); void dump(ostream& stream, int n); + Symbol get_name() const { return name; } + Symbol get_parent() const { return parent; } + Features get_features() const { return features; } #ifdef Class__SHARED_EXTRAS Class__SHARED_EXTRAS diff --git a/assignments/PA4/semant.cc b/assignments/PA4/semant.cc index 3f16e70..429ffae 100644 --- a/assignments/PA4/semant.cc +++ b/assignments/PA4/semant.cc @@ -1,8 +1,11 @@ +#include #include #include #include +#include +#include "cool-tree.h" #include "semant.h" #include "utilities.h" @@ -84,9 +87,48 @@ static void initialize_constants(void) ClassTable::ClassTable(Classes classes) : semant_errors(0) , error_stream(cerr) { + install_basic_classes(); + for (auto i = classes->first(); classes->more(i); i = classes->next(i)) { + auto class_i = static_cast(classes->nth(i)); + if (name_to_node.find(class_i->get_name()) != name_to_node.end()) { + semant_error(class_i) << "Class `" << class_i->get_name() << "` was previously defined.\n"; + } + else { + name_to_node[class_i->get_name()] = new ClassGraphNode(class_i, nullptr); + // null means we have this class, but not yet build inheritence graph for it + } + } + for (auto i = classes->first(); classes->more(i); i = classes->next(i)) { + auto class_i = static_cast(classes->nth(i)); + if (name_to_node.find(class_i->get_parent()) == name_to_node.end()) { + semant_error(class_i) << "Class `" << class_i->get_name() << "` inherits from an undefined class `" + << class_i->get_parent() << "`\n"; + } + else { + auto class_parent = name_to_node[class_i->get_parent()]; + assert(class_parent != nullptr); + class_parent->append_child(name_to_node[class_i->get_name()]); + } + } - /* Fill this in */ + if (semant_errors) return; + // we abort here before check cyclic inheritence if error once occurred + // In COOL's case, every class could have only one base class + // One simple judgement is that, if it cannot go up to object, then the class or its ancestor involves in a cycle + // Thus, we can start from object and mark all reachable nodes, error report those unreachable nodes + class_root->traverse(); + for (auto i : name_to_node) { + if (!i.second->reachable()) { + semant_error(i.second->get_class()) << "Class `" << i.first + << "` or its ancestor, is involved in an inheritance cycle.\n"; + } + } + if (semant_errors) return; + if (semant_debug) { + std::cout << "Class Inheritance Analysis done.\n"; + class_root->traverse(std::cout, 0); + } } void ClassTable::install_basic_classes() { @@ -188,6 +230,23 @@ void ClassTable::install_basic_classes() { Str, no_expr()))), filename); + auto object_class = static_cast(Object_class); + auto io_class = static_cast(IO_class); + auto bool_class = static_cast(Bool_class); + auto int_class = static_cast(Int_class); + auto str_class = static_cast(Str_class); + + class_root = new ClassGraphNode(Object_class, nullptr); + name_to_node[object_class->get_name()] = class_root; + name_to_node[io_class->get_name()] = class_root->new_child(IO_class); + name_to_node[int_class->get_name()] = class_root->new_child(Int_class); + name_to_node[bool_class->get_name()] = class_root->new_child(Bool_class); + name_to_node[str_class->get_name()] = class_root->new_child(Str_class); + + if (semant_debug) { + std::cout << "Basic classed installed\n"; + class_root->traverse(std::cout, 0); + } } //////////////////////////////////////////////////////////////////// diff --git a/assignments/PA4/semant.h b/assignments/PA4/semant.h index 0e0e28a..1d97e01 100644 --- a/assignments/PA4/semant.h +++ b/assignments/PA4/semant.h @@ -2,8 +2,12 @@ #define SEMANT_H_ #include -#include +#include +#include +#include +#include #include "cool-tree.h" +#include "cool-tree.handcode.h" #include "stringtab.h" #include "symtab.h" #include "list.h" @@ -19,11 +23,98 @@ typedef ClassTable *ClassTableP; // you like: it is only here to provide a container for the supplied // methods. +class ClassGraphNode { +private: + ClassGraphNode* parent; + std::vector children; + Class_ self; + int mark = 0; + +public: + // Note that, Class_ = Class__class*, itself is a ptr type, so just use it + ClassGraphNode(Class_ self_class, ClassGraphNode* parent_class = nullptr) : + parent(parent_class), self(self_class) { } + ClassGraphNode(const ClassGraphNode&) = delete; + ClassGraphNode& operator=(const ClassGraphNode) = delete; + ~ ClassGraphNode() { + // Long time no C++, god knows if the destructor works or not... + while (!children.empty()) { + auto child = children[children.size() - 1]; + children.pop_back(); + assert(child != nullptr); + delete child; + } + } + + /* + * Create a child node and set child points to child_class + */ + ClassGraphNode* new_child(Class_ child_class) { + for (auto i = children.begin(); i != children.end(); ++ i) { + if ((*i)->self == child_class) { + // it is okay to judge class by compare node address + // because a class cannot be redefined, the AST node is identical + return *i; + } + } + ClassGraphNode * newnode = new ClassGraphNode(child_class, this); + children.push_back(newnode); + return newnode; + } + + /* + * Set child_node as this's child, without any memory allocation + */ + void append_child(ClassGraphNode * child_node) { + for (auto i = children.begin(); i != children.end(); ++ i) { + if ((*i) == child_node) { + return; + } + } + child_node->parent = this; + children.push_back(child_node); + } + + /* + * traverse and print class name + */ + void traverse(std::ostream& stream, int n) const { + dump_Symbol(stream, n, dynamic_cast(self)->get_name()); + for (auto child: children) { + child->traverse(stream, n + 2); + } + } + + /* + * traverse and mark all reachable children + */ + void traverse() { + mark = 1; + for (auto child : children) { + child->traverse(); + } + } + + bool reachable() { + return !(mark == 0); + } + + Class_ get_class() { + return self; + } +}; + + +// First we need a tree-like struct to store inheritence relationship +// Since class doesn't need to be defined before used, I guess first class ClassTable { private: int semant_errors; void install_basic_classes(); ostream& error_stream; + ClassGraphNode* class_root; + std::map name_to_node; + // again, the correctness of using ptr can be inferred from `Single Copy` of the symbol string public: ClassTable(Classes);