316 lines
10 KiB
C
316 lines
10 KiB
C
/****************************************************/
|
|
/* File: analyze.c */
|
|
/* Semantic analyzer implementation */
|
|
/* for the CMinus compiler */
|
|
/* Yenru0 */
|
|
/****************************************************/
|
|
|
|
#include "analyze.h"
|
|
#include "globals.h"
|
|
#include "symtab.h"
|
|
|
|
static void symbolError(TreeNode *t, char *message) {
|
|
fprintf(listing, "Symbol error at line %d: %s\n", t->lineno, message);
|
|
Error = TRUE;
|
|
exit(-1);
|
|
}
|
|
|
|
static BucketList func_entry = NULL;
|
|
static Scope func_scope;
|
|
static TreeNode *func_params[256];
|
|
static int func_param_count = 0;
|
|
|
|
/* Procedure traverse is a generic recursive
|
|
* syntax tree traversal routine:
|
|
* it applies preProc in preorder and postProc
|
|
* in postorder to tree pointed to by t
|
|
*/
|
|
static void traverse(TreeNode *t,
|
|
void (*preProc)(TreeNode *),
|
|
void (*postProc)(TreeNode *)) {
|
|
if (t != NULL) {
|
|
preProc(t);
|
|
{
|
|
int i;
|
|
for (i = 0; i < MAXCHILDREN; i++)
|
|
traverse(t->child[i], preProc, postProc);
|
|
}
|
|
postProc(t);
|
|
traverse(t->sibling, preProc, postProc);
|
|
}
|
|
}
|
|
|
|
/* nullProc is a do-nothing procedure to
|
|
* generate preorder-only or postorder-only
|
|
* traversals from traverse
|
|
*/
|
|
static void nullProc(TreeNode *t) {
|
|
if (t == NULL) return;
|
|
else
|
|
return;
|
|
}
|
|
|
|
/* Procedure insertNode inserts
|
|
* identifiers stored in t into
|
|
* the symbol table
|
|
*/
|
|
static void insertNode(TreeNode *t) {
|
|
//printf("Insert Node: line %d\n", t->lineno);
|
|
switch (t->nodekind) {
|
|
case ExpK:
|
|
switch (t->kind.exp) {
|
|
case IdK:
|
|
case ArrIdK:
|
|
case CallK: {
|
|
BucketList entry = st_lookup(t->attr.name);
|
|
if (entry == NULL) {
|
|
symbolError(t, "Undeclared Symbol");
|
|
} else {
|
|
// t->type = entry->type;// TODO: Ambiguity
|
|
t->scope = curr_scope();
|
|
st_entry_insert_line(entry, t->lineno);
|
|
t->type = entry->type;
|
|
}
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case StmtK:
|
|
switch (t->kind.stmt) {
|
|
case CompK:
|
|
if (func_scope != NULL) {
|
|
push_scope(func_scope);
|
|
func_scope = NULL;
|
|
for (int i = 0; i < func_param_count; i++) {
|
|
TreeNode *param = func_params[i];
|
|
func_entry->param_types[func_entry->param_count++] = param->type;
|
|
if (st_lookup_current(param->attr.name) != NULL) {
|
|
symbolError(param, "Redefinition of a Parameter");
|
|
} else {
|
|
st_try_insert(param->attr.name, SymbolParam, param->type, param->lineno);
|
|
}
|
|
}
|
|
func_entry = NULL;
|
|
func_scope = NULL;
|
|
func_param_count = 0;
|
|
} else {
|
|
push_scope(scope_new("compound"));
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case DeclK:
|
|
switch (t->kind.decl) {
|
|
case FuncK:
|
|
if (st_lookup(t->attr.name) != NULL) {
|
|
symbolError(t, "Redefinition of a Function");
|
|
} else {
|
|
func_entry = st_try_insert(t->attr.name, SymbolFunc, t->type, t->lineno);
|
|
t->scope = curr_scope();
|
|
}
|
|
|
|
func_scope = scope_new(t->attr.name);
|
|
//push_scope(scope_new(t->attr.name));
|
|
break;
|
|
case ArrParamK:
|
|
case NonArrParamK: {
|
|
|
|
if (t->type != Void) {
|
|
func_params[func_param_count++] = t;
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
case VarK:
|
|
case ArrVarK:
|
|
if (st_lookup_current(t->attr.name) != NULL) {
|
|
symbolError(t, "Redefinition of a Variable");
|
|
} else {
|
|
if (t->type == Void) {
|
|
symbolError(t, "Variable cannot be of type void");
|
|
break;
|
|
}
|
|
t->scope = curr_scope();
|
|
st_try_insert(t->attr.name, SymbolVar, t->type, t->lineno);
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void afterNode(TreeNode *t) {
|
|
if (t->nodekind == StmtK && t->kind.stmt == CompK) {
|
|
pop_scope();
|
|
}
|
|
}
|
|
|
|
/* Function buildSymtab constructs the symbol
|
|
* table by preorder traversal of the syntax tree
|
|
*/
|
|
void buildSymtab(TreeNode *syntaxTree) {
|
|
st_init();
|
|
BucketList entry;
|
|
entry = st_try_insert("input", SymbolFunc, Integer, 0);
|
|
entry->param_count = 0;
|
|
|
|
entry = st_try_insert("output", SymbolFunc, Void, 0);
|
|
entry->param_types[0] = Integer;
|
|
entry->param_count = 1;
|
|
|
|
traverse(syntaxTree, insertNode, afterNode);
|
|
if (TraceAnalyze) {
|
|
fprintf(listing, "\nSymbol table:\n\n");
|
|
printSymTab(listing);
|
|
}
|
|
}
|
|
|
|
static void typeError(TreeNode *t, char *message) {
|
|
fprintf(listing, "Type error at line %d: %s\n", t->lineno, message);
|
|
Error = TRUE;
|
|
}
|
|
|
|
static void beforeCheckNode(TreeNode *t) {
|
|
if (t->nodekind == DeclK && t->kind.decl == FuncK) {
|
|
func_entry = st_lookup(t->attr.name);
|
|
}
|
|
}
|
|
static void checkNode(TreeNode *t) {
|
|
switch (t->nodekind) {
|
|
case ExpK:
|
|
switch (t->kind.exp) {
|
|
case OpK: {
|
|
TreeNode *left = t->child[0];
|
|
TreeNode *right = t->child[1];
|
|
if (left->type != Integer || right->type != Integer) {
|
|
typeError(t, "Operator applied to non-integer");
|
|
}
|
|
t->type = Integer;
|
|
|
|
} break;
|
|
case ConstK:
|
|
t->type = Integer;
|
|
break;
|
|
case IdK: {
|
|
BucketList entry = st_lookup_from(t->attr.name, t->scope);
|
|
t->type = entry->type;
|
|
|
|
} break;
|
|
case ArrIdK: {
|
|
if (t->child[0]->type != Integer) {
|
|
typeError(t, "Array subscript is not an integer");
|
|
}
|
|
t->type = IntegerArray;
|
|
|
|
} break;
|
|
|
|
case AssignK: {
|
|
TreeNode *left = t->child[0];
|
|
TreeNode *right = t->child[1];
|
|
if (left->type != right->type) {
|
|
typeError(t, "Assignment of different types");
|
|
}
|
|
t->type = left->type;
|
|
|
|
} break;
|
|
case CallK: {
|
|
BucketList entry = st_lookup_from(t->attr.name, t->scope);// not null
|
|
|
|
if (entry->symbolKind != SymbolFunc) {
|
|
typeError(t, "Call to a non-function");
|
|
}
|
|
|
|
TreeNode *arg = t->child[0];
|
|
|
|
int i = 0;// 파라미터 인덱스
|
|
|
|
while (arg != NULL && i < entry->param_count) {
|
|
if (arg->type != entry->param_types[i]) {
|
|
typeError(t, "Type mismatch in argument: Expected different type");
|
|
}
|
|
|
|
arg = arg->sibling;
|
|
i++;
|
|
}
|
|
|
|
if (arg != NULL) {
|
|
typeError(t, "Too many arguments in function call");
|
|
} else if (i < entry->param_count) {
|
|
typeError(t, "Too few arguments in function call");
|
|
}
|
|
|
|
t->type = entry->returnType;
|
|
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case StmtK:
|
|
switch (t->kind.stmt) {
|
|
case ReturnK: {
|
|
|
|
if (func_entry == NULL) {
|
|
typeError(t, "Return statement is not in a function");
|
|
break;
|
|
}
|
|
TreeNode *retval = t->child[0]; /* nullalbe */
|
|
if (func_entry->returnType == Void) {
|
|
if (retval != NULL) {
|
|
typeError(t, "Return with a value in a void function");
|
|
}
|
|
} else {
|
|
if (retval == NULL) {
|
|
typeError(t, "Return without a value in a non-void function");
|
|
} else if (retval->type != func_entry->returnType) {
|
|
typeError(t, "Return type mismatch");
|
|
}
|
|
}
|
|
} break;
|
|
case IterK: {
|
|
TreeNode *condition = t->child[0];
|
|
if (condition->type != Integer) {
|
|
typeError(t, "While condition is not of type integer");
|
|
}
|
|
} break;
|
|
case IfK: {
|
|
TreeNode *condition = t->child[0];
|
|
if (condition->type != Integer) {
|
|
typeError(t, "If condition is not of type integer");
|
|
}
|
|
} break;
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case DeclK:
|
|
switch (t->kind.decl) {
|
|
|
|
case FuncK:
|
|
func_entry = NULL;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Procedure typeCheck performs type checking
|
|
* by a postorder syntax tree traversal
|
|
*/
|
|
void typeCheck(TreeNode *syntaxTree) {
|
|
traverse(syntaxTree, beforeCheckNode, checkNode);
|
|
}
|