This lab comes from SE3355 *Compilers* lab4, which requires me to implement a type-checking module when scanning the AST.
The AST is created by bisonc++ script implemented in lab3. To make the following statement more clear, we can take a see of the structure of the AST node. The venv contains the symbols of variables and functions and the tenv contains the symbols of all types.
type::Ty *FieldVar::SemAnalyze(env::VEnvPtr venv, env::TEnvPtr tenv, int labelcount, err::ErrorMsg *errormsg)const{ // for field var first check var need to be a RecordTy, then check the sym_ is in the recordTY type type::Ty * variable_type = var_->SemAnalyze(venv, tenv, labelcount, errormsg); if (variable_type == nullptr) { errormsg->Error(pos_, "variable not defined."); } elseif (typeid(*(variable_type->ActualTy())) != typeid(type::RecordTy)) { errormsg->Error(pos_, "not a record type"); } else { type::RecordTy * real_type = ((type::RecordTy *) (variable_type)); int matched = 0; for (type::Field* field:real_type->fields_->GetList()) { if (field->name_->Name() == sym_->Name()) { matched = 1; } } if (matched == 1) { return real_type; } else { errormsg->Error(pos_, "field nam doesn't exist"); return real_type; } } }
type::Ty *SubscriptVar::SemAnalyze(env::VEnvPtr venv, env::TEnvPtr tenv, int labelcount, err::ErrorMsg *errormsg)const{ //check the type of var_ is an array or not //if so, check subscript_ is an int or not, and check the range. type::Ty *var_type = var_->SemAnalyze(venv, tenv, labelcount, errormsg); //var_type is array of the type that we want to return if (var_type == nullptr) { errormsg->Error(pos_, "variable not defined."); } elseif (typeid(*(var_type->ActualTy())) != typeid(type::ArrayTy)) { errormsg->Error(pos_, "array type required"); } else { type::Ty *subscript_type = subscript_->SemAnalyze(venv, tenv, labelcount, errormsg); if (typeid(*(subscript_type->ActualTy())) != typeid(type::IntTy)) { errormsg->Error(pos_, "subscribe is not a int type."); } else { return ((type::ArrayTy *) var_type)->ty_; } } returnnullptr; }
1.3 Call expression
The type-checking code of call expression is as follows.
Since the loop variable in a for-loop can’t not be assigned because of the rule of the tiger language, when a for-loop declares a variable, the variable should be marked with “readonly = true”. In such way, when encountering a variable in assign exp, the module can check the variable is read-only or not.
type::Ty *BreakExp::SemAnalyze(env::VEnvPtr venv, env::TEnvPtr tenv, int labelcount, err::ErrorMsg *errormsg)const{ if (labelcount == 0) { errormsg->Error(pos_, "break is not inside any loop"); } return type::NilTy::Instance(); }
2.3 handle the nested function declaration
Tiger supports the adjacent nested function, so we need to define all the function name first in order for all the function to find the reference function entry in the venv.
for (absyn::FunDec *function:functions_->GetList()) { if (last_function && function->name_->Name() == last_function->name_->Name()) { errormsg->Error(function->pos_, "two functions have the same name"); } venv->Enter(function->name_, new env::FunEntry(nullptr, nullptr)); last_function = function; }
for (absyn::FunDec *function:functions_->GetList()) { type::Ty *result_ty = tenv->Look(function->result_); type::TyList *formals = function->params_->MakeFormalTyList(tenv, errormsg); venv->Set(function->name_, new env::FunEntry(formals, result_ty)); venv->BeginScope(); auto formal_it = formals->GetList().begin(); auto param_it = function->params_->GetList().begin(); for (; param_it != function->params_->GetList().end(); formal_it++, param_it++) { venv->Enter((*param_it)->name_, new env::VarEntry(*formal_it)); } type::Ty *ty = function->body_->SemAnalyze(venv, tenv, labelcount, errormsg); errormsg->Error(pos_, "function body over"); if (function->result_ == nullptr && ty && typeid(*(ty->ActualTy())) != typeid(type::NilTy)) { errormsg->Error(pos_, "procedure returns value"); } venv->EndScope(); } }
2.4 illegal cycle in nested type declaration
1 2 3 4 5 6 7 8 9 10 11 12 13
// test.16.tig /* error: mutually recursive types thet do not pass through record or array */ let
type a=c type b=a type c=d type d=a
in "" end
The upper code is not allowed in tiger language, so we need to check if there is a cycle in type nested declaration. Each time we define a new type, we need to scan the defined type in the venv to make sure there is no cycle.