CS143-Lab/assignments/PA4/semant.cc
2023-03-24 15:46:11 +00:00

381 lines
14 KiB
C++

#include <cassert>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <utility>
#include <vector>
#include "cool-tree.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*/
std::vector<class__class* > class_vec;
for (auto i = classes->first(); classes->more(i); i = classes->next(i)) {
auto class_i = static_cast<class__class*>(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 {
// 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 */
auto sym_Bool = idtable.lookup_string("Bool");
auto sym_Int = idtable.lookup_string("Int");
auto sym_String = idtable.lookup_string("String");
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 == sym_Bool || sym_parent == sym_Int || sym_parent == sym_String) {
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();
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::cerr<< "Class Inheritance Analysis done.\n";
class_root->traverse(std::cout, 0);
}
/* fourth scan: gather attr and method definition */
for (auto class_i : class_vec) {
auto features_i = class_i->get_features();
auto name_i = class_i->get_name();
symtab_met[name_i] = new SymbolTable<Symbol, method_class>;
symtab_obj[name_i] = new SymbolTable<Symbol, Entry>;
symtab_met[name_i]->enterscope();
symtab_obj[name_i]->enterscope();
for (auto j = features_i->first(); features_i->more(j); j = features_i->next(j)) {
auto feature_j = features_i->nth(j);
if (typeid(*feature_j) == typeid(attr_class)) {
auto attr_j = static_cast<attr_class*>(feature_j);
symtab_obj[name_i]->addid(attr_j->get_name(), attr_j->get_type_decl());
}
else if (typeid(*feature_j) == typeid(method_class)) {
auto method_j = static_cast<method_class*>(feature_j);
symtab_met[name_i]->addid(method_j->get_name(), method_j);
}
else assert(0);
}
}
if (semant_debug) {
std::cerr << "Attr & Methods Collection done.\n";
for (auto class_i : class_vec) {
auto name_i = class_i->get_name();
std::cerr << "M(" << name_i << ") =";
symtab_met[name_i]->dump();
std::cerr << "O(" << name_i << ") =";
symtab_obj[name_i]->dump();
std::cerr << "--------\n\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(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;
}
/* 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);
}
for (auto i = classes->first(); classes->more(i); i = classes->next(i)) {
auto class_i = static_cast<class__class*>(classes->nth(i));
class_i->semant();
}
/* some semantic analysis code may go here */
if (classtable->errors()) {
cerr << "Compilation halted due to static semantic errors." << endl;
exit(1);
}
}
void class__class::semant()
{
}