semi-complete semantic

This commit is contained in:
2025-12-02 05:11:27 +09:00
parent f4501529df
commit f1fd203233
9 changed files with 567 additions and 213 deletions

View File

@@ -1,159 +1,315 @@
/****************************************************/
/* File: analyze.c */
/* Semantic analyzer implementation */
/* for the TINY compiler */
/* Compiler Construction: Principles and Practice */
/* Kenneth C. Louden */
/* for the CMinus compiler */
/* Yenru0 */
/****************************************************/
#include "analyze.h"
#include "globals.h"
#include "symtab.h"
#include "analyze.h"
/* counter for variable memory locations */
static int location = 0;
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);
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);
}
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;
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)
{ switch (t->nodekind)
{ case StmtK:
switch (t->kind.stmt)
{ case AssignK:
case ReadK:
if (st_lookup(t->attr.name) == -1)
/* not yet in table, so treat as new definition */
st_insert(t->attr.name,t->lineno,location++);
else
/* already in table, so ignore location,
add line number of use only */
st_insert(t->attr.name,t->lineno,0);
break;
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;
}
break;
case ExpK:
switch (t->kind.exp)
{ case IdK:
if (st_lookup(t->attr.name) == -1)
/* not yet in table, so treat as new definition */
st_insert(t->attr.name,t->lineno,location++);
else
/* already in table, so ignore location,
add line number of use only */
st_insert(t->attr.name,t->lineno,0);
break;
default:
break;
}
break;
default:
break;
}
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)
{ traverse(syntaxTree,insertNode,nullProc);
if (TraceAnalyze)
{ fprintf(listing,"\nSymbol table:\n\n");
printSymTab(listing);
}
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 typeError(TreeNode *t, char *message) {
fprintf(listing, "Type error at line %d: %s\n", t->lineno, message);
Error = TRUE;
}
/* Procedure checkNode performs
* type checking at a single tree node
*/
static void checkNode(TreeNode * t)
{ switch (t->nodekind)
{ case ExpK:
switch (t->kind.exp)
{ case OpK:
if ((t->child[0]->type != Integer) ||
(t->child[1]->type != Integer))
typeError(t,"Op applied to non-integer");
if ((t->attr.op == EQ) || (t->attr.op == LT))
t->type = Boolean;
else
t->type = Integer;
break;
case ConstK:
case IdK:
t->type = Integer;
break;
default:
break;
}
break;
case StmtK:
switch (t->kind.stmt)
{ case IfK:
if (t->child[0]->type == Integer)
typeError(t->child[0],"if test is not Boolean");
break;
case AssignK:
if (t->child[0]->type != Integer)
typeError(t->child[0],"assignment of non-integer value");
break;
case WriteK:
if (t->child[0]->type != Integer)
typeError(t->child[0],"write of non-integer value");
break;
case RepeatK:
if (t->child[1]->type == Integer)
typeError(t->child[1],"repeat test is not Boolean");
break;
default:
break;
}
break;
default:
break;
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,nullProc,checkNode);
void typeCheck(TreeNode *syntaxTree) {
traverse(syntaxTree, beforeCheckNode, checkNode);
}