aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ast.h2
-rw-r--r--semantics.c236
-rw-r--r--semantics.h5
3 files changed, 220 insertions, 23 deletions
diff --git a/ast.h b/ast.h
index bdae049..3ae318d 100644
--- a/ast.h
+++ b/ast.h
@@ -47,7 +47,7 @@ typedef struct CNode {
} rec;
union {
CType *type;
- CVar *var_ref;
+ CVar *var;
} ext;
struct CNode *chd, *next;
/* For error reporting */
diff --git a/semantics.c b/semantics.c
index a86462c..14586e1 100644
--- a/semantics.c
+++ b/semantics.c
@@ -6,8 +6,8 @@
#include "ast.h"
#define NEW(type) ((type *)malloc(sizeof(type)))
#define CHECK_TYPE(p, _type) assert(p->type == _type)
-#define ERROR(row, col) print_error(err_buff, NULL, row, col, 0)
-#define WARNING(row, col) print_error(err_buff, NULL, row, col, 1)
+#define ERROR(ast) print_error(err_buff, NULL, (ast)->loc.row, (ast)->loc.col, 0)
+#define WARNING(ast) print_error(err_buff, NULL, (ast)->loc.row, (ast)->loc.col, 1)
#ifdef CIBIC_DEBUG
CTable_t ctable_create(Hashfunc_t hfunc, Printfunc_t pfunc) {
@@ -29,6 +29,9 @@ CTable_t ctable_create(Hashfunc_t hfunc) {
extern void print_error(char *, char *, int, int, int);
extern char *load_line(int);
static char err_buff[MAX_ERROR_BUFF];
+static CType_t basic_type_int;
+static CType_t basic_type_char;
+static CType_t basic_type_void;
void *ctable_lookup(CTable_t ct, const char *key) {
unsigned int hv = ct->hfunc(key) % MAX_TABLE_SIZE;
@@ -303,7 +306,7 @@ static CType_t struct_type_merge(CType_t new, CScope_t scope) {
if (old->type != new->type) /* not a struct or union */
{
sprintf(err_buff, "conflicting types of '%s'", new->name);
- ERROR(new->ast->loc.row, new->ast->loc.col);
+ ERROR(new->ast);
}
/* otherwise it is a struct or union */
if (!new->rec.fields) /* use the old definition */
@@ -311,11 +314,11 @@ static CType_t struct_type_merge(CType_t new, CScope_t scope) {
/* otherwise there's a completion definition */
if (cscope_push_type(scope, new)) /* try to push the defintion */
return new;
- /* conflict appear */
+ /* conflict appears */
if (old->rec.fields) /* if the old one is complete */
{
sprintf(err_buff, "redefinition of '%s'", new->name);
- ERROR(new->ast->loc.row, new->ast->loc.col);
+ ERROR(new->ast);
}
/* otherwise incomplete, thus complete the type */
old->next = new->next;
@@ -325,8 +328,19 @@ static CType_t struct_type_merge(CType_t new, CScope_t scope) {
return old;
}
-int is_same_type(CType_t typea, CType_t typeb) {
+int is_same_type(CType_t typea, CType_t typeb, int arr2ptr) {
if (typea == typeb) return 1;
+ if (arr2ptr)
+ do {
+ int ta = typea->type, tb = typeb->type;
+ if (ta == CPTR) typea = typea->rec.ref;
+ else if (ta == CARR) typea = typea->rec.arr.elem;
+ else break;
+ if (tb == CPTR) typeb = typeb->rec.ref;
+ else if (tb == CARR) typeb = typeb->rec.arr.elem;
+ else break;
+ return is_same_type(typea, typeb, 0);
+ } while (0);
if (typea->type != typeb->type) return 0;
switch (typea->type)
{
@@ -336,22 +350,22 @@ int is_same_type(CType_t typea, CType_t typeb) {
if (typea->rec.arr.len != typeb->rec.arr.len)
return 0;
return is_same_type(typea->rec.arr.elem,
- typeb->rec.arr.elem);
+ typeb->rec.arr.elem, 0);
case CPTR:
return is_same_type(typea->rec.ref,
- typeb->rec.ref);
+ typeb->rec.ref, 0);
case CFUNC:
{
CVar_t pa, pb;
for (pa = typea->rec.func.params,
pb = typeb->rec.func.params; pa && pb;
pa = pa->next, pb = pb->next)
- if (!is_same_type(pa->type, pb->type))
+ if (!is_same_type(pa->type, pb->type, 0))
return 0;
if (pa || pb)
return 0; /* different number of parameters */
return is_same_type(typea->rec.func.ret,
- typeb->rec.func.ret);
+ typeb->rec.func.ret, 0);
}
case CINT: case CCHAR: case CVOID:
;
@@ -366,10 +380,10 @@ static CVar_t var_merge(CVar_t new, CScope_t scope) {
return new;
else
old = cscope_lookup_var(scope, new->name);
- if (!is_same_type(old->type, new->type) || scope->lvl > 0)
+ if (!is_same_type(old->type, new->type, 0) || scope->lvl > 0)
{
sprintf(err_buff, "conflicting types of '%s'", new->name);
- ERROR(new->ast->loc.row, new->ast->loc.col);
+ ERROR(new->ast);
}
free(new);
return old;
@@ -426,7 +440,7 @@ CVar_t semantics_params(CNode *p, CScope_t scope) {
if (!cscope_push_var(scope, var))
{
sprintf(err_buff, "redefinition of parameter '%s'", var->name);
- ERROR(var->ast->loc.row, var->ast->loc.col);
+ ERROR(var->ast);
}
var->next = params;
params = var;
@@ -434,6 +448,13 @@ CVar_t semantics_params(CNode *p, CScope_t scope) {
return params;
}
+#define CHECK_CVOID(name, ast) \
+ if (type_spec->type == CVOID) \
+ do { \
+ sprintf(err_buff, "variable or field '%s' declared void", name); \
+ ERROR(ast); \
+ } while (0)
+
CVar_t semantics_p_declr(CNode *p, CType_t type_spec) {
/* deal with pointer prefix */
CNode *t, *ast;
@@ -444,6 +465,7 @@ CVar_t semantics_p_declr(CNode *p, CType_t type_spec) {
ptype = type_spec; /* filled by type spec */
name = p->rec.strval;
ast = p;
+ CHECK_CVOID(name, ast);
}
else
{
@@ -534,7 +556,7 @@ CTable_t semantics_fields(CNode *p, CScope_t scope) {
if (!ctable_insert(ct, var->name, var, 0))
{
sprintf(err_buff, "duplicate member '%s'", var->name);
- ERROR(var->ast->loc.row, var->ast->loc.col);
+ ERROR(var->ast);
}
}
}
@@ -570,7 +592,173 @@ CVar_t semantics_decl(CNode *p, CScope_t scope) {
{
/* TODO: useless typename warning */
sprintf(err_buff, "useless declaration");
- WARNING(type->ast->loc.row, type->ast->loc.col);
+ WARNING(type->ast);
+ }
+ return res;
+}
+
+#define INCOMP_TYPE(ast) \
+ do { \
+ sprintf(err_buff, "incompatible types when assigning"); \
+ ERROR(ast); \
+ } while (0)
+
+ExpType exp_check_aseq(ExpType lhs, ExpType rhs, CNode *ast) {
+ switch (lhs.type->type)
+ {
+ case CSTRUCT: case CUNION:
+ if (!is_same_type(lhs.type, rhs.type, 0))
+ INCOMP_TYPE(ast);
+ break;
+ case CARR: case CFUNC: /* constant */
+ INCOMP_TYPE(ast);
+ break;
+ case CINT: case CCHAR:
+ switch (rhs.type->type)
+ {
+ case CINT: case CCHAR: ; break; /* ok */
+ case CPTR:
+ sprintf(err_buff, "assignment makes integer from pointer without a cast");
+ WARNING(ast);
+ break;
+ default: INCOMP_TYPE(ast);
+ }
+ break;
+ case CPTR:
+ switch (rhs.type->type)
+ {
+ case CPTR:
+ if (!is_same_type(lhs.type->rec.ref, rhs.type->rec.ref, 1))
+ {
+ sprintf(err_buff, "assignment from incompatible pointer type");
+ WARNING(ast);
+ }
+ break;
+ case CINT: case CCHAR:
+ sprintf(err_buff, "assignment makes pointer from integer without a cast");
+ WARNING(ast);
+ break;
+ default: INCOMP_TYPE(ast);
+ }
+ break;
+ default: assert(0);
+ }
+ return lhs;
+}
+
+ExpType exp_check_two_int(ExpType op1, ExpType op2, CNode *ast) {
+ int t1 = op1.type->type,
+ t2 = op2.type->type;
+ if (!((t1 == CINT || t1 == CCHAR) && (t2 == CINT || t2 == CCHAR)))
+ {
+ sprintf(err_buff, "invalid operands to binary operator");
+ ERROR(ast);
+ }
+ return op1;
+}
+
+ExpType exp_check_add(ExpType op1, ExpType op2, CNode *ast, int sub) {
+ int t1 = op1.type->type,
+ t2 = op2.type->type;
+ if (!sub && t2 == CPTR)
+ {
+ /* place the pointer type in the first place */
+ int t = t1;
+ ExpType te = op1;
+
+ t1 = t2;
+ t2 = t;
+
+ op1 = op2;
+ op2 = te;
+
+ CNode *n1 = ast->chd;
+ CNode *n2 = n1->next;
+ n2->next = n1;
+ n1->next = NULL;
+ ast->chd = n2;
+ }
+ if (!((t1 == CINT || t1 == CCHAR || t1 == CPTR) &&
+ (t2 == CINT || t2 == CCHAR)))
+ {
+ sprintf(err_buff, "invalid operands to binary operator");
+ ERROR(ast);
+ }
+ return op1;
+}
+
+ExpType semantics_exp(CNode *, CScope_t);
+ExpType exp_check_ass(CNode *p, CScope_t scope) {
+ ExpType lhs = semantics_exp(p->chd, scope),
+ rhs = semantics_exp(p->chd->next, scope);
+ if (!lhs.lval)
+ {
+ sprintf(err_buff, "lvalue required as left operand of assignment");
+ ERROR(p);
+ }
+ switch (p->rec.subtype)
+ {
+ case '=' : return exp_check_aseq(lhs, rhs, p);
+ case ASS_MUL: return exp_check_aseq(lhs, exp_check_two_int(lhs, rhs, p), p);
+ case ASS_DIV: return exp_check_aseq(lhs, exp_check_two_int(lhs, rhs, p), p);
+ case ASS_MOD: return exp_check_aseq(lhs, exp_check_two_int(lhs, rhs, p), p);
+ case ASS_ADD: return exp_check_aseq(lhs, exp_check_add(lhs, rhs, p, 0), p);
+ case ASS_SUB: return exp_check_aseq(lhs, exp_check_add(lhs, rhs, p, 1), p);
+ case ASS_SHL: return exp_check_aseq(lhs, exp_check_two_int(lhs, rhs, p), p);
+ case ASS_SHR: return exp_check_aseq(lhs, exp_check_two_int(lhs, rhs, p), p);
+ case ASS_AND: return exp_check_aseq(lhs, exp_check_two_int(lhs, rhs, p), p);
+ case ASS_XOR: return exp_check_aseq(lhs, exp_check_two_int(lhs, rhs, p), p);
+ case ASS_OR: return exp_check_aseq(lhs, exp_check_two_int(lhs, rhs, p), p);
+ default: assert(0);
+ }
+}
+
+ExpType semantics_exp(CNode *p, CScope_t scope) {
+ ExpType res;
+ switch (p->type)
+ {
+ case ID:
+ if (!(p->ext.var = cscope_lookup_var(scope, p->rec.strval)))
+ {
+ sprintf(err_buff, "'%s' undeclared", p->rec.strval);
+ ERROR(p);
+ }
+ res.type = p->ext.var->type;
+ res.lval = 1;
+ break;
+ case INT:
+ p->ext.type = res.type = basic_type_int;
+ res.lval = 0;
+ break;
+ case CHAR:
+ res.type = basic_type_char;
+ res.lval = 0;
+ break;
+ case STR:
+ {
+ CType_t type = ctype_create("", CPTR, NULL);
+ type->rec.ref = basic_type_char;
+ p->ext.type = res.type = type;
+ }
+ case EXP:
+ switch (p->rec.subtype)
+ {
+ case '=' :
+ case ASS_MUL:
+ case ASS_DIV:
+ case ASS_MOD:
+ case ASS_ADD:
+ case ASS_SUB:
+ case ASS_SHL:
+ case ASS_SHR:
+ case ASS_AND:
+ case ASS_XOR:
+ case ASS_OR:
+ return exp_check_ass(p, scope);
+ default: assert(0);
+ }
+ break;
+ default: assert(0);
}
return res;
}
@@ -582,6 +770,7 @@ CVar_t semantics_stmt(CNode *p, CScope_t scope) {
{
case STMT_EXP:
/* TODO: expression handle */
+ semantics_exp(p->chd, scope);
break;
case STMT_COMP:
{
@@ -676,17 +865,17 @@ CVar_t semantics_func(CNode *p, CScope_t scope) {
if (funco->type != CFUNC)
{
sprintf(err_buff, "conflicting types of '%s'", res->name);
- ERROR(res->ast->loc.row, res->ast->loc.col);
+ ERROR(res->ast);
}
else if (funco->rec.func.body)
{
sprintf(err_buff, "redefintion of function '%s'", res->name);
- ERROR(res->ast->loc.row, res->ast->loc.col);
+ ERROR(res->ast);
}
- else if (!is_same_type(funco, res->type))
+ else if (!is_same_type(funco, res->type, 0))
{
sprintf(err_buff, "function defintion does not match the prototype");
- ERROR(res->ast->loc.row, res->ast->loc.col);
+ ERROR(res->ast);
}
funco->rec.func.local = res->type->rec.func.local;
funco->rec.func.body = res->type->rec.func.body;
@@ -705,10 +894,13 @@ void semantics_check_(CNode *p, CScope_t scope) {
void semantics_check(CNode *ast) {
CScope_t scope = cscope_create();
+ basic_type_int = ctype_create("int", CINT, NULL);
+ basic_type_char = ctype_create("char", CCHAR, NULL);
+ basic_type_void = ctype_create("void", CVOID, NULL);
/* add top-level basic types */
- cscope_push_type(scope, ctype_create("int", CINT, NULL));
- cscope_push_type(scope, ctype_create("char", CCHAR, NULL));
- cscope_push_type(scope, ctype_create("void", CVOID, NULL));
+ cscope_push_type(scope, basic_type_int);
+ cscope_push_type(scope, basic_type_char);
+ cscope_push_type(scope, basic_type_void);
/* check all definitions and declarations */
for (ast = ast->chd; ast; ast = ast->next)
{
diff --git a/semantics.h b/semantics.h
index b770156..9136ebb 100644
--- a/semantics.h
+++ b/semantics.h
@@ -109,6 +109,11 @@ typedef struct CScope {
struct CTable *ttype;
} CScope;
+typedef struct ExpType {
+ CType *type;
+ int lval;
+} ExpType;
+
CScope_t cscope_create();
CVar *cscope_lookup_var(CScope_t cs, const char *name);
CType *cscope_lookup_type(CScope_t cs, const char *name);