1307 lines
48 KiB
C++
1307 lines
48 KiB
C++
|
||
|
||
#include <cassert>
|
||
#include <iostream>
|
||
#include <set>
|
||
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <stdarg.h>
|
||
#include <vector>
|
||
#include <queue>
|
||
#include <memory.h>
|
||
#include "cool-tree.h"
|
||
#include "cool-tree.handcode.h"
|
||
#include "semant.h"
|
||
#include "utilities.h"
|
||
|
||
|
||
extern int semant_debug;
|
||
extern char *curr_filename;
|
||
|
||
//////////////////////////////////////////////////////////////////////
|
||
//
|
||
// Symbols
|
||
//
|
||
// For convenience, a large number of symbols are predefined here.
|
||
// These symbols include the primitive type and method names, as well
|
||
// as fixed names used by the runtime system.
|
||
//
|
||
//////////////////////////////////////////////////////////////////////
|
||
static Symbol
|
||
arg,
|
||
arg2,
|
||
Bool,
|
||
concat,
|
||
cool_abort,
|
||
copy,
|
||
Int,
|
||
in_int,
|
||
in_string,
|
||
IO,
|
||
length,
|
||
Main,
|
||
main_meth,
|
||
No_class,
|
||
No_type,
|
||
Object,
|
||
out_int,
|
||
out_string,
|
||
prim_slot,
|
||
self,
|
||
SELF_TYPE,
|
||
Str,
|
||
str_field,
|
||
substr,
|
||
type_name,
|
||
val;
|
||
//
|
||
// Initializing the predefined symbols.
|
||
//
|
||
static void initialize_constants(void)
|
||
{
|
||
arg = idtable.add_string("arg");
|
||
arg2 = idtable.add_string("arg2");
|
||
Bool = idtable.add_string("Bool");
|
||
concat = idtable.add_string("concat");
|
||
cool_abort = idtable.add_string("abort");
|
||
copy = idtable.add_string("copy");
|
||
Int = idtable.add_string("Int");
|
||
in_int = idtable.add_string("in_int");
|
||
in_string = idtable.add_string("in_string");
|
||
IO = idtable.add_string("IO");
|
||
length = idtable.add_string("length");
|
||
Main = idtable.add_string("Main");
|
||
main_meth = idtable.add_string("main");
|
||
// _no_class is a symbol that can't be the name of any
|
||
// user-defined class.
|
||
No_class = idtable.add_string("_no_class");
|
||
No_type = idtable.add_string("_no_type");
|
||
Object = idtable.add_string("Object");
|
||
out_int = idtable.add_string("out_int");
|
||
out_string = idtable.add_string("out_string");
|
||
prim_slot = idtable.add_string("_prim_slot");
|
||
self = idtable.add_string("self");
|
||
SELF_TYPE = idtable.add_string("SELF_TYPE");
|
||
Str = idtable.add_string("String");
|
||
str_field = idtable.add_string("_str_field");
|
||
substr = idtable.add_string("substr");
|
||
type_name = idtable.add_string("type_name");
|
||
val = idtable.add_string("_val");
|
||
}
|
||
|
||
|
||
/*
|
||
* In ClassTable's constructor, the inheritance graph is built and checked,
|
||
* with all defined classes recorded for later check.
|
||
* In addition, scan attribute and method definition into global Symbol Table
|
||
*/
|
||
ClassTable::ClassTable(Classes classes) : semant_errors(0) , error_stream(cerr) {
|
||
install_basic_classes();
|
||
/* first scan: de-duplicate class definitions*/
|
||
for (auto i = classes->first(); classes->more(i); i = classes->next(i)) {
|
||
auto class_i = static_cast<class__class*>(classes->nth(i));
|
||
if (class_i->get_name() == SELF_TYPE) {
|
||
semant_error(class_i) << "Redefinition of basic class SELF_TYPE.\n";
|
||
}
|
||
else 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 {
|
||
// null means we have this class, but not yet build inheritence graph for it
|
||
name_to_node[class_i->get_name()] = new ClassGraphNode(class_i, nullptr);
|
||
// discard redefined classes by constructing a new vector of classes
|
||
_class_vec.push_back(class_i);
|
||
}
|
||
}
|
||
|
||
/* second scan: check base class inheritable */
|
||
for (auto class_i : _class_vec) {
|
||
auto sym_parent = class_i->get_parent();
|
||
// Cool has restrictions on inheriting from the basic classes
|
||
if ( sym_parent == Bool || sym_parent == Int || sym_parent == Str) {
|
||
semant_error(class_i) << "Class " << class_i->get_name()
|
||
<< " cannot inherit class " << sym_parent << ".\n";
|
||
}
|
||
else if (name_to_node.find(sym_parent) == name_to_node.end()) {
|
||
semant_error(class_i) << "Class `" << class_i->get_name() << "` inherits from an undefined class `"
|
||
<< sym_parent << "`\n";
|
||
}
|
||
else {
|
||
auto class_parent = name_to_node[sym_parent];
|
||
// assert(class_parent != nullptr);
|
||
class_parent->append_child(name_to_node[class_i->get_name()]);
|
||
}
|
||
}
|
||
|
||
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
|
||
|
||
/* third scan: check cyclic inheritance */
|
||
class_root->traverse_reachable(0);
|
||
for (auto i : name_to_node) {
|
||
if (i.second->depth == 0) {
|
||
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::cerr<< "Class Inheritance Analysis done.\n";
|
||
class_root->traverse_dump_name(std::cout, 0);
|
||
}
|
||
}
|
||
|
||
void ClassTable::symtab_dump(Symbol class_name) {
|
||
std::cerr << "SymTab of " << class_name << "\n";
|
||
std::cerr << "M(" << class_name << ") =";
|
||
_symtab_met[class_name]->dump();
|
||
std::cerr << "O(" << class_name << ") =";
|
||
_symtab_obj[class_name]->dump();
|
||
std::cerr << "--------\n\n";
|
||
}
|
||
|
||
Symbol ClassTable::symtab_object_lookup_parent(Class_ class_i, Symbol object_name) const {
|
||
return symtab_object_lookup_parent(class_to_name(class_i), object_name);
|
||
}
|
||
|
||
Symbol ClassTable::symtab_object_lookup_parent(Symbol class_name, Symbol object_name) const {
|
||
assert(name_to_node.find(class_name) != name_to_node.end());
|
||
auto class_node = name_to_node.find(class_name)->second;
|
||
auto parent_node= class_node;
|
||
while (parent_node) {
|
||
auto result = _symtab_obj.find(parent_node->get_class()->get_name())->second->lookup(object_name);
|
||
// only need to lookup in the top scope, because attributes all reside in top scope
|
||
if (result) return result;
|
||
parent_node = parent_node->parent;
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
method_class* ClassTable::symtab_method_lookup_parent(Class_ class_i, Symbol method_name) const {
|
||
return symtab_method_lookup_parent(class_to_name(class_i), method_name);
|
||
}
|
||
|
||
method_class* ClassTable::symtab_method_lookup_parent(Symbol class_name, Symbol method_name) const {
|
||
assert(name_to_node.find(class_name) != name_to_node.end());
|
||
auto class_node = name_to_node.find(class_name)->second;
|
||
auto parent_node= class_node;
|
||
while (parent_node) {
|
||
auto result = _symtab_met.find(parent_node->get_class()->get_name())->second->lookup(method_name);
|
||
// only need to lookup in the top scope, because attributes all reside in top scope
|
||
if (result) return result;
|
||
parent_node = parent_node->parent;
|
||
}
|
||
return nullptr;
|
||
}
|
||
|
||
void ClassTable::install_all_features() {
|
||
/* fourth scan: gather attr and method definition */
|
||
class__class* class_Main = nullptr;
|
||
method_class* method_main = nullptr; // main in class Main
|
||
std::queue<ClassGraphNode*> _iter_queue;
|
||
_iter_queue.push(class_root);
|
||
while (!_iter_queue.empty()) {
|
||
auto cur_node = _iter_queue.front();
|
||
_iter_queue.pop();
|
||
// std::cerr << "Pop " << cur_node->get_class()->get_name() << "\n";
|
||
|
||
for (auto child : cur_node->children) {
|
||
_iter_queue.push(child);
|
||
// std::cerr << "Push " << child->get_class()->get_name() << "\n";
|
||
}
|
||
auto cur_class = cur_node->get_class();
|
||
auto cur_name = cur_class->get_name();
|
||
if (cur_name == Main) {
|
||
class_Main = cur_class;
|
||
}
|
||
_symtab_met[cur_name] = new SymbolTable<Symbol, method_class>;
|
||
_symtab_obj[cur_name] = new SymbolTable<Symbol, Entry>;
|
||
_symtab_met[cur_name]->enterscope();
|
||
_symtab_obj[cur_name]->enterscope();
|
||
_symtab_obj[cur_name]->addid(self, SELF_TYPE);
|
||
auto cur_features = cur_class->get_features();
|
||
for (auto j = cur_features->first(); cur_features->more(j); j = cur_features->next(j)) {
|
||
auto cur_feature = cur_features->nth(j);
|
||
// std::cerr << "feature " << j << " ";
|
||
if (typeid(*cur_feature) == typeid(attr_class)) {
|
||
auto cur_attr = static_cast<attr_class*>(cur_feature);
|
||
// std::cerr << "attr " << cur_attr->get_name() << "\n";
|
||
if (cur_attr->get_name() == self) {
|
||
semant_error(cur_class->get_filename(), cur_attr)
|
||
<< "\'self\' cannot be the name of an attribute.\n";
|
||
}
|
||
else if (_symtab_obj[cur_name]->lookup(cur_attr->get_name()) != nullptr) {
|
||
semant_error(cur_class->get_filename(), cur_attr) << "Attribute " << cur_attr->get_name()
|
||
<< " is multiply defined.\n";
|
||
}
|
||
else if (symtab_object_lookup_parent(cur_name, cur_attr->get_name()) != nullptr) {
|
||
// check duplicate name for attributes
|
||
semant_error(cur_class->get_filename(), cur_attr) << "Attribute " << cur_attr->get_name()
|
||
<< " is an attribute of an inherited class.\n";
|
||
}
|
||
else {
|
||
_symtab_obj[cur_name]->addid(cur_attr->get_name(), cur_attr->get_type_decl());
|
||
}
|
||
}
|
||
else if (typeid(*cur_feature) == typeid(method_class)) {
|
||
auto cur_method = static_cast<method_class*>(cur_feature);
|
||
// std::cerr << "method " << cur_method->get_name() << "\n";
|
||
if (_symtab_met[cur_name]->lookup(cur_method->get_name()) != nullptr) {
|
||
semant_error(cur_class->get_filename(), cur_method) << "Method " << cur_method->get_name()
|
||
<< " is multiply defined.\n";
|
||
}
|
||
else {
|
||
auto overridden_method = symtab_method_lookup_parent(cur_name, cur_method->get_name());
|
||
auto _error_flag = false;
|
||
if (overridden_method != nullptr) {
|
||
// additional check is necessary for overridden methods
|
||
if (semant_debug) std::cerr << "Overriden method" << cur_method->get_name() << "\n";
|
||
if (cur_method->get_return_type() != overridden_method->get_return_type()) {
|
||
// direct comaprison between Symbols is okay, 'cause Symbols(ptr to Entry) are distinct
|
||
// when talking about classes or types
|
||
semant_error(cur_class->get_filename(), cur_method)
|
||
<< "In redefined method " << cur_method->get_name()
|
||
<< ", return type " << cur_method->get_return_type()
|
||
<< " is different from original return type" << overridden_method->get_return_type()
|
||
<< ".\n";
|
||
_error_flag = true;
|
||
}
|
||
else if (cur_method->get_formals()->len() != overridden_method->get_formals()->len()) {
|
||
semant_error(cur_class->get_filename(), cur_method)
|
||
<< "Incompatible number of formal parameters in redefined method "
|
||
<< cur_method->get_name()
|
||
<< ".\n";
|
||
_error_flag = true;
|
||
}
|
||
else {
|
||
// we cannot directly compare 2 formal lists
|
||
auto cur_formals = cur_method->get_formals();
|
||
auto overridden_formals = overridden_method->get_formals();
|
||
auto cur_formal_i = cur_formals->first();
|
||
auto overridden_formal_i = overridden_formals->first();
|
||
// by last check, we get ensured that their formal list should have the same length
|
||
while (cur_formals->more(cur_formal_i)) {
|
||
auto cur_formal = static_cast<formal_class*>(cur_formals->nth(cur_formal_i));
|
||
auto overridden_formal = static_cast<formal_class*>(overridden_formals->nth(cur_formal_i));
|
||
if (cur_formal->get_type_decl() != overridden_formal->get_type_decl()) {
|
||
semant_error(cur_class->get_filename(), cur_method)
|
||
<< "In redefined method " << cur_method->get_name()
|
||
<< ", parameter type " << cur_formal->get_type_decl()
|
||
<< " is different from original type" << overridden_formal->get_type_decl()
|
||
<< ".\n";
|
||
_error_flag = true;
|
||
break;
|
||
}
|
||
cur_formal_i = cur_formals->next(cur_formal_i);
|
||
overridden_formal_i = overridden_formals->next(overridden_formal_i);
|
||
}
|
||
}
|
||
}
|
||
if (!_error_flag) {
|
||
// std::cerr << "method done " << cur_method->get_name() << "\n";
|
||
_symtab_met[cur_name]->addid(cur_method->get_name(), cur_method);
|
||
if (class_Main != nullptr && cur_name == Main && cur_method->get_name() == main_meth) {
|
||
// main in class Main
|
||
method_main = cur_method;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else assert(0);
|
||
}
|
||
if (semant_debug) {
|
||
symtab_dump(cur_name);
|
||
}
|
||
}
|
||
|
||
if (!class_Main) {
|
||
semant_error() << "Class Main is not defined.\n";
|
||
return;
|
||
}
|
||
else if (!method_main) {
|
||
semant_error(class_Main) << "No 'main' method in class Main.\n";
|
||
return;
|
||
}
|
||
else
|
||
if (semant_debug) {
|
||
std::cerr << "Attrs & Methods Collection done.\n";
|
||
}
|
||
}
|
||
|
||
void ClassTable::install_basic_classes() {
|
||
|
||
// The tree package uses these globals to annotate the classes built below.
|
||
// curr_lineno = 0;
|
||
Symbol filename = stringtable.add_string("<basic class>");
|
||
|
||
// The following demonstrates how to create dummy parse trees to
|
||
// refer to basic Cool classes. There's no need for method
|
||
// bodies -- these are already built into the runtime system.
|
||
|
||
// IMPORTANT: The results of the following expressions are
|
||
// stored in local variables. You will want to do something
|
||
// with those variables at the end of this method to make this
|
||
// code meaningful.
|
||
|
||
//
|
||
// The Object class has no parent class. Its methods are
|
||
// abort() : Object aborts the program
|
||
// type_name() : Str returns a string representation of class name
|
||
// copy() : SELF_TYPE returns a copy of the object
|
||
//
|
||
// There is no need for method bodies in the basic classes---these
|
||
// are already built in to the runtime system.
|
||
|
||
Class_ Object_class =
|
||
class_(Object,
|
||
No_class,
|
||
append_Features(
|
||
append_Features(
|
||
single_Features(method(cool_abort, nil_Formals(), Object, no_expr())),
|
||
single_Features(method(type_name, nil_Formals(), Str, no_expr()))),
|
||
single_Features(method(copy, nil_Formals(), SELF_TYPE, no_expr()))),
|
||
filename);
|
||
|
||
//
|
||
// The IO class inherits from Object. Its methods are
|
||
// out_string(Str) : SELF_TYPE writes a string to the output
|
||
// out_int(Int) : SELF_TYPE " an int " " "
|
||
// in_string() : Str reads a string from the input
|
||
// in_int() : Int " an int " " "
|
||
//
|
||
Class_ IO_class =
|
||
class_(IO,
|
||
Object,
|
||
append_Features(
|
||
append_Features(
|
||
append_Features(
|
||
single_Features(method(out_string, single_Formals(formal(arg, Str)),
|
||
SELF_TYPE, no_expr())),
|
||
single_Features(method(out_int, single_Formals(formal(arg, Int)),
|
||
SELF_TYPE, no_expr()))),
|
||
single_Features(method(in_string, nil_Formals(), Str, no_expr()))),
|
||
single_Features(method(in_int, nil_Formals(), Int, no_expr()))),
|
||
filename);
|
||
|
||
//
|
||
// The Int class has no methods and only a single attribute, the
|
||
// "val" for the integer.
|
||
//
|
||
Class_ Int_class =
|
||
class_(Int,
|
||
Object,
|
||
single_Features(attr(val, prim_slot, no_expr())),
|
||
filename);
|
||
|
||
//
|
||
// Bool also has only the "val" slot.
|
||
//
|
||
Class_ Bool_class =
|
||
class_(Bool, Object, single_Features(attr(val, prim_slot, no_expr())),filename);
|
||
|
||
//
|
||
// The class Str has a number of slots and operations:
|
||
// val the length of the string
|
||
// str_field the string itself
|
||
// length() : Int returns length of the string
|
||
// concat(arg: Str) : Str performs string concatenation
|
||
// substr(arg: Int, arg2: Int): Str substring selection
|
||
//
|
||
Class_ Str_class =
|
||
class_(Str,
|
||
Object,
|
||
append_Features(
|
||
append_Features(
|
||
append_Features(
|
||
append_Features(
|
||
single_Features(attr(val, Int, no_expr())),
|
||
single_Features(attr(str_field, prim_slot, no_expr()))),
|
||
single_Features(method(length, nil_Formals(), Int, no_expr()))),
|
||
single_Features(method(concat,
|
||
single_Formals(formal(arg, Str)),
|
||
Str,
|
||
no_expr()))),
|
||
single_Features(method(substr,
|
||
append_Formals(single_Formals(formal(arg, Int)),
|
||
single_Formals(formal(arg2, Int))),
|
||
Str,
|
||
no_expr()))),
|
||
filename);
|
||
auto object_class = static_cast<class__class*>(Object_class);
|
||
auto io_class = static_cast<class__class*>(IO_class);
|
||
auto bool_class = static_cast<class__class*>(Bool_class);
|
||
auto int_class = static_cast<class__class*>(Int_class);
|
||
auto str_class = static_cast<class__class*>(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_dump_name(std::cout, 0);
|
||
}
|
||
}
|
||
|
||
////////////////////////////////////////////////////////////////////
|
||
//
|
||
// semant_error is an overloaded function for reporting errors
|
||
// during semantic analysis. There are three versions:
|
||
//
|
||
// ostream& ClassTable::semant_error()
|
||
//
|
||
// ostream& ClassTable::semant_error(Class_ c)
|
||
// print line number and filename for `c'
|
||
//
|
||
// ostream& ClassTable::semant_error(Symbol filename, tree_node *t)
|
||
// print a line number and filename
|
||
//
|
||
///////////////////////////////////////////////////////////////////
|
||
|
||
ostream& ClassTable::semant_error(Class_ c)
|
||
{
|
||
return semant_error(c->get_filename(),c);
|
||
}
|
||
|
||
ostream& ClassTable::semant_error(Symbol filename, tree_node *t)
|
||
{
|
||
error_stream << filename << ":" << t->get_line_number() << ": ";
|
||
return semant_error();
|
||
}
|
||
|
||
ostream& ClassTable::semant_error()
|
||
{
|
||
semant_errors++;
|
||
return error_stream;
|
||
}
|
||
|
||
/*
|
||
* Query the Inheritance Graph if T1 <= T0
|
||
*
|
||
* Note: if T1 inherits from(or is) T0, then T1 <= T0
|
||
*
|
||
* Warning: if the type doesn't exist, by default return `TRUE` to stop
|
||
* error cascading
|
||
*/
|
||
bool ClassTable::conform(Symbol T1, Symbol T0) const {
|
||
if (T1 == T0) return true;
|
||
if (!class_defined(T0) || !class_defined(T1))
|
||
return true;
|
||
auto cur_node_itr = name_to_node.find(T1);
|
||
if (cur_node_itr == name_to_node.end()){
|
||
std::cerr << "Comform: Escaping type T1 = " << T1 << "\n";
|
||
assert(cur_node_itr != name_to_node.end());
|
||
}
|
||
auto cur_node = cur_node_itr->second;
|
||
while (cur_node) {
|
||
if (cur_node->get_class()->get_name() == T0) {
|
||
return true;
|
||
}
|
||
cur_node = cur_node->parent;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
/*
|
||
* conformance check with SELF_TYPE support
|
||
*/
|
||
bool ClassTable::conform_self(Symbol T1, Symbol T0, Symbol env_class) const {
|
||
if (T1 == T0) return true;
|
||
/*
|
||
* Add a special rule for SELF_TYPE: SELF_TYPE_c <= T0 if C <= T0
|
||
*/
|
||
if (T1 == SELF_TYPE) {
|
||
return conform(env_class, T0);
|
||
}
|
||
if (T0 == SELF_TYPE) {
|
||
return false;
|
||
}
|
||
else {
|
||
return conform(T1, T0);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* conformance check with SELF_TYPE support
|
||
*/
|
||
bool ClassTable::conform_self(Symbol T1, Symbol T0, Class_ env_class) const {
|
||
return conform_self(T1, T0, class_to_name(env_class));
|
||
}
|
||
|
||
/*
|
||
* Check whether a class is defined (not including SELF_TYPE)
|
||
*/
|
||
bool ClassTable::class_defined(Symbol class_id) const {
|
||
return name_to_node.find(class_id) != name_to_node.end();
|
||
}
|
||
|
||
/*
|
||
* Check Class existence allowing `SELF_TYPE` to be a valid type name
|
||
* `SELF_TYPE` may be used in the following places: No other uses are permitted.
|
||
* - new SELF TYPE
|
||
* - return type of a method,
|
||
* - declared type of a let variable
|
||
* - declared type of an attribute
|
||
*/
|
||
bool ClassTable::class_defined_self(Symbol class_id) const {
|
||
if (class_id == SELF_TYPE) return true;
|
||
else return class_defined(class_id);
|
||
}
|
||
|
||
/*
|
||
* Get the `join` of two types, aka Lowest Upper Bound
|
||
* Actually it is the LCA of the two nodes on inheritence tree
|
||
*
|
||
* Warning:
|
||
* If either of the inputs is undefined, the operation returns Object
|
||
*/
|
||
Symbol ClassTable::lub(Symbol T1, Symbol T2) const {
|
||
if (T1 == T2) return T1;
|
||
auto node1_iter = name_to_node.find(T1);
|
||
auto node2_iter = name_to_node.find(T2);
|
||
/* Return Object if one of the argument is undefined for recovery */
|
||
if (node1_iter == name_to_node.end() || node2_iter == name_to_node.end()) {
|
||
return Object;
|
||
}
|
||
auto node1 = node1_iter->second;
|
||
auto node2 = node2_iter->second;
|
||
ClassGraphNode* node_lower = nullptr;
|
||
ClassGraphNode* node_upper = nullptr;
|
||
/* select the lower (further away to root) node */
|
||
if (node1->depth < node2->depth) {
|
||
node_lower = node2;
|
||
node_upper = node1;
|
||
node2 = node1 = nullptr;
|
||
}
|
||
else {
|
||
node_lower = node1;
|
||
node_upper = node2;
|
||
node2 = node1 = nullptr;
|
||
}
|
||
/* first let the lower node jump up to the same depth with the other node */
|
||
while (node_lower->depth > node_upper->depth) {
|
||
node_lower = node_lower->parent;
|
||
assert(node_lower != nullptr);
|
||
}
|
||
/* then jump together until they meet */
|
||
while (node_lower != node_upper) {
|
||
node_lower = node_lower->parent;
|
||
node_upper = node_upper->parent;
|
||
assert(node_lower != nullptr);
|
||
assert(node_upper != nullptr);
|
||
}
|
||
return node_lower->get_class()->get_name();
|
||
}
|
||
|
||
/*
|
||
* lub operation with SELF_TYPE support
|
||
*/
|
||
Symbol ClassTable::lub(Symbol T1, Symbol T2, Symbol env_class) const {
|
||
if (T1 == SELF_TYPE) T1 = env_class;
|
||
if (T2 == SELF_TYPE) T2 = env_class;
|
||
return lub(T1, T2);
|
||
}
|
||
|
||
/*
|
||
* Wrapper for the Object Symbol Table
|
||
*/
|
||
SymbolTable<Symbol, Entry>* ClassTable::symtab_obj(Class_ class_i) const {
|
||
auto class_id = static_cast<class__class*>(class_i)->get_name();
|
||
auto _iter = _symtab_obj.find(class_id);
|
||
if (_iter == _symtab_obj.end()) return nullptr;
|
||
return _iter->second;
|
||
};
|
||
|
||
/* This is the entry point to the semantic checker.
|
||
|
||
Your checker should do the following two things:
|
||
|
||
1) Check that the program is semantically correct
|
||
2) Decorate the abstract syntax tree with type information
|
||
by setting the `type' field in each Expression node.
|
||
(see `tree.h')
|
||
|
||
You are free to first do 1), make sure you catch all semantic
|
||
errors. Part 2) can be done in a second stage, when you want
|
||
to build mycoolc.
|
||
*/
|
||
void program_class::semant()
|
||
{
|
||
initialize_constants();
|
||
|
||
/* ClassTable constructor may do some semantic analysis */
|
||
ClassTable *classtable = new ClassTable(classes);
|
||
|
||
/* Without confidence in inheritance consistency, abort before type checking*/
|
||
if (classtable->errors()) {
|
||
cerr << "Compilation halted due to static semantic errors." << endl;
|
||
exit(1);
|
||
}
|
||
/* Gather declared types of features for later use */
|
||
classtable->install_all_features();
|
||
/* Top down type checking */
|
||
for (auto class_i : classtable->class_vec()) {
|
||
class_i->semant(classtable);
|
||
}
|
||
|
||
if (classtable->errors()) {
|
||
cerr << "Compilation halted due to static semantic errors." << endl;
|
||
exit(1);
|
||
}
|
||
}
|
||
|
||
void class__class::semant(ClassTable * classtable)
|
||
{
|
||
for (auto i = features->first(); features->more(i); i = features->next(i)) {
|
||
auto cur_feature = features->nth(i);
|
||
cur_feature->semant(this, classtable);
|
||
}
|
||
}
|
||
|
||
void attr_class::semant(Class_ cur_class, ClassTable * classtable)
|
||
{
|
||
/*
|
||
* Attr-No-Init:
|
||
* Check if Declared Type is defined, if so, do nothing because already
|
||
* in AST; else, error report and set its type to Object
|
||
*/
|
||
if (!classtable->class_defined_self(type_decl)) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Class " << type_decl
|
||
<< " of attribute " << name
|
||
<< " is undefined.\n";
|
||
}
|
||
/*
|
||
* Attr-Init:
|
||
* Except checking existence of type_decl, type the init expression and
|
||
* check rhs's type conformance with type_decl
|
||
*/
|
||
if (typeid(*init) != typeid(no_expr_class)) {
|
||
auto rhs_type = init->semant(cur_class, classtable);
|
||
// std::cerr << "attr init rhs type=" << rhs_type << "\n";
|
||
if (!classtable->conform_self(rhs_type, type_decl, cur_class)) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Inferred type " << rhs_type
|
||
<< " of initialization of attribute " << name
|
||
<< " does not conform to declared type " << type_decl
|
||
<< ".\n";
|
||
this->type_decl = Object;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* Method:
|
||
* Check formal types and and them to scope
|
||
* Checks the body of the method in an env in which formals and `self` bounded
|
||
* Check body type conforms to the declared return type
|
||
*/
|
||
void method_class::semant(Class_ cur_class, ClassTable * classtable)
|
||
{
|
||
auto symtab_obj = classtable->symtab_obj(cur_class);
|
||
symtab_obj->enterscope();
|
||
for (auto i = formals->first(); formals->more(i); i = formals->next(i)) {
|
||
auto formal_i = static_cast<formal_class*>(formals->nth(i));
|
||
auto formal_i_name = formal_i->get_name();
|
||
auto formal_i_type = formal_i->get_type_decl();
|
||
if (formal_i_name == self) {
|
||
classtable->semant_error(cur_class->get_filename(), formal_i)
|
||
<< "\'self\' cannot be the name of a formal parameter.\n";
|
||
}
|
||
else if (symtab_obj->probe(formal_i_name) == nullptr) {
|
||
symtab_obj->addid(formal_i_name, formal_i_type);
|
||
}
|
||
else {
|
||
classtable->semant_error(cur_class->get_filename(), formal_i)
|
||
<< "Formal parameter " << formal_i_name
|
||
<< " is multiply defined.\n";
|
||
}
|
||
if (!classtable->class_defined(formal_i_type)) {
|
||
classtable->semant_error(cur_class->get_filename(), formal_i)
|
||
<< "Class " << formal_i_type
|
||
<< " of formal parameter " << formal_i_name
|
||
<< " is undefined.\n";
|
||
}
|
||
}
|
||
auto expr_type = this->expr->semant(cur_class, classtable);
|
||
if (!classtable->class_defined_self(this->return_type)) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Undefined return type " << this->return_type
|
||
<< " in method " << this->name
|
||
<< ".\n";
|
||
}
|
||
symtab_obj->exitscope();
|
||
if (!classtable->conform_self(expr_type, this->return_type, cur_class)) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Inferred return type " << expr_type
|
||
<< " of method " << this->name
|
||
<< " does not conform to declared return type " << this->return_type
|
||
<< ".\n";
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ASSIGN:
|
||
* Given RHS:T' and Id:T, T is the declared type of Id in the env,
|
||
* there is T' <= T. Note that the type of the whole expression is T ′
|
||
*/
|
||
Symbol assign_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
if (this->name == self) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Cannot assign to 'self'.\n";
|
||
}
|
||
auto lhs_type = classtable->symtab_object_lookup_parent(cur_class, this->name);
|
||
if (lhs_type == nullptr) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Assignment to undeclared variable " << this->name << ".\n";
|
||
lhs_type = Object;
|
||
}
|
||
auto rhs_type = this->expr->semant(cur_class, classtable);
|
||
if (!classtable->conform_self(rhs_type, lhs_type, cur_class)) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Type " << rhs_type
|
||
<< " of assigned expression does not conform to declared type " << lhs_type
|
||
<< " of identifier " << this->name
|
||
<< ".\n";
|
||
}
|
||
this->type = rhs_type;
|
||
return this->type;
|
||
}
|
||
|
||
Symbol static_dispatch_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto expr_type = this->expr->semant(cur_class, classtable);
|
||
std::vector<Symbol> _param_type;
|
||
for (auto i = this->actual->first(); this->actual->more(i); i = this->actual->next(i)) {
|
||
auto param_i = this->actual->nth(i);
|
||
auto param_i_type = param_i->semant(cur_class, classtable);
|
||
_param_type.push_back(param_i_type);
|
||
}
|
||
if (!classtable->class_defined(this->type_name)) {
|
||
if (this->type_name == SELF_TYPE){
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Static dispatch to SELF_TYPE.\n";
|
||
}
|
||
else {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Static dispatch on undefined class " << this->type_name
|
||
<< ".\n";
|
||
}
|
||
return (this->type = Object);
|
||
}
|
||
if (!classtable->conform_self(expr_type, this->type_name, cur_class)) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Expression type " << expr_type
|
||
<< " does not conform to declared static dispatch type " << this->type_name
|
||
<< ".\n";
|
||
return (this->type = Object);
|
||
}
|
||
auto cur_method = classtable->symtab_method_lookup_parent(this->type_name, this->name);
|
||
if (cur_method == nullptr) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Static dispatch to undefined method " << this->name
|
||
<< ".\n";
|
||
return (this->type = Object);
|
||
}
|
||
if (actual->len() != cur_method->get_formals()->len()) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Method " << this->name
|
||
<< " called with wrong number of arguments.\n";
|
||
return (this->type = Object);
|
||
}
|
||
auto formals = cur_method->get_formals();
|
||
auto _param_type_iter = _param_type.begin();
|
||
for (auto i = formals->first(); formals->more(i); i = formals->next(i)) {
|
||
auto formal_i = static_cast<formal_class*>(formals->nth(i));
|
||
if (!classtable->conform_self(*_param_type_iter, formal_i->get_type_decl(), cur_class)) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "In call of method " << cur_method->get_name()
|
||
<< ", type " << *_param_type_iter
|
||
<< " of parameter " << formal_i->get_name()
|
||
<< " does not conform to declared type " << formal_i->get_type_decl()
|
||
<< ".\n";
|
||
}
|
||
++_param_type_iter;
|
||
}
|
||
auto return_type = cur_method->get_return_type();
|
||
if (return_type == SELF_TYPE) {
|
||
return_type = expr_type;
|
||
// if expr_type (T_0) is SELF_TYPE, then dispath type just keep it
|
||
}
|
||
return (this->type = return_type);
|
||
}
|
||
|
||
/*
|
||
* Dispatch:
|
||
* Object type check
|
||
* Each of the param exprs get typed
|
||
* Check conformance of each param-formal pair
|
||
*/
|
||
Symbol dispatch_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto expr_type = this->expr->semant(cur_class, classtable); // T_0
|
||
std::vector<Symbol> _param_type; // T_i
|
||
for (auto i = this->actual->first(); this->actual->more(i); i = this->actual->next(i)) {
|
||
auto param_i = this->actual->nth(i);
|
||
auto param_i_type = param_i->semant(cur_class, classtable);
|
||
_param_type.push_back(param_i_type);
|
||
}
|
||
Symbol method_env_class = expr_type; // T_0'
|
||
if (expr_type == SELF_TYPE) {
|
||
method_env_class = classtable->class_to_name(cur_class);
|
||
}
|
||
if (!classtable->class_defined(method_env_class)) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Dispatch on undefined class " << method_env_class
|
||
<< ".\n";
|
||
this->type = Object;
|
||
return this->type;
|
||
}
|
||
auto cur_method = classtable->symtab_method_lookup_parent(method_env_class, this->name);
|
||
if (cur_method == nullptr) {
|
||
if (semant_debug) {
|
||
std::cerr << "lookup method " << this->name << " in class " << method_env_class << "\n";
|
||
}
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Dispatch to undefined method " << this->name
|
||
<< ".\n";
|
||
this->type = Object;
|
||
return Object;
|
||
}
|
||
if (actual->len() != cur_method->get_formals()->len()) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Method " << this->name
|
||
<< " called with wrong number of arguments.\n";
|
||
this->type = Object;
|
||
return Object;
|
||
}
|
||
auto formals = cur_method->get_formals(); // T_i'
|
||
auto _param_type_iter = _param_type.begin();
|
||
for (auto i = formals->first();
|
||
formals->more(i);
|
||
i = formals->next(i), ++_param_type_iter)
|
||
{
|
||
auto formal_i = static_cast<formal_class*>(formals->nth(i));
|
||
auto formal_i_type = formal_i->get_type_decl();
|
||
if (!classtable->conform_self(*_param_type_iter, formal_i_type, cur_class)) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "In call of method " << cur_method->get_name()
|
||
<< ", type " << *_param_type_iter
|
||
<< " of parameter " << formal_i->get_name()
|
||
<< " does not conform to declared type " << formal_i->get_type_decl()
|
||
<< ".\n";
|
||
}
|
||
|
||
}
|
||
auto return_type = cur_method->get_return_type(); // T_n+1
|
||
if (return_type == SELF_TYPE) {
|
||
return_type = expr_type;
|
||
}
|
||
return (this->type = return_type);
|
||
}
|
||
|
||
/*
|
||
* If:
|
||
* Predicate must be Bool type
|
||
* Branches may have any static types
|
||
* Type of the whole expr is `lub(type(e1), type(e2))`
|
||
*/
|
||
Symbol cond_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto pred_type = this->pred->semant(cur_class, classtable);
|
||
if (pred_type != Bool) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Predicate of 'if' does not have type Bool.\n";
|
||
}
|
||
auto else_type = this->else_exp->semant(cur_class, classtable);
|
||
auto then_type = this->then_exp->semant(cur_class, classtable);
|
||
/* TODO: It seems unnecessary to check existence of else/then_type */
|
||
this->type = classtable->lub(else_type, then_type);
|
||
return this->type;
|
||
}
|
||
|
||
/*
|
||
* Loop:
|
||
* Predicate must be Bool
|
||
* Type of the entire loop is always Object
|
||
* Not care about loop body type, though still typing it
|
||
*/
|
||
Symbol loop_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto pred_type = this->pred->semant(cur_class, classtable);
|
||
if (pred_type != Bool) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Loop condition does not have type Bool.\n";
|
||
}
|
||
auto _ = this->body->semant(cur_class, classtable);
|
||
return (this->type = Object);
|
||
}
|
||
|
||
/*
|
||
* Case:
|
||
* For each branch, add xi:Ti to the env. Meanwhile they must have distinct types
|
||
* Type of the entire case is the join of its branches' types
|
||
* Note that SELF_TYPE is not allow here
|
||
*/
|
||
Symbol typcase_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto _ = this->expr->semant(cur_class, classtable);
|
||
std::set<Symbol> _existed_type;
|
||
Symbol entire_type = nullptr;
|
||
for (auto i = this->cases->first(); this->cases->more(i); i = this->cases->next(i)) {
|
||
auto branch_i = static_cast<branch_class*>(this->cases->nth(i));
|
||
auto type_decl_i = branch_i->get_type_decl();
|
||
auto name_i = branch_i->get_name();
|
||
// first check duplicated type
|
||
if (_existed_type.count(type_decl_i) == 0) {
|
||
_existed_type.insert(type_decl_i);
|
||
}
|
||
else {
|
||
classtable->semant_error(cur_class->get_filename(), branch_i)
|
||
<< "Duplicate branch " << type_decl_i
|
||
<< " in case statement.\n";
|
||
}
|
||
/*then check existence of declared type
|
||
The reference semant program's behavior is quite strange here,
|
||
if the type does not exist, it continue to use the undefined type
|
||
for later expr typing
|
||
*/
|
||
if (name_i == self) {
|
||
classtable->semant_error(cur_class->get_filename(), branch_i)
|
||
<< "\'self\' bound in 'case'.\n";
|
||
}
|
||
if (type_decl_i == SELF_TYPE) {
|
||
classtable->semant_error(cur_class->get_filename(), branch_i)
|
||
<< "Identifier " << name_i
|
||
<< " declared with type SELF_TYPE in case branch.\n";
|
||
}
|
||
else if (!classtable->class_defined(type_decl_i)) {
|
||
classtable->semant_error(cur_class->get_filename(), branch_i)
|
||
<< "Class " << type_decl_i
|
||
<< " of case branch is undefined.\n";
|
||
}
|
||
/* next add x[i]:T[i] to scope and type the rhs expr */
|
||
classtable->symtab_obj(cur_class)->enterscope();
|
||
classtable->symtab_obj(cur_class)->addid(name_i, type_decl_i);
|
||
auto branch_expr_type = branch_i->get_expr()->semant(cur_class, classtable);
|
||
classtable->symtab_obj(cur_class)->exitscope();
|
||
/* finally, remember to join expr types for the entire case's type*/
|
||
if (entire_type == nullptr) {
|
||
entire_type = branch_expr_type;
|
||
}
|
||
else {
|
||
entire_type = classtable->lub(entire_type, branch_expr_type);
|
||
}
|
||
}
|
||
return (this->type = entire_type);
|
||
}
|
||
|
||
/*
|
||
* Sequence:
|
||
* Iterate over the expr_list, type each expr and use the type of the last expr
|
||
*/
|
||
Symbol block_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
Symbol type_i;
|
||
for (auto i = this->body->first(); this->body->more(i); i = this->body->next(i)) {
|
||
type_i = this->body->nth(i)->semant(cur_class, classtable);
|
||
}
|
||
return (this->type = type_i);
|
||
}
|
||
|
||
/*
|
||
* Let:
|
||
* Since `let` exprs are nested in AST, we only need to handle only 1 identifier
|
||
* Check existence of declared type
|
||
* If there is an initializer, type it and check conformance with declared type
|
||
* Add the identifier to the scope and type the rhs expr
|
||
* Set the `let` expr's type to the type of rhs
|
||
*/
|
||
Symbol let_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
if (this->identifier == self) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "\'self\' cannot be bound in a \'let\' expression.\n";
|
||
}
|
||
if (!classtable->class_defined_self(this->type_decl)) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Class " << this->type_decl
|
||
<< " of let-bound identifier " << this->identifier
|
||
<< " is undefined.\n";
|
||
}
|
||
if (typeid(*this->init) != typeid(no_expr_class)) {
|
||
auto init_type = this->init->semant(cur_class, classtable);
|
||
if (!classtable->conform_self(init_type, this->type_decl, cur_class)) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Inferred type " << init_type
|
||
<< " of initialization of " << this->identifier
|
||
<< " does not conform to identifier's declared type " << this->type_decl
|
||
<< ".\n";
|
||
}
|
||
}
|
||
classtable->symtab_obj(cur_class)->enterscope();
|
||
classtable->symtab_obj(cur_class)->addid(this->identifier, this->type_decl);
|
||
auto expr_type = this->body->semant(cur_class, classtable);
|
||
classtable->symtab_obj(cur_class)->exitscope();
|
||
return (this->type = expr_type);
|
||
}
|
||
|
||
/*
|
||
* Arith(+):
|
||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Int
|
||
*/
|
||
Symbol plus_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto e1_type = this->e1->semant(cur_class, classtable);
|
||
auto e2_type = this->e2->semant(cur_class, classtable);
|
||
if (e1_type != Int || e2_type != Int) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "non-Int arguments: " << e1_type
|
||
<< " + " << e2_type
|
||
<< "\n";
|
||
}
|
||
return (this->type = Int);
|
||
}
|
||
|
||
/*
|
||
* Arith(-):
|
||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Int
|
||
*/
|
||
Symbol sub_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto e1_type = this->e1->semant(cur_class, classtable);
|
||
auto e2_type = this->e2->semant(cur_class, classtable);
|
||
if (e1_type != Int || e2_type != Int) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "non-Int arguments: " << e1_type
|
||
<< " - " << e2_type
|
||
<< "\n";
|
||
}
|
||
return (this->type = Int);
|
||
}
|
||
|
||
/*
|
||
* Arith(*):
|
||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Int
|
||
*/
|
||
Symbol mul_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto e1_type = this->e1->semant(cur_class, classtable);
|
||
auto e2_type = this->e2->semant(cur_class, classtable);
|
||
if (e1_type != Int || e2_type != Int) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "non-Int arguments: " << e1_type
|
||
<< " * " << e2_type
|
||
<< "\n";
|
||
}
|
||
return (this->type = Int);
|
||
}
|
||
|
||
/*
|
||
* Arith(/):
|
||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Int
|
||
*/
|
||
Symbol divide_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto e1_type = this->e1->semant(cur_class, classtable);
|
||
auto e2_type = this->e2->semant(cur_class, classtable);
|
||
if (e1_type != Int || e2_type != Int) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "non-Int arguments: " << e1_type
|
||
<< " / " << e2_type
|
||
<< "\n";
|
||
}
|
||
return (this->type = Int);
|
||
}
|
||
|
||
/*
|
||
* Neg:
|
||
* Check type(e1) == Int, and whatever the result, derive to Int
|
||
*/
|
||
Symbol neg_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto type_subexpr = this->e1->semant(cur_class, classtable);
|
||
if (type_subexpr != Int) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Argument of '~' has type " << type_subexpr
|
||
<< " instead of Int.\n";
|
||
}
|
||
return (this->type = Int);
|
||
}
|
||
|
||
/*
|
||
* Compare(<):
|
||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Bool
|
||
*/
|
||
Symbol lt_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto e1_type = this->e1->semant(cur_class, classtable);
|
||
auto e2_type = this->e2->semant(cur_class, classtable);
|
||
if (e1_type != Int || e2_type != Int) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "non-Int arguments: " << e1_type
|
||
<< " < " << e2_type
|
||
<< "\n";
|
||
}
|
||
return (this->type = Bool);
|
||
}
|
||
|
||
/*
|
||
* Equal:
|
||
* Any types may be freely compared (compare their Runtime addresses)
|
||
* except for Int, String, Bool. These 3 basic types can only be compared with
|
||
* exprs with the same type (compare their content values)
|
||
*/
|
||
Symbol eq_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto e1_type = this->e1->semant(cur_class, classtable);
|
||
auto e2_type = this->e2->semant(cur_class, classtable);
|
||
if ((e1_type == Int || e2_type == Int
|
||
|| e1_type == Str || e2_type == Str
|
||
|| e1_type == Bool || e2_type == Bool)
|
||
&& e1_type != e2_type) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Illegal comparison with a basic type.\n";
|
||
}
|
||
return (this->type = Bool);
|
||
}
|
||
|
||
/*
|
||
* Compare(<=):
|
||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Bool
|
||
*/
|
||
Symbol leq_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto e1_type = this->e1->semant(cur_class, classtable);
|
||
auto e2_type = this->e2->semant(cur_class, classtable);
|
||
if (e1_type != Int || e2_type != Int) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "non-Int arguments: " << e1_type
|
||
<< " <= " << e2_type
|
||
<< "\n";
|
||
}
|
||
return (this->type = Bool);
|
||
}
|
||
|
||
/*
|
||
* Not:
|
||
* Check type(e1) == Bool, and whatever the result, derive to Bool
|
||
*
|
||
* Note:
|
||
* I still cannot understand why operator `not` is named `comp` in AST
|
||
*/
|
||
Symbol comp_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto type_subexpr = this->e1->semant(cur_class, classtable);
|
||
/* It is safe to compare expr_type with Bool because neither Bool is inheritable
|
||
nor COOL support overloaded operator.
|
||
Thus we dont need to care about SELF_TYPE(self can never be Bool) and no
|
||
other type could use these operators.
|
||
*/
|
||
if (type_subexpr != Bool) {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Argument of 'not' has type " << type_subexpr
|
||
<< " instead of Bool.\n";
|
||
}
|
||
return (this->type = Bool);
|
||
}
|
||
|
||
/*
|
||
* Int:
|
||
* Integer constants derive to Int type
|
||
*/
|
||
Symbol int_const_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
return (this->type = Int);
|
||
}
|
||
|
||
/*
|
||
* True | False:
|
||
* true or false both derive to Bool type
|
||
*/
|
||
Symbol bool_const_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
return (this->type = Bool);
|
||
}
|
||
|
||
/*
|
||
* String
|
||
* String constants derive to String type
|
||
*/
|
||
Symbol string_const_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
return (this->type = Str);
|
||
}
|
||
|
||
/*
|
||
* New:
|
||
* Two cases for new, `SELF_TYPE` and any other
|
||
* But here we dont check `SELF_TYPE[C]` here, if encountered `SELF_TYPE`, just
|
||
* annotate it as `SELF_TYPE` and defer the type check to the conformance part
|
||
* The only check we do here is to make sure its `type_name` exists
|
||
*
|
||
* Warning:
|
||
* there is a static `Symbol` variable also called `type_name`, which conflicts
|
||
* with `new__class::type_name`
|
||
*/
|
||
Symbol new__class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
if (classtable->class_defined_self(this->type_name)) {
|
||
this->type = this->type_name;
|
||
}
|
||
else {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "\'new\' used with undefined class " << this->type_name
|
||
<< ".\n";
|
||
this->type = Object;
|
||
}
|
||
return this->type;
|
||
}
|
||
|
||
/*
|
||
* Isvoid:
|
||
* An `isvoid` test has type `Bool`, dont forget to eval its sub expr
|
||
*/
|
||
Symbol isvoid_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto _ = this->e1->semant(cur_class, classtable);
|
||
return (this->type = Bool);
|
||
}
|
||
|
||
/*
|
||
* NoExpr:
|
||
* `no_expr` always has type `_no_type`
|
||
* However, this class should never be visited during type checking
|
||
*/
|
||
Symbol no_expr_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
assert(0);
|
||
return No_type;
|
||
}
|
||
|
||
/*
|
||
* Var:
|
||
* If the env assigns an identifier ID type T, then ID has type T
|
||
* Otherwise, error report and set type Object
|
||
*/
|
||
Symbol object_class::semant(Class_ cur_class, ClassTable* classtable)
|
||
{
|
||
auto object_type = classtable->symtab_object_lookup_parent(cur_class, this->name);
|
||
if (object_type != nullptr) {
|
||
return (this->type = object_type);
|
||
}
|
||
else {
|
||
classtable->semant_error(cur_class->get_filename(), this)
|
||
<< "Undeclared identifier " << this->name<< ".\n";
|
||
return (this->type = Object);
|
||
}
|
||
}
|