Created
December 16, 2013 16:55
-
-
Save jagt/7990292 to your computer and use it in GitHub Desktop.
cool language semantic check implementation.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// | |
// The following include files must come first. | |
#ifndef COOL_TREE_HANDCODE_H | |
#define COOL_TREE_HANDCODE_H | |
#include <iostream> | |
#include "tree.h" | |
#include "cool.h" | |
#include "stringtab.h" | |
#define yylineno curr_lineno; | |
extern int yylineno; | |
inline Boolean copy_Boolean(Boolean b) {return b; } | |
inline void assert_Boolean(Boolean) {} | |
inline void dump_Boolean(ostream& stream, int padding, Boolean b) | |
{ stream << pad(padding) << (int) b << "\n"; } | |
void dump_Symbol(ostream& stream, int padding, Symbol b); | |
void assert_Symbol(Symbol b); | |
Symbol copy_Symbol(Symbol b); | |
class Program_class; | |
typedef Program_class *Program; | |
class Class__class; | |
typedef Class__class *Class_; | |
class Feature_class; | |
typedef Feature_class *Feature; | |
class Formal_class; | |
typedef Formal_class *Formal; | |
class Expression_class; | |
typedef Expression_class *Expression; | |
class Case_class; | |
typedef Case_class *Case; | |
typedef list_node<Class_> Classes_class; | |
typedef Classes_class *Classes; | |
typedef list_node<Feature> Features_class; | |
typedef Features_class *Features; | |
typedef list_node<Formal> Formals_class; | |
typedef Formals_class *Formals; | |
typedef list_node<Expression> Expressions_class; | |
typedef Expressions_class *Expressions; | |
typedef list_node<Case> Cases_class; | |
typedef Cases_class *Cases; | |
#define Program_EXTRAS \ | |
virtual void semant() = 0; \ | |
virtual void dump_with_types(ostream&, int) = 0; \ | |
virtual void semantic_check(ClassTable &ct) = 0; | |
#define program_EXTRAS \ | |
void semant(); \ | |
void dump_with_types(ostream&, int); \ | |
void semantic_check(ClassTable &ct); | |
#define Class__EXTRAS \ | |
virtual Symbol get_filename() = 0; \ | |
virtual void dump_with_types(ostream&,int) = 0; \ | |
virtual Symbol get_name() = 0; \ | |
virtual Symbol get_parent() = 0; \ | |
virtual void set_parent(Symbol symbol) = 0; \ | |
virtual Features get_features() = 0; \ | |
virtual void semantic_check(ClassTable &ct) = 0; | |
#define class__EXTRAS \ | |
Symbol get_filename() { return filename; } \ | |
void dump_with_types(ostream&,int); \ | |
Symbol get_name(); \ | |
Symbol get_parent(); \ | |
void set_parent(Symbol symbol); \ | |
Features get_features(); \ | |
void semantic_check(ClassTable &ct); | |
#define Feature_EXTRAS \ | |
virtual void dump_with_types(ostream&,int) = 0; \ | |
virtual void semantic_check(ClassTable &ct) = 0; | |
#define Feature_SHARED_EXTRAS \ | |
void dump_with_types(ostream&,int); \ | |
void semantic_check(ClassTable &ct); | |
#define Formal_EXTRAS \ | |
virtual void dump_with_types(ostream&,int) = 0; \ | |
virtual Symbol get_type_decl() = 0; \ | |
virtual void semantic_check(ClassTable &ct) = 0; | |
#define formal_EXTRAS \ | |
void dump_with_types(ostream&,int); \ | |
Symbol get_type_decl(); \ | |
void semantic_check(ClassTable &ct); | |
#define Case_EXTRAS \ | |
virtual void dump_with_types(ostream& ,int) = 0; \ | |
virtual void semantic_check(ClassTable &ct) = 0; \ | |
virtual Symbol get_name() = 0; \ | |
virtual Symbol get_type_decl() = 0; \ | |
virtual Symbol type_check_expr(ClassTable &ct) = 0; | |
#define branch_EXTRAS \ | |
void dump_with_types(ostream& ,int); \ | |
void semantic_check(ClassTable &ct); \ | |
Symbol get_name(); \ | |
Symbol get_type_decl(); \ | |
Symbol type_check_expr(ClassTable &ct); | |
// forward declare ClassTable | |
class ClassTable; | |
#define Expression_EXTRAS \ | |
Symbol type; \ | |
Symbol get_type() { return type; } \ | |
Expression set_type(Symbol s) { type = s; return this; } \ | |
virtual void dump_with_types(ostream&,int) = 0; \ | |
void dump_type(ostream&, int); \ | |
Expression_class() { type = (Symbol) NULL; } \ | |
virtual Symbol type_check(ClassTable &ct) = 0; | |
// FIXME change above one to virtual once all done | |
#define Expression_SHARED_EXTRAS \ | |
void dump_with_types(ostream&,int); \ | |
Symbol type_check(ClassTable &ct); | |
// additional extras that has no stub provided | |
#define method_EXTRAS \ | |
Symbol get_name(); \ | |
Formals get_formals(); \ | |
Symbol get_return_type(); | |
#define attr_EXTRAS \ | |
Symbol get_name(); \ | |
Symbol get_type_decl(); | |
#endif |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdlib.h> | |
#include <stdio.h> | |
#include <stdarg.h> | |
#include <vector> | |
#include <algorithm> | |
#include <set> | |
#include "semant.h" | |
#include "utilities.h" | |
using std::endl; | |
//using std::set; | |
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, | |
No_expr; | |
// | |
// 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"); // HACK! as user land uses 'Str' as a type name, must be aligned with it | |
str_field = idtable.add_string("_str_field"); | |
substr = idtable.add_string("substr"); | |
type_name = idtable.add_string("type_name"); | |
val = idtable.add_string("_val"); | |
No_expr = idtable.add_string("_no_expr"); // !!!! not used, code generation chokes on this | |
} | |
ClassTable::ClassTable(Classes classes) : semant_errors(0) , error_stream(cerr), class_map(), | |
method_dmap(), attr_dmap(), scope(), sigmap() { | |
/* Fill this in */ | |
install_basic_classes(); | |
for(int i = classes->first(); classes->more(i); i = classes->next(i)) { | |
add_class(classes->nth(i)); | |
} | |
// SEMANTIC CHECK - all classes shouldn't inherit from SELF_TYPE | |
for(int i = classes->first(); classes->more(i); i = classes->next(i)) { | |
Class_ c = classes->nth(i); | |
if (c->get_parent() == SELF_TYPE) { | |
semant_error(c); | |
error_stream << "inherit from SELF_TYPE is forbidden." << endl; | |
c->set_parent(Object); // try recovery | |
} | |
} | |
// SEMANTIC CHECK - ensure all classes inherit from existing class | |
for(int i = classes->first(); classes->more(i); i = classes->next(i)) { | |
Class_ c = classes->nth(i); | |
if (class_map.find(c->get_parent()) == class_map.end()) { | |
semant_error(c); | |
error_stream << "inherits from a class not found: " << c->get_parent()->get_string() << endl; | |
c->set_parent(Object); // try recovery | |
} | |
} | |
// Fill in method signatures and attrbute map, from now on must iterate through class_map | |
for(auto iter = class_map.begin(); iter != class_map.end(); ++iter) { | |
Class_ c = iter->second; | |
// initialize inner map | |
sigmap_type &sigmap = method_dmap[c->get_name()]; | |
attrmap_type &attrmap = attr_dmap[c->get_name()]; | |
Features features = c->get_features(); | |
for (int j = features->first(); features->more(j); j = features->next(j)) { | |
Feature f = features->nth(j); | |
Method method = dynamic_cast<Method>(f); | |
if (method != NULL) { | |
MethodSignature &sig = sigmap[method->get_name()]; | |
sig.return_type = method->get_return_type(); | |
Formals formals = method->get_formals(); | |
for (int k = formals->first(); formals->more(k); k = formals->next(k)) { | |
Formal formal = formals->nth(k); | |
sig.arg_types.push_back(formal->get_type_decl()); | |
} | |
} else { | |
Attr attr = dynamic_cast<Attr>(f); assert(attr); | |
attrmap[attr->get_name()] = attr->get_type_decl(); | |
} | |
} | |
} | |
// SEMANTIC CHECK - ensure subclass method signature are the same as the parent one. | |
for (auto iter = class_map.begin(); iter != class_map.end(); ++iter) { | |
Class_ class_ = iter->second; | |
sigmap_type &class_sigmap = method_dmap[iter->first]; | |
for (Symbol parent = class_->get_parent(); parent != No_class; parent = class_map[parent]->get_parent()) { | |
sigmap_type &parent_sigmap = method_dmap[parent]; | |
for (auto class_sigiter = class_sigmap.begin(); class_sigiter != class_sigmap.end(); ++class_sigiter) { | |
MethodSignature class_sig = class_sigiter->second; | |
sigmap_type::iterator parent_sigiter = parent_sigmap.find(class_sigiter->first); | |
if (parent_sigiter != parent_sigmap.end()) { | |
MethodSignature parent_sig = parent_sigiter->second; | |
if ((parent_sig.arg_types != class_sig.arg_types) || (class_sig.return_type != parent_sig.return_type)) { | |
semant_error(class_); | |
error_stream << "class has invalid override method signature: " << class_->get_name()->get_string() | |
<< " method: " << class_sigiter->first->get_string() << endl; | |
continue; | |
} | |
} | |
} | |
} | |
} | |
// SEMANTIC CHECK - ensure attributes have types that are in the class map OR SELFTYPE | |
for (auto iter = attr_dmap.begin(); iter != attr_dmap.end(); ++iter) { | |
//cout << iter->first->get_string() << ":" << endl; | |
auto &attrmap = iter->second; | |
for (auto jiter = attrmap.begin(); jiter != attrmap.end(); ++jiter) { | |
Symbol type_decl = jiter->second; | |
// some cases needs to be handled, like Str is using String as class name | |
if (type_decl != prim_slot && | |
type_decl != SELF_TYPE && class_map.find(type_decl) == class_map.end()) { | |
semant_error(class_map[iter->first]); | |
error_stream << "attribute has invalid type: " << type_decl->get_string() << endl; | |
continue; | |
} | |
} | |
} | |
// error out when have semant error at this stage | |
if (semant_errors) { | |
cerr << "ABORT: class hierachy types has errors. Refuses to continue." << endl; | |
exit(-1); | |
} | |
////////////////////// DEBUG and asserts | |
#if 0 | |
for (map<Symbol, Class_>::iterator iter = class_map.begin(); | |
iter != class_map.end(); ++iter) { | |
cout << iter->first->get_string() << endl; | |
} | |
assert(is_subtype(class_map[Object], class_map[IO])); | |
assert(is_subtype(class_map[Object], class_map[Object])); | |
assert(!(is_subtype(class_map[Int], class_map[IO]))); | |
assert(lup(class_map[Object], class_map[Object]) == Object); | |
assert(lup(class_map[idtable.lookup_string("D")], class_map[idtable.lookup_string("E")]) == idtable.lookup_string("C")); | |
assert(lup(class_map[idtable.lookup_string("D")], class_map[idtable.lookup_string("C")]) == idtable.lookup_string("C")); | |
assert(lup(class_map[idtable.lookup_string("F")], class_map[idtable.lookup_string("E")]) == idtable.lookup_string("C")); | |
assert(lup(class_map[idtable.lookup_string("Main")], class_map[idtable.lookup_string("E")]) == Object); | |
assert(lookup(idtable.lookup_string("F"), idtable.lookup_string("baz"))); | |
// dump method_dmap content | |
cout << "method env dump" << endl; | |
for (auto iter = method_dmap.begin(); iter != method_dmap.end(); ++iter) { | |
cout << iter->first->get_string() << ":" << endl; | |
auto &sigmap = iter->second; | |
for (auto jiter = sigmap.begin(); jiter != sigmap.end(); ++jiter) { | |
cout << jiter->first->get_string() << " ( "; | |
auto &sig = jiter->second; | |
for (auto veciter = sig.arg_types.begin(); veciter != sig.arg_types.end(); ++veciter) { | |
cout << (*veciter)->get_string() << " "; | |
} | |
cout << " ) :" << sig.return_type->get_string() << endl; | |
} | |
} | |
// dump attr_dmap content | |
cout << "attr env dump" << endl; | |
for (auto iter = attr_dmap.begin(); iter != attr_dmap.end(); ++iter) { | |
cout << iter->first->get_string() << ":" << endl; | |
auto &attrmap = iter->second; | |
for (auto jiter = attrmap.begin(); jiter != attrmap.end(); ++jiter) { | |
cout << jiter->first->get_string() << " : " << jiter->second->get_string() << endl; | |
} | |
} | |
////////////////////// END OF DEBUG and asserts | |
#endif | |
} | |
void ClassTable::add_class(Class_ c) { | |
class_map[c->get_name()] = c; | |
} | |
bool ClassTable::is_subtype(Symbol parent_sym, Symbol sub_sym) { | |
// handle self type using cur_class | |
if (parent_sym == No_type || sub_sym == No_type) return false; | |
if (parent_sym == sub_sym) return true; | |
if (parent_sym == SELF_TYPE) return false; | |
if (sub_sym == SELF_TYPE) { | |
return is_subtype(class_map[parent_sym], cur_class); | |
} | |
return is_subtype(class_map[parent_sym], class_map[sub_sym]); | |
} | |
bool ClassTable::is_subtype(Class_ parent, Class_ sub) { | |
Symbol sub_parent = sub->get_name(); // include sub's type so 'Object is_subtype of Object' | |
while (sub_parent != No_class) { | |
if (sub_parent == parent->get_name()) { | |
return true; | |
} | |
sub_parent = class_map[sub_parent]->get_parent(); | |
} | |
return false; | |
} | |
Symbol ClassTable::lup(Symbol p_sym, Symbol q_sym) { | |
// handle self type using cur_class | |
if (p_sym == No_type || q_sym == No_type) return No_type; | |
if (p_sym == q_sym) return p_sym; | |
if (p_sym == SELF_TYPE) { | |
return lup(cur_class, class_map[q_sym]); | |
} else if (q_sym == SELF_TYPE) { | |
return lup(class_map[p_sym], cur_class); | |
} | |
return lup(class_map[p_sym], class_map[q_sym]); | |
} | |
Symbol ClassTable::lup(Class_ p, Class_ q) { | |
// find the way up to Object and count how far from tail it can go | |
vector<Symbol> p_supers, q_supers; | |
Symbol p_symbol = p->get_name(), | |
q_symbol = q->get_name(); | |
while (p_symbol != No_class) { | |
p_supers.push_back(p_symbol); | |
p_symbol = class_map[p_symbol]->get_parent(); | |
} | |
while (q_symbol != No_class) { | |
q_supers.push_back(q_symbol); | |
q_symbol = class_map[q_symbol]->get_parent(); | |
} | |
auto p_riter = p_supers.rbegin(), q_riter = q_supers.rbegin(); | |
while(*p_riter == *q_riter) { | |
if ( (p_riter + 1 == p_supers.rend()) | |
|| (q_riter + 1 == q_supers.rend()) | |
|| (*(p_riter + 1) != *(q_riter + 1)) | |
) { | |
return *p_riter; | |
} else { | |
++p_riter; | |
++q_riter; | |
} | |
} | |
return No_type; | |
} | |
MethodSignature* ClassTable::lookup(Symbol class_sym, Symbol name) { | |
// when looking up for SELF_TYPE, just use the current class | |
if (class_sym == SELF_TYPE) { | |
class_sym = cur_class->get_name(); | |
} | |
if (class_map.find(class_sym) == class_map.end()) { | |
return NULL; | |
} | |
for (Symbol class_ = class_sym; class_ != No_class; class_ = class_map[class_]->get_parent()) { | |
sigmap_type &sigmap = method_dmap[class_]; | |
if (sigmap.find(name) != sigmap.end()) { | |
return &sigmap[name]; | |
} | |
} | |
return NULL; | |
} | |
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); | |
// add builtin classes | |
add_class(Object_class); | |
add_class(IO_class); | |
add_class(Int_class); | |
add_class(Bool_class); | |
add_class(Str_class); | |
} | |
//////////////////////////////////////////////////////////////////// | |
// | |
// 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; | |
} | |
// easy err for easier calling | |
ostream& ClassTable::err(tree_node *t) | |
{ | |
semant_error(cur_class->get_filename(), t); | |
return error_stream; | |
} | |
/////////////////////////////////////////////////////////////////// | |
// | |
// additional node class methods | |
// | |
/////////////////////////////////////////////////////////////////// | |
// additional getter/setters to construct type environment | |
Symbol class__class::get_name() { | |
return name; | |
} | |
Symbol class__class::get_parent() { | |
return parent; | |
} | |
void class__class::set_parent(Symbol symbol) { | |
parent = symbol; | |
} | |
Features class__class::get_features() { | |
return features; | |
} | |
Symbol method_class::get_name() { | |
return name; | |
} | |
Formals method_class::get_formals() { | |
return formals; | |
} | |
Symbol method_class::get_return_type() { | |
return return_type; | |
} | |
Symbol formal_class::get_type_decl() { | |
return type_decl; | |
} | |
Symbol attr_class::get_name() { | |
return name; | |
} | |
Symbol attr_class::get_type_decl() { | |
return type_decl; | |
} | |
Symbol branch_class::get_name() { | |
return name; | |
} | |
Symbol branch_class::get_type_decl() { | |
return type_decl; | |
} | |
Symbol branch_class::type_check_expr(ClassTable &ct) { | |
return expr->type_check(ct); | |
} | |
// non expression semantic_checks for entry point to expression type_checks | |
void program_class::semantic_check(ClassTable &ct) { | |
for(int i = classes->first(); classes->more(i); i = classes->next(i)) { | |
// add all attributes and methods from the top of the hierachy down to class | |
Class_ class_ = classes->nth(i); | |
vector<Symbol> parents; | |
parents.push_back(class_->get_name()); | |
for (Symbol p = class_->get_parent(); p != No_class; p = ct.class_map[p]->get_parent()) { | |
parents.push_back(p); | |
} | |
for (auto iter = parents.rbegin(); iter != parents.rend(); ++iter) { | |
attrmap_type &attrmap = ct.attr_dmap[*iter]; | |
ct.scope.enterscope(); | |
for (auto jiter = attrmap.begin(); jiter != attrmap.end(); ++jiter) { | |
ct.scope.addid(jiter->first, &(jiter->second)); | |
} | |
// construct a flat sigmap to for type checking. | |
sigmap_type &class_sigmap = ct.method_dmap[*iter]; | |
for (auto kiter = class_sigmap.begin(); kiter != class_sigmap.end(); ++kiter) { | |
if (ct.sigmap.find(kiter->first) == ct.sigmap.end()) { | |
ct.sigmap[kiter->first] = kiter->second; | |
} | |
} | |
} | |
// check with proper scope | |
ct.cur_class = class_; | |
class_->semantic_check(ct); | |
// pop all all scopes and ready for the next one. | |
for (size_t ix = 0u; ix != parents.size(); ++ix) { | |
ct.scope.exitscope(); | |
} | |
ct.sigmap.clear(); | |
} | |
} | |
void class__class::semantic_check(ClassTable &ct) { | |
for(int i = features->first(); features->more(i); i = features->next(i)) { | |
features->nth(i)->semantic_check(ct); | |
} | |
} | |
void method_class::semantic_check(ClassTable &ct) { | |
// add all formals into scope then check the return value | |
ct.scope.enterscope(); // ! enter scope for each method | |
for(int i = formals->first(); formals->more(i); i = formals->next(i)) { | |
formals->nth(i)->semantic_check(ct); | |
} | |
ct.scope.addid(self, &SELF_TYPE); | |
Symbol expr_type = expr->type_check(ct); | |
// SEMANTIC CHECK - expr_type <= return declare type | |
if (!ct.is_subtype(return_type, expr_type)) { | |
ct.err(this) << "return type declared as " << return_type->get_string() | |
<< " but expr type infered as " << expr_type->get_string() << "." << endl; | |
expr->set_type(return_type); // try correction | |
} | |
ct.scope.exitscope(); // clean up upon exiting | |
} | |
void formal_class::semantic_check(ClassTable &ct) { | |
// SEMANTIC CHECK - formal types needs to be valid. | |
if (type_decl == SELF_TYPE) { | |
ct.err(this) << "can't have SELF_TYPE in formal." << endl; | |
type_decl = ct.cur_class->get_name(); | |
} else if (ct.class_map.find(type_decl) == ct.class_map.end()) { | |
ct.err(this) << "can't have SELF_TYPE in formal." << endl; | |
type_decl = Object; | |
} | |
ct.scope.addid(name, &type_decl); | |
} | |
void attr_class::semantic_check(ClassTable &ct) { | |
ct.scope.enterscope(); | |
ct.scope.addid(self, &SELF_TYPE); | |
if (dynamic_cast<no_expr_class*>(init) == NULL) { | |
Symbol init_type = init->type_check(ct); | |
// SEMANTIC CHECK - check init_type <= type_decl, self type is implicitly handled | |
if (!ct.is_subtype(type_decl, init_type)) { | |
ct.err(this) << "invalid init expr type on attr:" << name->get_string() << endl; | |
} | |
} | |
ct.scope.exitscope(); | |
} | |
void branch_class::semantic_check(ClassTable &ct) { | |
// branch checks nothing. provide getters for typcase to check | |
} | |
// expression type_checks | |
// simple expressions that can get type quite easily | |
// defined in the sequence of cool-tree.aps | |
Symbol assign_class::type_check(ClassTable &ct) { | |
Symbol *lhs_type_ptr = ct.scope.lookup(name); | |
if (lhs_type_ptr == NULL) { | |
ct.err(this) << "assigning to non existent object: " << name->get_string() << endl; | |
return type = Object; | |
} | |
Symbol expr_type = expr->type_check(ct); | |
if (!ct.is_subtype(*lhs_type_ptr, expr_type)) { | |
ct.err(this) << "assigning type violation: " << (*lhs_type_ptr)->get_string() | |
<< " assigned with type " << expr_type->get_string() << endl; | |
} | |
return type = expr_type; | |
} | |
// FIXME how to easily share code between classes... | |
Symbol static_dispatch_class::type_check(ClassTable &ct) { | |
Symbol expr_type = expr->type_check(ct); | |
size_t arg_cnt = actual->len(); | |
vector<Symbol> arg_types(arg_cnt); | |
for(int i = actual->first(); actual->more(i); i = actual->next(i)) { | |
arg_types[i] = actual->nth(i)->type_check(ct); | |
} | |
// only difference against normal dispatch is to test for explicity given type | |
if (!ct.is_subtype(type_name, expr_type)) { | |
ct.err(this) << "invalid static dispatch: " << expr_type->get_string() | |
<< " trying to dispatch on " << type_name->get_string() << endl; | |
return type = Object; | |
} | |
// check argument types | |
MethodSignature *sig_ptr = ct.lookup(expr_type, name); | |
if (sig_ptr == NULL) { | |
ct.err(this) << "method look up failed: " << expr_type->get_string() | |
<< " looking for " << name->get_string() << endl; | |
return type = Object; | |
} | |
if (sig_ptr->arg_types.size() != arg_cnt) { | |
ct.err(this) << "argument count doesn't match: " << name->get_string() | |
<< " expects " << sig_ptr->arg_types.size() << " get " << arg_cnt << endl; | |
return type = Object; | |
} | |
// check on each argument type | |
for (size_t i = 0; i != arg_cnt; ++i) { | |
if (!ct.is_subtype(sig_ptr->arg_types[i], arg_types[i])) { | |
ct.err(this) << "argument type does not match: " << arg_types[i]->get_string() | |
<< " is not inherited from " << sig_ptr->arg_types[i] << endl; | |
} | |
} | |
if (sig_ptr->return_type == SELF_TYPE) { | |
return type = expr_type; | |
} else { | |
return type = sig_ptr->return_type; | |
} | |
} | |
Symbol dispatch_class::type_check(ClassTable &ct) { | |
Symbol expr_type = expr->type_check(ct); | |
size_t arg_cnt = actual->len(); | |
vector<Symbol> arg_types(arg_cnt); | |
for(int i = actual->first(); actual->more(i); i = actual->next(i)) { | |
arg_types[i] = actual->nth(i)->type_check(ct); | |
} | |
// check argument types | |
MethodSignature *sig_ptr = ct.lookup(expr_type, name); | |
if (sig_ptr == NULL) { | |
ct.err(this) << "method look up failed: " << expr_type->get_string() | |
<< " looking for " << name->get_string() << endl; | |
return type = Object; | |
} | |
if (sig_ptr->arg_types.size() != arg_cnt) { | |
ct.err(this) << "argument count doesn't match: " << name->get_string() | |
<< " expects " << sig_ptr->arg_types.size() << " get " << arg_cnt << endl; | |
return type = Object; | |
} | |
// check on each argument type | |
for (size_t i = 0; i != arg_cnt; ++i) { | |
if (!ct.is_subtype(sig_ptr->arg_types[i], arg_types[i])) { | |
ct.err(this) << "argument type does not match: " << arg_types[i]->get_string() | |
<< " is not inherited from " << sig_ptr->arg_types[i] << endl; | |
} | |
} | |
if (sig_ptr->return_type == SELF_TYPE) { | |
return type = expr_type; | |
} else { | |
return type = sig_ptr->return_type; | |
} | |
} | |
Symbol cond_class::type_check(ClassTable &ct) { | |
Symbol pred_type = pred->type_check(ct); | |
if (pred_type != Bool) { | |
ct.err(this) << "predicate in if has type: " << pred_type->get_string() << endl; | |
return type = Object; | |
} | |
Symbol then_type = then_exp->type_check(ct), | |
else_type = else_exp->type_check(ct); | |
Symbol lup = ct.lup(then_type, else_type); | |
if (lup == No_type) { | |
ct.err(this) << "then and else has no lup: " << then_type->get_string() | |
<< " ," << else_type->get_string() << endl; | |
return type = Object; | |
} | |
return type = lup; | |
} | |
Symbol loop_class::type_check(ClassTable &ct) { | |
Symbol pred_type = pred->type_check(ct); | |
if (pred_type != Bool) { | |
ct.err(this) << "predicate in while has type: " << pred_type->get_string() << endl; | |
return type = Object; | |
} | |
body->type_check(ct); // discard body's type | |
return type = Object; // use this meaning less type to avoid user use it | |
} | |
Symbol typcase_class::type_check(ClassTable &ct) { | |
// typecheck all the branches and | |
// 1. calculate the lup of all | |
// 2. ensure cases have different types | |
// discard the type of expr | |
expr->type_check(ct); | |
if (cases->len() == 0) { | |
ct.err(this) << "case contains no branches" << endl; | |
return Object; | |
} | |
Symbol final_type = NULL; | |
// !!! here's an interesting problem, can't use bare 'set', must use std::set | |
// or parse will fail. need to figure this out later | |
std::set<Symbol> case_type_set; | |
for(int i = cases->first(); cases->more(i); i = cases->next(i)) { | |
Case case_ = cases->nth(i); | |
Symbol case_type = case_->get_type_decl(); | |
if (case_type_set.find(case_type) != case_type_set.end()) { | |
ct.err(this) << "case branch has duplicated type: " << case_type->get_string() << endl; | |
} else { | |
case_type_set.insert(case_type); | |
} | |
// add branch name to scope | |
ct.scope.enterscope(); | |
ct.scope.addid(case_->get_name(), &case_type); | |
Symbol case_expr_type = case_->type_check_expr(ct); | |
ct.scope.exitscope(); | |
if (final_type == NULL) { | |
final_type = case_expr_type; | |
} else { | |
final_type = ct.lup(final_type, case_expr_type); | |
if (final_type == No_type) { | |
ct.err(this) << "case body can't find a common lup: " << final_type->get_string() | |
<< " with " << case_expr_type->get_string() << endl; | |
return type = Object; | |
} | |
} | |
} | |
return type = final_type; | |
} | |
Symbol block_class::type_check(ClassTable &ct) { | |
Symbol last; | |
for(int i = body->first(); body->more(i); i = body->next(i)) { | |
last = body->nth(i)->type_check(ct); | |
} | |
return type = last; | |
} | |
// notice that this part differs from the manual, as no multiple lets is allowed in | |
// this cool implementation. | |
Symbol let_class::type_check(ClassTable &ct) { | |
// evaluate init before enterscope | |
Symbol init_type = init->type_check(ct); | |
// handle no init | |
if (init_type != No_type) { | |
if (!ct.is_subtype(type_decl, init_type)) { | |
ct.err(this) << "init in let has incompatible type: " << init_type->get_string() | |
<< " initing " << type_decl << endl; | |
return type = Object; | |
} | |
} | |
Symbol body_type; | |
ct.scope.enterscope(); | |
ct.scope.addid(identifier, &type_decl); | |
body_type = body->type_check(ct); | |
ct.scope.exitscope(); | |
return type = body_type; | |
} | |
Symbol plus_class::type_check(ClassTable &ct) { | |
if (e1->type_check(ct) != Int) { | |
ct.err(this) << "lhs of plus is not int" << endl; | |
} | |
if (e2->type_check(ct) != Int) { | |
ct.err(this) << "rhs of plus is not int" << endl; | |
} | |
return type = Int; | |
} | |
Symbol sub_class::type_check(ClassTable &ct) { | |
if (e1->type_check(ct) != Int) { | |
ct.err(this) << "lhs of substract is not int" << endl; | |
} | |
if (e2->type_check(ct) != Int) { | |
ct.err(this) << "rhs of substract is not int" << endl; | |
} | |
return type = Int; | |
} | |
Symbol mul_class::type_check(ClassTable &ct) { | |
if (e1->type_check(ct) != Int) { | |
ct.err(this) << "lhs of mul is not int" << endl; | |
} | |
if (e2->type_check(ct) != Int) { | |
ct.err(this) << "rhs of mul is not int" << endl; | |
} | |
return type = Int; | |
} | |
Symbol divide_class::type_check(ClassTable &ct) { | |
if (e1->type_check(ct) != Int) { | |
ct.err(this) << "lhs of divide is not int" << endl; | |
} | |
if (e2->type_check(ct) != Int) { | |
ct.err(this) << "rhs of divide is not int" << endl; | |
} | |
return type = Int; | |
} | |
Symbol neg_class::type_check(ClassTable &ct) { | |
if (e1->type_check(ct) != Int) { | |
ct.err(this) << "neg on non Int type." << endl; | |
} | |
return type = Int; | |
} | |
Symbol lt_class::type_check(ClassTable &ct) { | |
if (e1->type_check(ct) != Int) { | |
ct.err(this) << "lhs of less than is not int" << endl; | |
} | |
if (e2->type_check(ct) != Int) { | |
ct.err(this) << "rhs of less than is not int" << endl; | |
} | |
return type = Bool; | |
} | |
Symbol eq_class::type_check(ClassTable &ct) { | |
Symbol lhs_type = e1->type_check(ct); | |
Symbol rhs_type = e2->type_check(ct); | |
if (lhs_type == Int || lhs_type == Str || lhs_type == Bool) { | |
if (lhs_type != rhs_type) { | |
ct.err(this) << "primitive types only can do equal check on same type: " | |
<< lhs_type->get_string() << " with " << rhs_type->get_string() << endl; | |
} | |
} | |
return type = Bool; | |
} | |
Symbol leq_class::type_check(ClassTable &ct) { | |
if (e1->type_check(ct) != Int) { | |
ct.err(this) << "lhs of less equal than is not int" << endl; | |
} | |
if (e2->type_check(ct) != Int) { | |
ct.err(this) << "rhs of less equal than is not int" << endl; | |
} | |
return type = Bool; | |
} | |
Symbol comp_class::type_check(ClassTable &ct) { | |
if (e1->type_check(ct) != Bool) { | |
ct.err(this) << "not on non bool type." << endl; | |
} | |
return type = Bool; | |
} | |
Symbol int_const_class::type_check(ClassTable &ct) { | |
return type = Int; | |
} | |
Symbol bool_const_class::type_check(ClassTable &ct) { | |
return type = Bool; | |
} | |
Symbol string_const_class::type_check(ClassTable &ct) { | |
return type = Str; | |
} | |
Symbol new__class::type_check(ClassTable &ct) { | |
// notice that actually we don't have pure SELF_TYPE, | |
// only have SELF_TYPEc, so directly returning this is correct. | |
return type = type_name; | |
} | |
Symbol isvoid_class::type_check(ClassTable &ct) { | |
return type = Bool; | |
} | |
Symbol no_expr_class::type_check(ClassTable &ct) { | |
//return type = No_expr; // notice that this is one returns a special symbol just to indicate no_expr | |
return type = No_type; // notice that this is one returns a special symbol just to indicate no_expr | |
} | |
Symbol object_class::type_check(ClassTable &ct) { | |
Symbol *type_ptr = ct.scope.lookup(name); | |
if (type_ptr == NULL) { | |
ct.err(this) << "referencing a object that is not in any scope: " | |
<< name->get_string() << endl; | |
return type = Object; | |
} | |
return type = *type_ptr; | |
} | |
/* 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); | |
semantic_check(*classtable); | |
/* some semantic analysis code may go here */ | |
if (classtable->errors()) { | |
cerr << "Compilation halted due to static semantic errors." << endl; | |
exit(1); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#ifndef SEMANT_H_ | |
#define SEMANT_H_ | |
#include <assert.h> | |
#include <iostream> | |
#include <map> | |
#include "cool-tree.h" | |
#include "stringtab.h" | |
#include "symtab.h" | |
#include "list.h" | |
#define TRUE 1 | |
#define FALSE 0 | |
using std::vector; | |
using std::map; | |
// additional node typedefs, as originally phylum has class typedefs | |
typedef method_class *Method; | |
typedef attr_class *Attr; | |
class ClassTable; | |
typedef ClassTable *ClassTableP; | |
// This is a structure that may be used to contain the semantic | |
// information such as the inheritance graph. You may use it or not as | |
// you like: it is only here to provide a container for the supplied | |
// methods. | |
struct MethodSignature { | |
vector<Symbol> arg_types; | |
Symbol return_type; | |
}; | |
typedef map<Symbol, MethodSignature> sigmap_type; | |
typedef map<Symbol, Symbol> attrmap_type; | |
typedef SymbolTable<Symbol, Symbol> scope_type; | |
// to keep the code compact, ClassTable itself is used also as the environment. | |
class ClassTable { | |
private: | |
int semant_errors; | |
void install_basic_classes(); | |
ostream& error_stream; | |
public: | |
ClassTable(Classes); | |
int errors() { return semant_errors; } | |
ostream& semant_error(); | |
ostream& semant_error(Class_ c); | |
ostream& semant_error(Symbol filename, tree_node *t); | |
ostream& err(tree_node *t); | |
// Class environment related methods | |
// add new class to class_map | |
void add_class(Class_ c); | |
// is sub a subtype of parent | |
bool is_subtype(Symbol parent_sym, Symbol sub_sym); | |
bool is_subtype(Class_ parent, Class_ sub); | |
// least upper parent | |
Symbol lup(Symbol p_sym, Symbol q_sym); | |
Symbol lup(Class_ p, Class_ q); | |
// look up method signature | |
MethodSignature* lookup(Symbol class_sym, Symbol name); | |
// class map, no need for a real tree as parent symbol is stored in Class_ | |
// as idtable stores only single copy of strings, using char* as map key should | |
// be fine. | |
map<Symbol, Class_> class_map; | |
// class-> (method_name -> MethodSignature) double map, as Method environment | |
map< Symbol, sigmap_type > method_dmap; | |
// class-> (attr_name -> symbol) double map, as static Attr environment | |
map< Symbol, attrmap_type > attr_dmap; | |
// scope map object | |
scope_type scope; | |
// static method signature map | |
sigmap_type sigmap; | |
// current class | |
Class_ cur_class; | |
}; | |
#endif | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment