diff options
Diffstat (limited to 'semantics.c')
-rw-r--r-- | semantics.c | 2042 |
1 files changed, 2042 insertions, 0 deletions
diff --git a/semantics.c b/semantics.c new file mode 100644 index 0000000..e40a26a --- /dev/null +++ b/semantics.c @@ -0,0 +1,2042 @@ +#include <stdlib.h> +#include <assert.h> +#include <string.h> +#include <stdio.h> +#include "semantics.h" +#include "ast.h" + +#define NEW(type) ((type *)malloc(sizeof(type))) +#define CHECK_TYPE(p, _type) assert(p->type == _type) +#define ERROR(x) do { error_print x; } while (0) +#define WARNING(x) do { warning_print x; } while (0) + +#define NOT_IGNORE_VOID(et, ast) \ + if (et->type == CVOID) \ + do { \ + ERROR((ast, "void value not ignored as it ought to be")); \ + } while (0) + +#define INCOMP_TYPE(ast) \ + do { \ + ERROR((ast, "incompatible types when assigning")); \ + } while (0) + +/* pointer to function conversion (std 6.3.2/4) */ +/* also convert array to pointer */ +#define POINTER_CONV(t, p) \ + do { \ + if ((t)->type == CFUNC) \ + { \ + CType_t f = ctype_create("", CPTR, p); \ + f->rec.ref = t; \ + t = f; \ + } \ + else if ((t)->type == CARR) \ + { \ + CType_t a = ctype_create("", CPTR, p); \ + a->rec.ref = t->rec.arr.elem; \ + free(t); \ + t = a; \ + } \ + } while (0) + +#define CHECK_CVOID(name, ast) \ + if (typespec->type == CVOID) \ + do { \ + ERROR((ast, "variable or field '%s' declared void", name)); \ + } while (0) + +#define IS_INT(tt) ((tt) == CINT || (tt) == CCHAR) +#define IS_PTR(tt) ((tt) == CPTR || (tt) == CARR) +#define IS_SCALAR(tt) (!((tt) == CUNION || (tt) == CSTRUCT)) +#define IS_ARITH(tt) IS_INT(tt) + +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; +static CType_t builtin_printf; +static CType_t builtin_scanf; +static CType_t builtin_malloc; +static CType_t builtin_memcpy; +static CType_t builtin_print_int; +static CType_t builtin_print_char; +static CType_t builtin_print_string; + +CTList_t funcs; +CVList_t gvars; +CSList_t cstrs; + +static void error_print(CNode *ast, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vsprintf(err_buff, fmt, args); + print_error(err_buff, NULL, ast->loc.row, ast->loc.col, 0); + va_end(args); +} + +static void warning_print(CNode *ast, const char *fmt, ...) { + va_list args; + va_start(args, fmt); + vsprintf(err_buff, fmt, args); + print_error(err_buff, NULL, ast->loc.row, ast->loc.col, 1); + va_end(args); +} + +const char *csymbol_getname(CSymbol_t sym) { + switch (sym->kind) + { + case CVAR: return sym->rec.var->name; + case CTYPE: return sym->rec.type->name; + case CDEF: return sym->rec.def->name; + } + return NULL; +} + +CTable_t ctable_create(Hashfunc_t hfunc, Printfunc_t pfunc) { + CTable_t ct = NEW(CTable); + memset(ct->head, 0, sizeof(CTNode*) * MAX_TABLE_SIZE); + ct->hfunc = hfunc; + ct->pfunc = pfunc; + return ct; +} + +void ctable_destory(CTable_t ct) { + int i; + for (i = 0; i < MAX_TABLE_SIZE; i++) + { + CTNode *p, *np; + for (p = ct->head[i]; p; p = np) + { + np = p->next; + free(p); + } + } +} + +void *ctable_lookup(CTable_t ct, const char *key) { + unsigned int hv = ct->hfunc(key) % MAX_TABLE_SIZE; + CTNode *p = ct->head[hv]; + for (; p; p = p->next) + if (!strcmp(p->key, key)) + return p->val; + return NULL; /* not found */ +} + +int ctable_insert(CTable_t ct, const char *key, void *val, int lvl) { + unsigned int hv = ct->hfunc(key) % MAX_TABLE_SIZE; + CTNode *p = ct->head[hv], *np; + for (; p && p->lvl == lvl; p = p->next) + if (!strcmp(p->key, key)) + return 0; /* conflict */ + np = NEW(CTNode); + np->key = key; + np->val = val; + np->lvl = lvl; + np->next = ct->head[hv]; + ct->head[hv] = np; + return 1; +} + +void ctable_clip(CTable_t ct, const char *key, int max_lvl) { + unsigned int hv = ct->hfunc(key) % MAX_TABLE_SIZE; + CTNode *p = ct->head[hv], *np; + for (; p && p->lvl > max_lvl; p = np) + { + np = p->next; + free(p); + } + ct->head[hv] = p; +} + +CScope_t cscope_create(void) { + CScope_t p = NEW(CScope); + p->lvl = -1; + p->top = NULL; + p->func = NULL; + p->inside_loop = 0; + p->ids = ctable_create(bkdr_hash, csymbol_print); + p->tags = ctable_create(bkdr_hash, csymbol_print); + cscope_enter(p); + return p; +} + +static int cscope_push(CScope_t cs, CSymbol_t sym, int nspace) { + CTable_t ct = nspace == NS_ID ? cs->ids : cs->tags; +#ifdef CIBIC_DEBUG + assert(cs->top); +#endif + if (ctable_insert(ct, csymbol_getname(sym), sym, cs->lvl)) + { + CSElem *e = NEW(CSElem); + e->sym = sym; + e->next = cs->top->symlist; + cs->top->symlist = e; + return 1; + } + else return 0; /* naming conflict */ +} + +int cscope_push_var(CScope_t cs, CVar_t var, int nspace) { + CSymbol_t p = NEW(CSymbol); + p->kind = CVAR; + p->rec.var = var; + if (!cscope_push(cs, p, nspace)) + { + free(p); + return 0; + } + return 1; +} + +int cscope_push_type(CScope_t cs, CType_t type, int nspace) { + CSymbol_t p = NEW(CSymbol); + p->kind = CTYPE; + p->rec.type = type; + if (!cscope_push(cs, p, nspace)) + { + free(p); + return 0; + } + return 1; +} + +int cscope_push_def(CScope_t cs, CDef_t def, int nspace) { + CSymbol_t p = NEW(CSymbol); + p->kind = CDEF; + p->rec.def = def; + if (!cscope_push(cs, p, nspace)) + { + free(p); + return 0; + } + return 1; +} + +void cscope_enter(CScope_t cs) { + CSNode *np = NEW(CSNode); + np->next = cs->top; + np->symlist = NULL; + cs->top = np; + cs->lvl++; +} + +void cscope_exit(CScope_t cs) { + CSNode *otop = cs->top; + CSElem *p, *np; + cs->lvl--; + cs->top = otop->next; + for (p = otop->symlist; p; p = np) + { + const char *name = csymbol_getname(p->sym); + ctable_clip(cs->ids, name, cs->lvl); + ctable_clip(cs->tags, name, cs->lvl); + np = p->next; + free(p->sym); /* free CSymbol */ + free(p); /* free CSElem */ + } + free(otop); +} + +CSymbol_t cscope_lookup(CScope_t cs, const char *name, int nspace) { + if (nspace == NS_ID) + return ctable_lookup(cs->ids, name); + else + return ctable_lookup(cs->tags, name); + return NULL; +} + +unsigned int bkdr_hash(const char *str) { + unsigned int seed = 131; + unsigned int hv = 0; + while (*str) + hv = hv * seed + (unsigned)(*str++); + return hv; +} + +CVar_t cvar_create(char *name, CType_t type, CNode *ast) { + CVar_t cv = NEW(CVar); + cv->name = name; + cv->type = type; + cv->ast = ast; + cv->initr = NULL; + cv->defsite = NULL; + cv->loc = 0; + cv->weight = 0; + cv->reload = 0; + cv->cnt = 0; + return cv; +} + +CType_t ctype_create(char *name, int type, CNode *ast) { + CType_t ct = NEW(CType); + ct->name = name; + ct->type = type; + ct->ast = ast; + ct->size = -1; + return ct; +} + +int align_shift(int x) { + return ((4 - (x & 3)) & 3); +} + +int calc_size(CType_t type) { + int size = type->size; + if (size != -1) return size; + /* TODO: correct alignment */ + switch (type->type) + { + case CINT: size = INT_SIZE; break; + case CCHAR: size = CHAR_SIZE; break; + case CPTR: size = PTR_SIZE; break; + case CARR: + size = type->rec.arr.len * calc_size(type->rec.arr.elem); + break; + case CSTRUCT: + { + size = 0; + CVar_t p = type->rec.st.flist; + if (!p) return -1; + for (; p; p = p->next) + { + /* add padding to align to word boundary */ + if (p->type->type != CCHAR) + size += align_shift(size); + p->start = size; + size += calc_size(p->type); + } + } + break; + case CUNION: + { + size = 0; + CVar_t p = type->rec.st.flist; + if (!p) return -1; + for (; p; p = p->next) + { + int t = calc_size(p->type); + if (t > size) size = t; + p->start = 0; + } + } + break; + case CVOID: return -1; + case CFUNC: return 1; + } + return (type->size = size); +} + +static CType_t struct_type_merge(CType_t new, CScope_t scope) { + /* Note: we shall try to lookup first instead of pushing !! */ + CSymbol_t lu = cscope_lookup(scope, new->name, NS_TAG); + CType_t old; + if (!lu) /* create it if it does not exist */ + { + cscope_push_type(scope, new, NS_TAG); + return new; + } /* otherwise we have it */ + old = lu->rec.type; + /* it must be a struct or union */ + if (!new->rec.st.fields) /* use the old definition */ + return old; + /* otherwise it's a complete definition */ + if (cscope_push_type(scope, new, NS_TAG)) + return new; /* try to push the defintion */ + /* conflict appears */ + if (old->rec.st.fields) /* if the old one is complete */ + ERROR((new->ast, "redefinition of '%s'", new->name)); + /* otherwise incomplete, thus complete the type */ + old->rec.st = new->rec.st; + old->ast = new->ast; + free(new); + return old; +} + +static void type_merge(CType_t old, CType_t new) { + /* assume old and new are the same type */ + assert(old->type == new->type); + switch (old->type) + { + case CINT: case CCHAR: case CPTR: + case CUNION: case CSTRUCT: + break; + case CFUNC: + if (new->rec.func.params) + old->rec.func.params = new->rec.func.params; + if (new->rec.func.body) + { + old->rec.func.local = new->rec.func.local; + old->rec.func.body = new->rec.func.body; + } + break; + default: assert(0); + } +} + +int is_same_type(CType_t typea, CType_t typeb) { + if (typea == typeb) return 1; + if (typea->type != typeb->type) return 0; + switch (typea->type) + { + case CSTRUCT: case CUNION: + return typea == typeb; + case CARR: + if (typea->rec.arr.len != typeb->rec.arr.len) + return 0; + return is_same_type(typea->rec.arr.elem, typeb->rec.arr.elem); + case CPTR: + return is_same_type(typea->rec.ref, typeb->rec.ref); + case CFUNC: + { + CVar_t pa = typea->rec.func.params, + pb = typeb->rec.func.params; + if ((pa || typea->rec.func.body) && + (pb || typeb->rec.func.body)) + { + for (;pa && pb; pa = pa->next, pb = pb->next) + if (!is_same_type(pa->type, pb->type)) + return 0; + if (pa || pb) + return 0; /* different number of parameters */ + } + return is_same_type(typea->rec.func.ret, typeb->rec.func.ret); + } + case CINT: case CCHAR: case CVOID: break; + } + return 1; +} + +static CVar_t var_merge(CVar_t new, CScope_t scope) { + CVar_t old; + if (cscope_push_var(scope, new, NS_ID)) + return new; + else + old = cscope_lookup(scope, new->name, NS_ID)->rec.var; + if (!is_same_type(old->type, new->type)) + ERROR((new->ast, "conflicting types of '%s'", new->name)); + else if (scope->lvl) + ERROR((new->ast, "redeclaration of '%s' with no linkage", new->name)); + type_merge(old->type, new->type); + free(new); + return old; +} + +void semantics_fields(CNode *, CType_t, CScope_t scope); +CType_t semantics_typespec(CNode *p, CScope_t scope) { + CHECK_TYPE(p, TYPE_SPEC); + CType_t type; + switch (p->rec.subtype) + { + case KW_VOID: + type = ctype_create("", CVOID, p); break; + case KW_CHAR: + type = ctype_create("", CCHAR, p); break; + case KW_INT: + type = ctype_create("", CINT, p); break; + case KW_STRUCT: case KW_UNION: + { + CNode *id = p->chd, + *fields = p->chd->next; + type = ctype_create(id->type == NOP ? "" : id->rec.strval, + p->rec.subtype == KW_STRUCT ? CSTRUCT : CUNION, + p); + if (fields->type == NOP) + type->rec.st.fields = NULL; /* incomplete type */ + else + semantics_fields(fields, type, scope); + if (id->type != NOP) + type = struct_type_merge(type, scope); + } + break; + case USER_TYPE: + { + CHECK_TYPE(p->chd, ID); + CSymbol_t lu = cscope_lookup(scope, p->chd->rec.strval, NS_ID); + assert(lu && lu->kind == CDEF); /* parser guarantees this */ + type = lu->rec.def->type; + } + break; + default: assert(0); + } + return type; +} + +int type_is_complete(CType_t type) { + switch(type->type) + { + case CINT: case CCHAR: + /* basic types are always complete */ + case CPTR: + /* pointer may point to an incomplete type */ + case CARR: + /* syntax of incomplete arrays is not allowed in `cibic.y` */ + return 1; + case CSTRUCT: case CUNION: + /* fields are guaranteed to be complete if exists, due to + * `semantics_fields` */ + return type->rec.st.fields != NULL; + case CVOID: + /* void type is never complete */ + return 0; + case CFUNC: + /* function body is not required here, it is checked in the last + * phase */ + return 1; + default: assert(0); + } + return 1; +} + +CVar_t semantics_declr(CNode *, CType_t, CScope_t, int); +CVar_t semantics_pdecl(CNode *p, CScope_t scope) { + CHECK_TYPE(p, PLAIN_DECL); + CVar_t var = semantics_declr(p->chd->next, + semantics_typespec(p->chd, scope), + scope, 0); + return var; +} + +CVar_t semantics_params(CNode *p, CScope_t scope) { + CHECK_TYPE(p, PARAMS); + static CVar dummy; + CVar_t params = &dummy, tail = params; + CTable_t ct; + if (!(p = p->chd)) return NULL; /* no parameters */ + ct = ctable_create(bkdr_hash, ctable_cvar_print); + for (; p; p = p->next) + { + CVar_t var = semantics_pdecl(p, scope); + POINTER_CONV(var->type, p); + if (scope) /* params inside a function definition */ + if (!ctable_insert(ct, var->name, var, 0)) + ERROR((var->ast, "redefinition of parameter '%s'", var->name)); + tail->next = var; + tail = var; + } + ctable_destory(ct); + tail->next = NULL; + return params->next; +} + +ExpType semantics_exp(CNode *, CScope_t); +CVar_t semantics_declr(CNode *p, CType_t typespec, CScope_t scope, int flag) { + CVar_t type; + if (p->type == ID) + { + if (!(flag & FLAG_FUNC_CHK)) CHECK_CVOID(p->rec.strval, p); + return cvar_create(p->rec.strval, typespec, p); + } + if (p->type == NOP) /* type name */ + return cvar_create(NULL, typespec, p); + switch (p->rec.subtype) + { + case DECLR_FUNC: + { + CType_t func = ctype_create("", CFUNC, p); /* function declr */ + if (flag & FLAG_FUNC_DEF) /* function def */ + func->rec.func.params = semantics_params(p->chd->next, scope); + else /* function declaration */ + { + cscope_enter(scope); + func->rec.func.params = semantics_params(p->chd->next, scope); + cscope_exit(scope); + /* incomplete type */ + func->rec.func.local = NULL; + func->rec.func.body = NULL; /* not a definition */ + } + func->rec.func.ret = typespec; /* might be an incomplete type */ + type = semantics_declr(p->chd, func, scope, flag | FLAG_FUNC_CHK); + if (typespec->type == CARR) + ERROR((p, "'%s' declared as function returning an array", + type->name)); + if (typespec->type == CFUNC) + ERROR((p, "'%s' declared as function returing a function", + type->name)); + } + break; + case DECLR_ARR: + { + CType_t arr = ctype_create("", CARR, p); /* array declr */ + CNode *rch = p->chd->next; + ExpType tl = semantics_exp(rch, scope); + if (calc_size(typespec) == -1) + ERROR((p, "array type has incomplete element type")); + if (!rch->ext.is_const) + ERROR((p, "size of array must be a constant")); + if (!IS_INT(tl.type->type)) + ERROR((p, "size of array has non-integer type")); + arr->rec.arr.elem = typespec; + arr->rec.arr.len = rch->ext.const_val; + type = semantics_declr(p->chd, arr, scope, 0); + } + break; + case '*': + { + CType_t ptr = ctype_create("", CPTR, p); /* pointer */ + ptr->rec.ref = typespec; + type = semantics_declr(p->chd, ptr, scope, 0); + } + break; + default: assert(0); + } + return type; +} + +void semantics_fields(CNode *p, CType_t type, CScope_t scope) { + CTable_t ct = ctable_create(bkdr_hash, ctable_cvar_print); + type->rec.st.fields = ct; + type->rec.st.flist = NULL; + for (p = p->chd; p; p = p->next) + { + CNode *declr = p->chd->next->chd; + for (; declr; declr = declr->next) + { + CVar_t var = semantics_declr(declr, + semantics_typespec(p->chd, scope), + scope, 0); + if (var->type->type == CFUNC) + ERROR((var->ast, "field '%s' declared as a function", var->name)); + /* types of fields are supposed to be complete */ + if (calc_size(var->type) == -1) + ERROR((var->ast, "field '%s' has incomplete type", var->name)); + if (!ctable_insert(ct, var->name, var, 0)) + ERROR((p, "duplicate member '%s'", var->name)); + var->next = type->rec.st.flist; + type->rec.st.flist = var; + } + } +} + +static void exp_check_aseq_(CType_t lhs, CType_t rhs, CNode *ast) { + NOT_IGNORE_VOID(lhs, ast); + NOT_IGNORE_VOID(rhs, ast); + switch (lhs->type) + { + case CSTRUCT: case CUNION: + if (!is_same_type(lhs, rhs)) + INCOMP_TYPE(ast); + break; + case CARR: case CFUNC: /* constant */ + INCOMP_TYPE(ast); + break; + case CINT: case CCHAR: + switch (rhs->type) + { + case CINT: case CCHAR: + ; break; /* ok */ + case CPTR: case CARR: + WARNING((ast, "assignment makes integer from pointer without a cast")); + break; + default: INCOMP_TYPE(ast); + } + break; + case CPTR: + switch (rhs->type) + { + case CPTR: case CARR: + if (!is_same_type(lhs->rec.ref, rhs->rec.ref)) + WARNING((ast, "assignment from incompatible pointer type")); + break; + case CINT: case CCHAR: + WARNING((ast, "assignment makes pointer from integer without a cast")); + break; + default: INCOMP_TYPE(ast); + } + break; + default: assert(0); + } +} + +void semantics_initr(CNode *p, CScope_t scope, CType_t type) { + switch (p->rec.subtype) + { + case INITR_NORM: + { + ExpType et = semantics_exp(p->chd, scope); + if (!scope->lvl && !p->chd->ext.is_const) /* in global scope */ + ERROR((p->chd, "initializer element is not constant")); + exp_check_aseq_(type, et.type, p); + } + break; + case INITR_ARR: + { + if (type->type == CARR) + type = type->rec.arr.elem; + else + ERROR((p, "invalid initializer")); + for (p = p->chd; p; p = p->next) + semantics_initr(p, scope, type); + } + break; + } +} + +void semantics_typedef(CNode *p, CType_t type, CScope_t scope) { + CNode *declr = p->chd->next; + for (p = declr->chd; p; p = p->next) + { + CVar_t var = semantics_declr(p, type, scope, 0); + CDef_t def = cdef_create(var->name, var->type, var->ast); + if (!cscope_push_def(scope, def, NS_ID)) + { + CSymbol_t lu = cscope_lookup(scope, def->name, NS_ID); + if (lu->kind != CDEF) + ERROR((def->ast, "'%s' redeclared as different kind of symbol", def->name)); + /* FIXME: `typedef int a()` is different from typedef `int a(int)` */ + if (!is_same_type(lu->rec.def->type, def->type)) + ERROR((def->ast, "conflicting types of '%s'", def->name)); + } + } +} + +CVar_t semantics_decl(CNode *p, CScope_t scope) { + CNode *declr = p->chd->next; + CType_t type = semantics_typespec(p->chd, scope); + CVar_t res = NULL; + int useful = 0; + if ((type->type == CSTRUCT || type->type == CUNION) && + (*type->name) != '\0') + { + cscope_push_type(scope, type, NS_TAG); + useful = 1; + } + if (p->type == TYPEDEF) + { + semantics_typedef(p, type, scope); + return NULL; + } + CHECK_TYPE(p, DECL); + if (declr->chd->type != NOP) + { + CNode *p; + for (p = declr->chd; p; p = p->next) + { + CNode *initr = p->chd->next; + CVar_t var = semantics_declr(p->chd, type, scope, 0); + if (var->type->type == CFUNC) + { + CType_t func = var->type; + CSymbol_t lu; + func->name = var->name; + if (initr->type == INITR) + ERROR((var->ast, "function '%s' is initialized like a variable", func->name)); + if (!cscope_push_type(scope, func, NS_ID)) + { + lu = cscope_lookup(scope, func->name, NS_ID); + if (lu->kind != CTYPE) + ERROR((func->ast, "'%s' redeclared as different kind of symbol", func->name)); + if (!is_same_type(lu->rec.type, func)) + ERROR((func->ast, "conflicting types of '%s'", func->name)); + type_merge(lu->rec.type, func); + } + } + else + { + if (scope->lvl && calc_size(var->type) == -1) + ERROR((var->ast, "storage size of '%s' isn’t known", var->name)); + var = var_merge(var, scope); + var->next = res; + res = var; + /* check initializer */ + if (initr->type == INITR) + { + var->initr = initr; + semantics_initr(initr, scope, var->type); + } + } + } + useful = 1; + } + if (!useful) + WARNING((type->ast, "useless declaration")); + return res; +} + +ExpType exp_check_aseq(ExpType lhs, ExpType rhs, CNode *ast) { + exp_check_aseq_(lhs.type, rhs.type, ast); + lhs.lval = 0; + return lhs; +} + +ExpType semantics_cast(CNode *p, CScope_t scope) { + CNode *chd = p->chd->next; + ExpType op = semantics_exp(chd, scope); + CVar_t var = semantics_declr(p->chd->chd->next, + semantics_typespec(p->chd->chd, scope), + scope, 0); + CType_t type = var->type; + free(var); + if (!IS_SCALAR(type->type)) + ERROR((p, "conversion to non-scalar type requested")); + if (!IS_SCALAR(op.type->type)) + ERROR((p, "aggregate value used where a scalar was expected")); + if (type->type == CARR) + ERROR((p, "cast specifies array type")); + if (type->type == CFUNC) + ERROR((p, "cast specifies function type")); + type->ast = p; + op.type = type; + op.lval = 0; + if ((p->ext.is_const &= !IS_INT(type->type))) + { + p->ext.const_val = chd->ext.const_val; + } + return op; +} + +ExpType exp_check_arith(ExpType op1, ExpType op2, CNode *p, char kind) { + CNode *lch = p->chd, + *rch = lch->next; + int t1 = op1.type->type, + t2 = op2.type->type; + ExpType res; + NOT_IGNORE_VOID(op1.type, p); + NOT_IGNORE_VOID(op2.type, p); + res.lval = 0; + res.type = basic_type_int; + if ((p->ext.is_const = lch->ext.is_const && rch->ext.is_const)) + { + long l = lch->ext.const_val, + r = rch->ext.const_val, + *a = &(p->ext.const_val); + switch (kind) + { + case '*': *a = l * r; break; + case '/': *a = l / r; break; + case '%': *a = l % r; break; + } + } + if (!(IS_ARITH(t1) && IS_ARITH(t2))) + ERROR((p, "invalid operands to binary operator")); + return res; +} + +ExpType exp_check_bitwise(ExpType op1, ExpType op2, CNode *p, char kind) { + CNode *lch = p->chd, + *rch = lch->next; + int t1 = op1.type->type, + t2 = op2.type->type; + ExpType res; + NOT_IGNORE_VOID(op1.type, p); + NOT_IGNORE_VOID(op2.type, p); + res.lval = 0; + res.type = basic_type_int; + if ((p->ext.is_const = lch->ext.is_const && rch->ext.is_const)) + { + long l = lch->ext.const_val, + r = rch->ext.const_val, + *a = &(p->ext.const_val); + switch (kind) + { + case 'l': *a = l << r; break; + case 'r': *a = l >> r; break; + case '&': *a = l & r; break; + case '|': *a = l | r; break; + case '^': *a = l ^ r; break; + } + } + if (!(IS_INT(t1) && IS_INT(t2))) + ERROR((p, "invalid operands to binary operator")); + return res; +} + +ExpType exp_check_add(ExpType op1, ExpType op2, CNode *p, char kind) { + CNode *lch = p->chd, + *rch = lch->next; + int t1 = op1.type->type, + t2 = op2.type->type; + NOT_IGNORE_VOID(op1.type, p); + NOT_IGNORE_VOID(op2.type, p); + if (kind == '+' && IS_PTR(t2)) + { + /* place the pointer type in the first place */ + int t = t1; + ExpType te = op1; + + t1 = t2; + t2 = t; + + op1 = op2; + op2 = te; + + CNode *n1 = p->chd; + CNode *n2 = n1->next; + n2->next = n1; + n1->next = NULL; + p->chd = n2; + } + if (t1 == CPTR && calc_size(op1.type->rec.ref) == -1) + ERROR((p, "invalid use of undefined type")); + if (t2 == CPTR && calc_size(op2.type->rec.ref) == -1) + ERROR((p, "invalid use of undefined type")); + if (kind == '-') + { + if (IS_PTR(t2) && !IS_PTR(t1)) + ERROR((p, "invalid operands to binary operator")); + } + else + { + if (!((IS_INT(t1) || IS_PTR(t1)) && IS_INT(t2))) + ERROR((p, "invalid operands to binary operator")); + } + if ((p->ext.is_const = lch->ext.is_const && rch->ext.is_const)) + { + long r = rch->ext.const_val, + *a = &(p->ext.const_val); + if (t1 == CARR) + { + int l = p->chd->ext.offset; + CType_t type; + p->ext.var = p->chd->ext.var; + if (t1 == CPTR) type = op1.type->rec.ref; + else type = op1.type->rec.arr.elem; + r *= calc_size(type); + switch (kind) + { + case '+': p->ext.offset = l + r; break; + case '-': p->ext.offset = l - r; break; + } + } + else + { + int l = lch->ext.const_val; + switch (kind) + { + case '+': *a = l + r; break; + case '-': *a = l - r; break; + } + } + } + op1.lval = 0; + return op1; /* int or pointer */ +} + +ExpType exp_check_int(ExpType op1, CNode *p) { + if (!IS_INT(op1.type->type)) + ERROR((p, "wrong type argument to unary operator")); + op1.lval = 0; + return op1; +} + +ExpType exp_check_scalar(ExpType op1, CNode *p) { + if (!IS_SCALAR(op1.type->type)) + ERROR((p, "wrong type argument to unary operator")); + op1.lval = 0; + return op1; +} + +ExpType exp_check_deref(ExpType op1, CNode *p) { + if (!IS_PTR(op1.type->type)) + ERROR((p, "invalid type argument of unary '*'")); + if (op1.type->rec.ref->type == CFUNC) + return op1; + op1.lval = 1; /* deref changes exp to lval */ + if (calc_size(op1.type = op1.type->rec.ref) == -1) + ERROR((p, "dereferencing pointer to incomplete type")); + return op1; +} + +ExpType exp_check_ref(ExpType op1, CNode *p) { + ExpType res; + CType_t t = op1.type; + if (t->type == CARR || (t->type == CPTR && t->rec.ref->type == CFUNC)) + return op1; + if (!op1.lval) + ERROR((p, "lvalue required as unary '&' operand")); + /* TODO: constant pointer folding */ + p->ext.is_const = 0; + /* should not be constant */ + res.lval = 0; + res.type = ctype_create("", CPTR, p); + res.type->rec.ref = op1.type; + return res; +} + +ExpType exp_check_sizeof(CNode *p, CScope_t scope) { + ExpType res, sub; + if (p->chd->type == DECLR) + { + CVar_t abs_declr = semantics_declr( + p->chd->chd->next, + semantics_typespec(p->chd->chd, scope), + scope, 0); + sub.type = abs_declr->type; + free(abs_declr); + } + else + sub = semantics_exp(p->chd, scope); + res.lval = 0; + res.type = basic_type_int; + p->ext.const_val = calc_size(sub.type); + p->ext.is_const = 1; + return res; +} + +ExpType exp_check_inc(ExpType op1, CNode *p) { + if (!IS_SCALAR(op1.type->type)) + ERROR((p, "wrong type argument to increment/decrement")); + if (!op1.lval) + ERROR((p, "lvalue required as increment/decrement operand")); + op1.lval = 0; + return op1; +} + +ExpType exp_check_logical(ExpType op1, ExpType op2, CNode *p, char kind) { + CNode *lch = p->chd, + *rch = lch->next; + int t1 = op1.type->type, + t2 = op2.type->type; + ExpType res; + NOT_IGNORE_VOID(op1.type, p); + NOT_IGNORE_VOID(op2.type, p); + res.lval = 0; + res.type = basic_type_int; + + if ((p->ext.is_const = lch->ext.is_const && rch->ext.is_const)) + { + long l = lch->ext.const_val, + r = rch->ext.const_val, + *a = &(p->ext.const_val); + switch (kind) + { + case '&': *a = l && r; break; + case '|': *a = l || r; break; + } + } + + if (!(IS_SCALAR(t1) && IS_SCALAR(t2))) + ERROR((p, "invalid operands to binary operator")); + return res; +} + +ExpType exp_check_ass(ExpType lhs, ExpType rhs, CNode *p) { + NOT_IGNORE_VOID(lhs.type, p); + NOT_IGNORE_VOID(rhs.type, p); + if (!lhs.lval) + ERROR((p, "lvalue required as left operand of assignment")); + switch (p->rec.subtype) + { + case '=' : return exp_check_aseq(lhs, rhs, p); + case ASS_MUL: return exp_check_aseq(lhs, exp_check_arith(lhs, rhs, p, '*'), p); + case ASS_DIV: return exp_check_aseq(lhs, exp_check_arith(lhs, rhs, p, '/'), p); + case ASS_MOD: return exp_check_aseq(lhs, exp_check_arith(lhs, rhs, p, '%'), p); + + case ASS_ADD: return exp_check_aseq(lhs, exp_check_add(lhs, rhs, p, '+'), p); + case ASS_SUB: return exp_check_aseq(lhs, exp_check_add(lhs, rhs, p, '-'), p); + + case ASS_SHL: return exp_check_aseq(lhs, exp_check_bitwise(lhs, rhs, p, 'l'), p); + case ASS_SHR: return exp_check_aseq(lhs, exp_check_bitwise(lhs, rhs, p, 'r'), p); + case ASS_AND: return exp_check_aseq(lhs, exp_check_bitwise(lhs, rhs, p, '&'), p); + case ASS_XOR: return exp_check_aseq(lhs, exp_check_bitwise(lhs, rhs, p, '^'), p); + case ASS_OR: return exp_check_aseq(lhs, exp_check_bitwise(lhs, rhs, |