buggy prototype
This commit is contained in:
parent
1b3f14ab34
commit
c5bbdde30b
66
assignments/PA4/bad_expr.cl
Normal file
66
assignments/PA4/bad_expr.cl
Normal file
@ -0,0 +1,66 @@
|
||||
class ErrTest {
|
||||
errCase(): Bool {
|
||||
{
|
||||
case (new NOEXIST) of
|
||||
x1: Bool => true;
|
||||
x2: Int => x2 <- 10;
|
||||
x3: NOEXIST => x3 + 10;
|
||||
x3: NOEXIST => new Object;
|
||||
x4: Int => x4 + true;
|
||||
x4: Object => 111;
|
||||
x5: SELF_TYPE => self;
|
||||
x5: SELF_TYPE => self;
|
||||
esac;
|
||||
}
|
||||
};
|
||||
errArith(): Object {
|
||||
{
|
||||
new NOEXIST;
|
||||
true + 1;
|
||||
1 + true;
|
||||
(1 * false) + (true * 2);
|
||||
1 + "" + true * true;
|
||||
(~(not 2)) + 2;
|
||||
1 = false;
|
||||
not(~(not 2)<(not (true * true))) ;
|
||||
}
|
||||
};
|
||||
errLet(): Object {
|
||||
let x1:NOEXIST, x2:Int<-10, x3:NOEXIST <- x1, x4:Bool <- 10 in {
|
||||
x1 <- attr1 + x1;
|
||||
}
|
||||
};
|
||||
attr1: NOEXIST;
|
||||
};
|
||||
|
||||
class ErrMethods {
|
||||
errMethod(x1:Int, x1:Bool) : Bool {
|
||||
x1
|
||||
};
|
||||
errMethod2(x1:Int, x2:NOEXIST) : Bool {
|
||||
x2 + x1
|
||||
};
|
||||
};
|
||||
|
||||
class Main {
|
||||
attr1: Bool <- (new Err5).hello();
|
||||
attr2: Int <- (new Err5).bye();
|
||||
self(): SELF_TYPE { new SELF_TYPE };
|
||||
main(): Object {
|
||||
{
|
||||
let x:Int in {
|
||||
x1 <- x + 1;
|
||||
x <- false;
|
||||
x <- x1;
|
||||
};
|
||||
while (1 + 1) loop
|
||||
x1 <- true
|
||||
pool;
|
||||
if (1 + 1) then 2 else 3 fi;
|
||||
(new ErrMethods).errMethod(true + false);
|
||||
(new ErrMethods).errMethod2(false, false);
|
||||
(new ErrMethods).errMethod2(true + false);
|
||||
attr2@Main.hello();
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -57,7 +57,6 @@ class Err6Sub inherits Err6Base {
|
||||
};
|
||||
*)
|
||||
|
||||
|
||||
class DummyMain {
|
||||
la: Bool <- 20;
|
||||
ala: Bool;
|
||||
@ -74,7 +73,13 @@ class Main inherits DummyMain {
|
||||
{
|
||||
let x:Int in {
|
||||
x1 <- x + 1;
|
||||
x <- false;
|
||||
x <- x1;
|
||||
};
|
||||
while (1 + 1) loop
|
||||
x1 <- true
|
||||
pool;
|
||||
if (1 + 1) then 2 else 3 fi;
|
||||
(new Err4).hello(true);
|
||||
(new Err4).hello();
|
||||
}
|
||||
|
||||
@ -279,6 +279,10 @@ public:
|
||||
Case copy_Case();
|
||||
void dump(ostream& stream, int n);
|
||||
|
||||
Symbol get_name() const { return name; };
|
||||
Symbol get_type_decl() const { return type_decl; };
|
||||
Expression get_expr() const { return expr; };
|
||||
|
||||
#ifdef Case_SHARED_EXTRAS
|
||||
Case_SHARED_EXTRAS
|
||||
#endif
|
||||
|
||||
@ -2,6 +2,7 @@
|
||||
|
||||
#include <cassert>
|
||||
#include <iostream>
|
||||
#include <set>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
@ -137,9 +138,9 @@ ClassTable::ClassTable(Classes classes) : semant_errors(0) , error_stream(cerr)
|
||||
// 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();
|
||||
class_root->traverse_reachable(0);
|
||||
for (auto i : name_to_node) {
|
||||
if (!i.second->reachable) {
|
||||
if (i.second->depth == 0) {
|
||||
semant_error(i.second->get_class()) << "Class `" << i.first
|
||||
<< "` or its ancestor, is involved in an inheritance cycle.\n";
|
||||
}
|
||||
@ -493,6 +494,42 @@ bool ClassTable::class_exist(Symbol class_id) const {
|
||||
return name_to_node.find(class_id) != name_to_node.end();
|
||||
}
|
||||
|
||||
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);
|
||||
assert(node1_iter != name_to_node.end());
|
||||
assert(node2_iter != name_to_node.end());
|
||||
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();
|
||||
}
|
||||
|
||||
/* This is the entry point to the semantic checker.
|
||||
|
||||
Your checker should do the following two things:
|
||||
@ -524,11 +561,11 @@ void program_class::semant()
|
||||
for (auto class_i : classtable->class_vec()) {
|
||||
// before we enter a class, add self:SELF_TYPE to its scope
|
||||
// TODO: Maybe integrate this into last scan
|
||||
classtable->symtab_obj(class_i->get_name())->enterscope();
|
||||
classtable->symtab_obj(class_i->get_name())->addid(self, SELF_TYPE);
|
||||
classtable->symtab_obj(class_i)->enterscope();
|
||||
classtable->symtab_obj(class_i)->addid(self, SELF_TYPE);
|
||||
class_i->semant(classtable);
|
||||
// leave scope
|
||||
classtable->symtab_obj(class_i->get_name())->exitscope();
|
||||
classtable->symtab_obj(class_i)->exitscope();
|
||||
}
|
||||
|
||||
if (classtable->errors()) {
|
||||
@ -566,10 +603,11 @@ void attr_class::semant(Class_ cur_class, ClassTable * classtable)
|
||||
*/
|
||||
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(rhs_type, type_decl)) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< "Inferred type " << rhs_type
|
||||
<< " of initialization of attribute " << name
|
||||
<< " of initialization of attribute " << name
|
||||
<< " does not conform to declared type " << type_decl
|
||||
<< " .\n";
|
||||
this->type_decl = Object;
|
||||
@ -579,125 +617,583 @@ void attr_class::semant(Class_ cur_class, ClassTable * classtable)
|
||||
|
||||
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 (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_exist(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);
|
||||
symtab_obj->exitscope();
|
||||
if (!classtable->conform(expr_type, this->return_type)) {
|
||||
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)
|
||||
{
|
||||
return Object;
|
||||
auto lhs_type = classtable->symtab_obj(cur_class)->lookup(name);
|
||||
if (lhs_type == nullptr) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< "Assignment to undeclared variable " << name << "\n";
|
||||
lhs_type = Object;
|
||||
}
|
||||
auto rhs_type = this->expr->semant(cur_class, classtable);
|
||||
if (!classtable->conform(rhs_type, lhs_type)) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< "Type " << rhs_type
|
||||
<< " of assigned expression does not conform to declared type " << lhs_type
|
||||
<< " of identifier " << name
|
||||
<< " .\n";
|
||||
this->type = Object;
|
||||
}
|
||||
else {
|
||||
this->type = rhs_type;
|
||||
}
|
||||
return this->type;
|
||||
}
|
||||
|
||||
Symbol static_dispatch_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
auto expr_type = expr->semant(cur_class, classtable);
|
||||
std::vector<Symbol> _param_type;
|
||||
for (auto i = actual->first(); actual->more(i); i = actual->next(i)) {
|
||||
auto param_i = actual->nth(i);
|
||||
auto param_i_type = param_i->semant(cur_class, classtable);
|
||||
_param_type.push_back(param_i_type);
|
||||
}
|
||||
if (!classtable->class_exist(this->type_name)) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< "Static dispatch to undefined class " << this->type_name
|
||||
<< ".\n";
|
||||
this->type = Object;
|
||||
return this->type;
|
||||
}
|
||||
if (!classtable->conform(expr_type, this->type_name)) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< "Expression type " << expr_type
|
||||
<< " does not conform to declared static dispatch type " << this->type_name
|
||||
<< ".\n";
|
||||
this->type = Object;
|
||||
return this->type;
|
||||
}
|
||||
auto symtab_method = classtable->symtab_met(this->type_name);
|
||||
auto cur_method = symtab_method->lookup(name);
|
||||
if (cur_method == nullptr) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< " Dispatch to undefined method " << name
|
||||
<< ".\n";
|
||||
this->type = Object;
|
||||
return Object;
|
||||
}
|
||||
if (actual->len() != cur_method->get_formals()->len()) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< " Method " << name
|
||||
<< " called with wrong number of arguments.\n";
|
||||
this->type = Object;
|
||||
return 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(*_param_type_iter, formal_i->get_type_decl())) {
|
||||
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;
|
||||
}
|
||||
this->type = return_type;
|
||||
return this->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)
|
||||
{
|
||||
return Object;
|
||||
auto expr_type = expr->semant(cur_class, classtable);
|
||||
std::vector<Symbol> _param_type;
|
||||
for (auto i = actual->first(); actual->more(i); i = actual->next(i)) {
|
||||
auto param_i = actual->nth(i);
|
||||
auto param_i_type = param_i->semant(cur_class, classtable);
|
||||
_param_type.push_back(param_i_type);
|
||||
}
|
||||
auto symtab_method = classtable->symtab_met(expr_type);
|
||||
assert(symtab_method);
|
||||
auto cur_method = symtab_method->lookup(name);
|
||||
if (cur_method == nullptr) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< " Dispatch to undefined method " << name
|
||||
<< ".\n";
|
||||
this->type = Object;
|
||||
return Object;
|
||||
}
|
||||
if (actual->len() != cur_method->get_formals()->len()) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< " Method " << name
|
||||
<< " called with wrong number of arguments.\n";
|
||||
this->type = Object;
|
||||
return 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(*_param_type_iter, formal_i->get_type_decl())) {
|
||||
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;
|
||||
}
|
||||
this->type = return_type;
|
||||
return this->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)
|
||||
{
|
||||
return Object;
|
||||
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);
|
||||
this->type = Object;
|
||||
return 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)
|
||||
{
|
||||
return Object;
|
||||
auto _ = this->expr->semant(cur_class, classtable);
|
||||
std::set<Symbol> _existed_type;
|
||||
Symbol entire_type = nullptr;
|
||||
for (auto i = cases->first(); cases->more(i); i = cases->next(i)) {
|
||||
auto branch_i = static_cast<branch_class*>(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(), this)
|
||||
<< "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 (type_decl_i == SELF_TYPE) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< "Identifier " << name_i
|
||||
<< " declared with type SELF_TYPE in case branch.\n";
|
||||
}
|
||||
else if (!classtable->class_exist(type_decl_i)) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< "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);
|
||||
}
|
||||
}
|
||||
this->type = entire_type;
|
||||
return this->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)
|
||||
{
|
||||
return Object;
|
||||
Symbol type_i;
|
||||
for (auto i = body->first(); body->more(i); i = body->next(i)) {
|
||||
type_i = body->nth(i)->semant(cur_class, classtable);
|
||||
}
|
||||
this->type = type_i;
|
||||
return 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)
|
||||
{
|
||||
return Object;
|
||||
if (!classtable->class_exist(type_decl)) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< "Class " << type_decl
|
||||
<< " of attribute " << identifier
|
||||
<< " is undefined.\n";
|
||||
this->type_decl = Object;
|
||||
}
|
||||
if (typeid(*init) != typeid(no_expr_class)) {
|
||||
auto init_type = init->semant(cur_class, classtable);
|
||||
if (!classtable->conform(init_type, type_decl)) {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< "Inferred type " << init_type
|
||||
<< " of initialization of " << identifier
|
||||
<< " does not conform to identifier's declared type " << type_decl
|
||||
<< ".\n";
|
||||
}
|
||||
}
|
||||
classtable->symtab_obj(cur_class)->enterscope();
|
||||
classtable->symtab_obj(cur_class)->addid(identifier, type_decl);
|
||||
auto expr_type = this->body->semant(cur_class, classtable);
|
||||
classtable->symtab_obj(cur_class)->exitscope();
|
||||
this->type = expr_type;
|
||||
return this->type;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arith(+):
|
||||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Int
|
||||
*/
|
||||
Symbol plus_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
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";
|
||||
}
|
||||
this->type = Int;
|
||||
return Int;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arith(-):
|
||||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Int
|
||||
*/
|
||||
Symbol sub_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
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";
|
||||
}
|
||||
this->type = Int;
|
||||
return Int;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arith(*):
|
||||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Int
|
||||
*/
|
||||
Symbol mul_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
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";
|
||||
}
|
||||
this->type = Int;
|
||||
return Int;
|
||||
}
|
||||
|
||||
/*
|
||||
* Arith(/):
|
||||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Int
|
||||
*/
|
||||
Symbol divide_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
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";
|
||||
}
|
||||
this->type = Int;
|
||||
return Int;
|
||||
}
|
||||
|
||||
/*
|
||||
* Neg:
|
||||
* Check type(e1) == Int, and whatever the result, derive to Int
|
||||
*/
|
||||
Symbol neg_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
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";
|
||||
}
|
||||
this->type = Int;
|
||||
return Int;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare(<):
|
||||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Bool
|
||||
*/
|
||||
Symbol lt_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
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";
|
||||
}
|
||||
this->type = Bool;
|
||||
return 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)
|
||||
{
|
||||
return Object;
|
||||
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";
|
||||
}
|
||||
this->type = Bool;
|
||||
return Bool;
|
||||
}
|
||||
|
||||
/*
|
||||
* Compare(<=):
|
||||
* Check type(e1) == Int == type(e2), and whatever the result, derive to Bool
|
||||
*/
|
||||
Symbol leq_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
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";
|
||||
}
|
||||
this->type = Bool;
|
||||
return 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)
|
||||
{
|
||||
return Object;
|
||||
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";
|
||||
}
|
||||
this->type = Bool;
|
||||
return Bool;
|
||||
}
|
||||
|
||||
/*
|
||||
* Int:
|
||||
* Integer constants derive to Int type
|
||||
*/
|
||||
Symbol int_const_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
this->type = Int;
|
||||
return this->type;
|
||||
}
|
||||
|
||||
/*
|
||||
* True | False:
|
||||
* true or false both derive to Bool type
|
||||
*/
|
||||
Symbol bool_const_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
this->type = Bool;
|
||||
return this->type;
|
||||
}
|
||||
|
||||
/*
|
||||
* String
|
||||
* String constants derive to String type
|
||||
*/
|
||||
Symbol string_const_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
this->type = Str;
|
||||
return this->type;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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)
|
||||
{
|
||||
return Object;
|
||||
if (classtable->class_exist(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)
|
||||
{
|
||||
return Object;
|
||||
auto _ = this->e1->semant(cur_class, classtable);
|
||||
return Bool;
|
||||
}
|
||||
|
||||
/*
|
||||
* `no_expr` always has type `_no_type`
|
||||
*/
|
||||
Symbol no_expr_class::semant(Class_ cur_class, ClassTable* classtable)
|
||||
{
|
||||
return Object;
|
||||
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)
|
||||
{
|
||||
return Object;
|
||||
auto object_type = classtable->symtab_obj(cur_class)->lookup(name);
|
||||
if (object_type != nullptr) {
|
||||
this->type = object_type;
|
||||
return object_type;
|
||||
}
|
||||
else {
|
||||
classtable->semant_error(cur_class->get_filename(), this)
|
||||
<< "Undeclared identifier " << name<< "\n";
|
||||
this->type = Object;
|
||||
return Object;
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,7 +32,7 @@ public:
|
||||
// the `class__class` object for which it builds graph
|
||||
Class_ self;
|
||||
// for cycle detection
|
||||
int reachable = 0;
|
||||
int depth = 0;
|
||||
|
||||
public:
|
||||
// Note that, Class_ = Class__class*, itself is a ptr type, so just use it
|
||||
@ -91,12 +91,12 @@ public:
|
||||
}
|
||||
|
||||
/*
|
||||
* traverse and mark all reachable children
|
||||
* traverse and mark all reachable children, get their depth as well
|
||||
*/
|
||||
void traverse_reachable() {
|
||||
reachable = 1;
|
||||
void traverse_reachable(int depth) {
|
||||
this->depth = depth + 1;
|
||||
for (auto child : children) {
|
||||
child->traverse_reachable();
|
||||
child->traverse_reachable(depth + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -160,10 +160,20 @@ public:
|
||||
method_class* symtab_method_lookup_parent(Symbol, Symbol);
|
||||
bool conform(Symbol, Symbol) const;
|
||||
bool class_exist(Symbol) const;
|
||||
Symbol lub(Symbol, Symbol) const;
|
||||
/* wrappers */
|
||||
std::vector<class__class*>& class_vec() { return _class_vec; }
|
||||
SymbolTable<Symbol, Entry>* symtab_obj(Symbol class_id) { return _symtab_obj[class_id]; };
|
||||
SymbolTable<Symbol, method_class>* symtab_met(Symbol class_id) { return _symtab_met[class_id]; };
|
||||
SymbolTable<Symbol, Entry>* symtab_obj(Class_ class_i) {
|
||||
auto class_id = static_cast<class__class*>(class_i)->get_name();
|
||||
return _symtab_obj[class_id];
|
||||
};
|
||||
SymbolTable<Symbol, method_class>* symtab_met(Class_ class_i) {
|
||||
auto class_id = static_cast<class__class*>(class_i)->get_name();
|
||||
return _symtab_met[class_id];
|
||||
};
|
||||
SymbolTable<Symbol, method_class>* symtab_met(Symbol class_id) {
|
||||
return _symtab_met[class_id];
|
||||
};
|
||||
|
||||
int errors() { return semant_errors; }
|
||||
ostream& semant_error();
|
||||
|
||||
Loading…
Reference in New Issue
Block a user