From 946b600ddd829039770636007a4491f999a68ef5 Mon Sep 17 00:00:00 2001
From: bellard <bellard>
Date: Thu, 1 Nov 2001 14:48:10 +0000
Subject: [PATCH] added union/struct + correct local symbols

---
 tcc.c | 624 ++++++++++++++++++++++++++++++++++++++++------------------
 1 file changed, 430 insertions(+), 194 deletions(-)

diff --git a/tcc.c b/tcc.c
index fd75f2bb..e2085aa1 100644
--- a/tcc.c
+++ b/tcc.c
@@ -1,3 +1,22 @@
+/*
+ *  TCC - Tiny C Compiler
+ * 
+ *  Copyright (c) 2001 Fabrice Bellard
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
 #include <stdio.h>
 
 #define TEXT_SIZE       20000
@@ -5,9 +24,19 @@
 #define SYM_TABLE_SIZE  10000
 #define VAR_TABLE_SIZE  4096
 
-/* vac: offset of variables 
-   vat: type of variables
-   loc : local variable index
+/* symbol management */
+typedef struct Sym {
+    int v;    /* symbol token */
+    int t;    /* associated type */
+    int c;    /* associated number */
+    struct Sym *next; /* next related symbol */
+    struct Sym *prev; /* prev symbol in stack */
+} Sym;
+
+#define SYM_STRUCT 0x40000000 /* struct/union/enum symbol space */
+#define SYM_FIELD  0x20000000 /* struct/union field symbol space */
+
+/* loc : local variable index
    glo : global variable index
    parm : parameter variable index
    ind : output code ptr
@@ -15,10 +44,12 @@
    prog: output code
    astk: arg position stack
 */
-int tok, tok1, *vac, *vat, rsym, 
-    prog, ind, loc, glo, file, vt, 
+void *file;
+int tok, tok1, rsym, 
+    prog, ind, loc, glo, vt, 
     vc, *macro_stack, *macro_stack_ptr, line_num;
 char *idtable, *idptr, *filename;
+Sym *define_stack, *global_stack, *local_stack, *label_stack;
 
 /* The current value can be: */
 #define VT_CONST   0x0002  /* constant in vc */
@@ -43,29 +74,30 @@ char *idtable, *idptr, *filename;
  * Basic types:
  *
  * VT_BYTE indicate a char
- *
+ * VT_UNSIGNED indicates unsigned type
  *
  * otherwise integer type is assumed.
- *  */
+ *  
+ */
 
-#define VT_BYTE     0x00001  /* byte pointer. HARDCODED VALUE */
+#define VT_BYTE     0x00001  /* byte type, HARDCODED VALUE */
 #define VT_PTRMASK  0x00f00  /* pointer mask */
 #define VT_PTRINC   0x00100  /* pointer increment */
 #define VT_FUNC     0x01000  /* function type */
 #define VT_UNSIGNED 0x02000  /* unsigned type */
 #define VT_ARRAY    0x04000  /* array type (only used in parsing) */
-#define VT_TYPE     0x07f01  /* type mask */
-#define VT_TYPEN   0xffff80fe  /* ~VT_TYPE */
-#define VT_FUNCN   -4097
+#define VT_TYPE    0xffffff01  /* type mask */
+#define VT_TYPEN   0x000000fe  /* ~VT_TYPE */
+#define VT_FUNCN   -4097       /* ~VT_FUNC */
 
-#define VT_EXTERN   0x08000   /* extern definition */
-#define VT_STATIC   0x10000   /* static variable */
+#define VT_EXTERN  0x008000   /* extern definition */
+#define VT_STATIC  0x010000   /* static variable */
 
 /* Special infos */
-#define VT_LABEL    0x40000  /* goto label symbol */
-#define VT_DEFINE   0x80000  /* special value for #defined symbols */
-#define VT_ENUM    0x100000  /* enum definition */
-#define VT_TYPEDEF 0x200000  /* typedef definition */
+#define VT_ENUM    0x020000  /* enum definition */
+#define VT_STRUCT  0x040000  /* struct/union definition */
+#define VT_TYPEDEF 0x080000  /* typedef definition */
+#define VT_STRUCT_SHIFT 20   /* structure/enum name shift (12 bits lefts) */
 
 /* token values */
 #define TOK_INT      256
@@ -95,12 +127,11 @@ char *idtable, *idptr, *filename;
 #define TOK_REGISTER 278
 #define TOK_SIGNED   279
 
-/* unsupported types. Must have contiguous values */
-#define TOK_FLOAT    280
-#define TOK_DOUBLE   281
+#define TOK_FLOAT    280 /* unsupported */
+#define TOK_DOUBLE   281 /* unsupported */
+
 #define TOK_STRUCT   282
 #define TOK_UNION    283
-
 #define TOK_TYPEDEF  284
 #define TOK_DEFAULT  285
 #define TOK_ENUM     286
@@ -115,12 +146,13 @@ char *idtable, *idptr, *filename;
 #define TOK_LAND 0xa0
 #define TOK_LOR  0xa1
 
-#define TOK_DEC  0xa2
-#define TOK_MID  0xa3 /* inc/dec, to void constant */
-#define TOK_INC  0xa4
+#define TOK_DEC   0xa2
+#define TOK_MID   0xa3 /* inc/dec, to void constant */
+#define TOK_INC   0xa4
+#define TOK_ARROW 0xa7 
 
-#define TOK_SHL  0x01 
-#define TOK_SHR  0x02 
+#define TOK_SHL   0x01 
+#define TOK_SHR   0x02 
   
 /* assignement operators : normal operator or 0x80 */
 #define TOK_A_MOD 0xa5
@@ -136,8 +168,15 @@ char *idtable, *idptr, *filename;
 
 #ifdef TINY
 #define expr_eq() expr()
+#else
+void sum();
+void next();
+void expr_eq();
+void expr();
+void decl();
 #endif
 
+
 int inp()
 {
 #if 0
@@ -170,6 +209,12 @@ void error(char *msg)
     exit(1);
 }
 
+void expect(char *msg)
+{
+    printf("%s:%d: %s expected\n", filename, line_num, msg);
+    exit(1);
+}
+
 void warning(char *msg)
 {
     printf("%s:%d: warning: %s\n", filename, line_num, msg);
@@ -187,7 +232,7 @@ void skip(c)
 void test_lvalue()
 {
     if (!(vt & VT_LVAL))
-        error("lvalue expected\n");
+        expect("lvalue");
 }
 
 #else
@@ -212,10 +257,74 @@ char *get_tok_str(int v)
     return p;
 }
 
+/* find a symbol and return its associated structure. 's' is the top
+   of the symbol stack */
+Sym *sym_find1(Sym *s, int v)
+{
+    while (s) {
+        if (s->v == v)
+            return s;
+        s = s->prev;
+    }
+    return 0;
+}
+
+Sym *sym_push1(Sym **ps, int v, int t, int c)
+{
+    Sym *s;
+    s = malloc(sizeof(Sym));
+    if (!s)
+        error("memory full");
+    s->v = v;
+    s->t = t;
+    s->c = c;
+    s->next = 0;
+    s->prev = *ps;
+    *ps = s;
+    return s;
+}
+
+/* find a symbol in the right symbol space */
+Sym *sym_find(int v)
+{
+    Sym *s;
+    s = sym_find1(local_stack, v);
+    if (!s)
+        s = sym_find1(global_stack, v);
+    return s;
+}
+
+/* push a given symbol on the symbol stack */
+Sym *sym_push(int v, int t, int c)
+{
+    //    printf("sym_push: %s type=%x\n", get_tok_str(v), t);
+    if (local_stack)
+        return sym_push1(&local_stack, v, t, c);
+    else
+        return sym_push1(&global_stack, v, t, c);
+}
+
+/* pop symbols until top reaches 'b' */
+void sym_pop(Sym **ps, Sym *b)
+{
+    Sym *s, *ss;
+
+    s = *ps;
+    while(s != b) {
+        ss = s->prev;
+        //        printf("sym_pop: %s type=%x\n", get_tok_str(s->v), s->t);
+        free(s);
+        s = ss;
+    }
+    *ps = b;
+}
+
+
 void next()
 {
     int c, v;
     char *q, *p;
+    Sym *s;
 
     /* special 'ungettok' case for label parsing */
     if (tok1) {
@@ -260,8 +369,7 @@ void next()
             if (tok == TOK_DEFINE) {
                 next();
                 /* now tok is the macro symbol */
-                vat[tok] = VT_DEFINE;
-                vac[tok] = ftell(file);
+                sym_push1(&define_stack, tok, 0, ftell(file));
             }
             /* ignore preprocessor or shell */
             while (c != '\n')
@@ -297,16 +405,16 @@ void next()
         if (p == idptr)
             idptr = q;
         /* eval defines */
-        if (vat[tok] & VT_DEFINE) {
+        if (s = sym_find1(define_stack, tok)) {
             *macro_stack_ptr++ = ftell(file);
-            fseek(file, vac[tok], 0);
+            fseek(file, s->c, 0);
             next();
         }
     } else {
 #ifdef TINY
         q = "<=\236>=\235!=\225++\244--\242==\224";
 #else
-        q = "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253-=\255*=\252/=\257%=\245&=\246^=\336|=\374";
+        q = "<=\236>=\235!=\225&&\240||\241++\244--\242==\224<<\1>>\2+=\253-=\255*=\252/=\257%=\245&=\246^=\336|=\374->\247";
 #endif
         /* two chars */
         v = inp();
@@ -460,19 +568,35 @@ int gtst(inv, t)
     return t;
 }
 
-int type_size(t)
+/* return type size. Put alignment at 'a' */
+int type_size(int t, int *a)
 {
-    if ((t & VT_PTRMASK) >= VT_PTRINC | (t & VT_TYPE) == 0)
+    Sym *s;
+
+    /* int, enum or pointer */
+    if ((t & VT_PTRMASK) >= VT_PTRINC | 
+        (t & VT_TYPE) == 0 |
+        (t & VT_ENUM)) {
+        *a = 4;
         return 4;
-    else
+    } else if (t & VT_STRUCT) {
+        /* struct/union */
+        s = sym_find(((unsigned)t >> VT_STRUCT_SHIFT) | SYM_STRUCT);
+        *a = 4; /* XXX: cannot store it yet. Doing that is safe */
+        return s->c;
+    } else {
+        *a = 1;
         return 1;
+    }
 }
 
 /* return the number size in bytes of a given type */
 int incr_value(t)
 {
+    int a;
+
     if ((t & VT_PTRMASK) >= VT_PTRINC)
-        return type_size(t - VT_PTRINC);
+        return type_size(t - VT_PTRINC, &a);
     else
         return 1;
 }
@@ -557,10 +681,95 @@ int expr_const()
 {
     expr_eq();
     if ((vt & (VT_CONST | VT_LVAL)) != VT_CONST)
-        error("constant expected");
+        expect("constant");
     return vc;
 }
 
+#ifndef TINY
+
+/* enum/struct/union declaration */
+int struct_decl(u)
+{
+    int a, t, b, v, size, align, maxalign, c;
+    Sym *slast, *s, *ss;
+
+    a = tok; /* save decl type */
+    next();
+    v = 0;
+    if (tok != '{') {
+        v = tok;
+        next();
+        /* struct already defined ? return it */
+        /* XXX: check consistency */
+        if (s = sym_find(v | SYM_STRUCT)) {
+            if (s->t != a)
+                error("invalid type");
+            u = u | (v << VT_STRUCT_SHIFT);
+            return u;
+        }
+    }
+    s = sym_push(v | SYM_STRUCT, a, 0);
+    /* put struct/union/enum name in type */
+    u = u | (v << VT_STRUCT_SHIFT);
+    
+    if (tok == '{') {
+        next();
+        /* cannot be empty */
+        c = 0;
+        maxalign = 0;
+        slast = 0;
+        while (1) {
+            if (a == TOK_ENUM) {
+                v = tok;
+                next();
+                if (tok == '=') {
+                    next();
+                    c = expr_const();
+                }
+                sym_push(v, VT_CONST, c);
+                if (tok == ',')
+                    next();
+                c++;
+            } else {
+                b = ist();
+                while (1) {
+                    t = typ(&v, b, &size);
+                    if (t & (VT_FUNC | VT_TYPEDEF))
+                        error("invalid type");
+                    /* XXX: align & correct type size */
+                    v |= SYM_FIELD;
+                    size = type_size(t, &align);
+                    if (a == TOK_STRUCT) {
+                        c = (c + align - 1) & -align;
+                        ss = sym_push(v, t, c);
+                        c += size;
+                    } else {
+                        ss = sym_push(v, t, 0);
+                        if (size > c)
+                            c = size;
+                    }
+                    if (align > maxalign)
+                        maxalign = align;
+                    ss->next = slast;
+                    slast = ss;
+                    if (tok == ';' || tok == -1)
+                        break;
+                    skip(',');
+                }
+                skip(';');
+            }
+            if (tok == '}')
+                break;
+        }
+        skip('}');
+        s->next = slast;
+        /* size for struct/union, dummy for enum */
+        s->c = (c + maxalign - 1) & -maxalign; 
+    }
+    return u;
+}
+#endif
+
 /* return 0 if no type declaration. otherwise, return the basic type
    and skip it. 
    XXX: A '2' is ored to ensure non zero return if int type.
@@ -568,41 +777,25 @@ int expr_const()
 int ist()
 {
     int t, n, v;
+    Sym *s;
 
     t = 0;
     while(1) {
+#ifndef TINY
         if (tok == TOK_ENUM) {
-            next();
-            if (tok != '{')
-                next();
-            if (tok == '{') {
-                next();
-                n = 0;
-                while (tok != '}' & tok != -1) {
-                    v = tok;
-                    next();
-                    if (tok == '=') {
-                        next();
-                        n = expr_const();
-                    }
-                    vat[v] = VT_CONST;
-                    vac[v] = n;
-                    if (tok != ',')
-                        break;
-                    next();
-                    n++;
-                }
-                skip('}');
-            }
-            t |= VT_ENUM;
-        } else {
+            t = struct_decl(VT_ENUM);
+        } else if (tok == TOK_STRUCT || tok == TOK_UNION) {
+            t = struct_decl(VT_STRUCT);
+        } else
+#endif
+        {
             if (tok == TOK_CHAR | tok == TOK_VOID) {
                 t |= VT_BYTE;
             } else if (tok == TOK_INT |
                        (tok >= TOK_CONST & tok <= TOK_SIGNED)) {
                 /* ignored types */
-            } else if (tok >= TOK_FLOAT & tok <= TOK_UNION) {
-                error("unsupported type");
+            } else if (tok == TOK_FLOAT & tok == TOK_DOUBLE) {
+                error("floats not supported");
             } else if (tok == TOK_EXTERN) {
                 t |= VT_EXTERN;
             } else if (tok == TOK_STATIC) {
@@ -612,10 +805,10 @@ int ist()
             } else if (tok == TOK_TYPEDEF) {
                 t |= VT_TYPEDEF;
             } else {
-                v = vat[tok];
-                if (!(v & VT_TYPEDEF))
+                s = sym_find(tok);
+                if (!s || !(s->t & VT_TYPEDEF))
                     break;
-                t = v & ~VT_TYPEDEF;
+                t = s->t & ~VT_TYPEDEF;
             }
             next();
         }
@@ -654,21 +847,22 @@ int typ(int *v, int t, int *array_size_ptr)
         if (tok == '(') {
             /* function declaration */
             next();
+            /* push a dummy symbol to force local symbol stack usage */
+            sym_push1(&local_stack, 0, 0, 0);
             p = 4; 
             while (tok != ')') {
                 /* read param name and compute offset */
-            if (t = ist())
-                t = typ(&n, t, 0); /* XXX: should accept both arg/non arg if v == 0 */
-            else {
-                n = tok;
-                t = 0;
-                next();
-            }
-            p = p + 4;
-            vat[n] = VT_LOCAL | VT_LVAL | t;
-            vac[n] = p;
-            if (tok == ',')
-                next();
+                if (t = ist())
+                    t = typ(&n, t, 0); /* XXX: should accept both arg/non arg if v == 0 */
+                else {
+                    n = tok;
+                    t = 0;
+                    next();
+                }
+                p = p + 4;
+                sym_push(n, VT_LOCAL | VT_LVAL | t, p);
+                if (tok == ',')
+                    next();
             }
             next(); /* skip ')' */
             if (u)
@@ -697,20 +891,24 @@ int typ(int *v, int t, int *array_size_ptr)
 }
 
 /* define a new external reference to a function 'v' of type 'u' */
-void external_func(v, u)
+Sym *external_func(v, u)
 {
     int t, n;
-    t = vat[v];
-    if (t == 0) {
+    Sym *s;
+    s = sym_find(v);
+    if (!s) {
         n = dlsym(0, get_tok_str(v));
         if (n == 0) {
-            vat[v] = u | VT_CONST | VT_LVAL | VT_FORWARD;
-            vac[v] = 0; /* used to generate symbol list */
+            /* used to generate symbol list */
+            s = sym_push1(&global_stack, 
+                          v, u | VT_CONST | VT_LVAL | VT_FORWARD, 0);
         } else {
-            vat[v] = u | VT_CONST | VT_LVAL; /* int f() */
-            vac[v] = n;
+            /* int f() */
+            s = sym_push1(&global_stack,
+                          v, u | VT_CONST | VT_LVAL, n);
         }
     }
+    return s;
 }
 
 /* read a number in base b */
@@ -762,6 +960,7 @@ int getq(n)
 void unary()
 {
     int n, t, ft, fc, p;
+    Sym *s;
 
     if (isnum(tok)) {
         /* number */
@@ -817,7 +1016,7 @@ void unary()
                 gv();
 #ifndef TINY
             if (!(vt & VT_PTRMASK))
-                error("pointer expected");
+                expect("pointer");
 #endif
             vt = (vt - VT_PTRINC) | VT_LVAL;
         } else if (t == '&') {
@@ -859,86 +1058,115 @@ void unary()
             }
         } else 
         {
-            if (vat[t] == 0) {
+            s = sym_find(t);
+            if (!s) {
                 if (tok != '(')
                     error("undefined symbol");
                 /* for simple function calls, we tolerate undeclared
                    external reference */
-                external_func(t, VT_FUNC); /* int() function */
+                s = external_func(t, VT_FUNC); /* int() function */
             }
-            vset(vat[t], vac[t]);
-            /* if forward reference, we must point to vac[t] */
+            vset(s->t, s->c);
+            /* if forward reference, we must point to s->c */
             if (vt & VT_FORWARD)
-                vc = t;
+                vc = (int)&s->c;
         }
     }
     
     /* post operations */
-    if (tok == TOK_INC | tok == TOK_DEC) {
-        inc(POST_ADD, tok);
-        next();
-    } else 
-    if (tok == '[') {
+    while (1) {
+        if (tok == TOK_INC | tok == TOK_DEC) {
+            inc(POST_ADD, tok);
+            next();
+        } else if (tok == '.' | tok == TOK_ARROW) {
+            /* field */ 
+            if (tok == '.') {
+                test_lvalue();
+                vt = (vt & VT_LVALN) + VT_PTRINC;
+            }
+            next();
+            /* expect pointer on structure */
+            if (!(vt & VT_STRUCT) || (vt & VT_PTRMASK) == 0)
+                expect("struct or union");
+            s = sym_find(((unsigned)vt >> VT_STRUCT_SHIFT) | SYM_STRUCT);
+            /* find field */
+            tok |= SYM_FIELD;
+            while (s = s->next) {
+                if (s->v == tok)
+                    break;
+            }
+            if (!s)
+                error("field not found");
+            /* add field offset to pointer */
+            gv();
+            if (s->c)
+                oad(0x05, s->c);
+            /* change type to field type, and set to lvalue */
+            vt = (vt & VT_TYPEN) | VT_LVAL | s->t;
+            next();
+        } else if (tok == '[') {
 #ifndef TINY
-        if (!(vt & VT_PTRMASK))
-            error("pointer expected");
+            if (!(vt & VT_PTRMASK))
+                expect("pointer");
 #endif
-        gen_op('+', -1);
-        /* dereference pointer */
-        vt = (vt - VT_PTRINC) | VT_LVAL;
-        skip(']');
-    } else
-    if (tok == '(') {
-        /* function call  */
-        /* lvalue is implied */
-        vt = vt & VT_LVALN;
-        if ((vt & VT_CONST) == 0) {
-            /* evaluate function address */
-            gv();
-            o(0x50); /* push %eax */
-        }
-        ft = vt;
-        fc = vc;
+            gen_op('+', -1);
+            /* dereference pointer */
+            vt = (vt - VT_PTRINC) | VT_LVAL;
+            skip(']');
+        } else if (tok == '(') {
+            /* function call  */
+            /* lvalue is implied */
+            vt = vt & VT_LVALN;
+            if ((vt & VT_CONST) == 0) {
+                /* evaluate function address */
+                gv();
+                o(0x50); /* push %eax */
+            }
+            ft = vt;
+            fc = vc;
 
-        next();
-        t = 0;
-        while (tok != ')') {
-            t = t + 4;
-            expr_eq();
-            gv();
-            o(0x50); /* push %eax */
-            if (tok == ',')
-                next();
-        }
-        skip(')');
-        /* horrible, but needed : convert to native ordering (could
-           parse parameters in reverse order, but would cost more
-           code) */
-        n = 0;
-        p = t - 4;
-        while (n < p) {
-            oad(0x24848b, p); /* mov x(%esp,1), %eax */
-            oad(0x248487, n); /* xchg   x(%esp,1), %eax */
-            oad(0x248489, p); /* mov %eax, x(%esp,1) */
-            n = n + 4;
-            p = p - 4;
-        }
-        if (ft & VT_CONST) {
-            /* forward reference */
-            if (ft & VT_FORWARD) {
-                vac[fc] = psym(0xe8, vac[fc]);
-            } else
-                oad(0xe8, fc - ind - 5);
-            /* return value is variable, and take type from function proto */
-            vt = VT_VAR | (ft & VT_TYPE & VT_FUNCN);
+            next();
+            t = 0;
+            while (tok != ')') {
+                t = t + 4;
+                expr_eq();
+                gv();
+                o(0x50); /* push %eax */
+                if (tok == ',')
+                    next();
+            }
+            skip(')');
+            /* horrible, but needed : convert to native ordering (could
+               parse parameters in reverse order, but would cost more
+               code) */
+            n = 0;
+            p = t - 4;
+            while (n < p) {
+                oad(0x24848b, p); /* mov x(%esp,1), %eax */
+                oad(0x248487, n); /* xchg   x(%esp,1), %eax */
+                oad(0x248489, p); /* mov %eax, x(%esp,1) */
+                n = n + 4;
+                p = p - 4;
+            }
+            if (ft & VT_CONST) {
+                /* forward reference */
+                if (ft & VT_FORWARD) {
+                    *(int *)fc = psym(0xe8, *(int *)fc);
+                } else
+                    oad(0xe8, fc - ind - 5);
+                /* return value is variable, and take type from function proto */
+                vt = VT_VAR | (ft & VT_TYPE & VT_FUNCN);
+            } else {
+                oad(0x2494ff, t); /* call *xxx(%esp) */
+                t = t + 4;
+                /* return value is variable, int */
+                vt = VT_VAR;
+            }
+            if (t)
+                oad(0xc481, t);
         } else {
-            oad(0x2494ff, t); /* call *xxx(%esp) */
-            t = t + 4;
-            /* return value is variable, int */
-            vt = VT_VAR;
+            break;
         }
-        if (t)
-            oad(0xc481, t);
     }
 }
 
@@ -1082,6 +1310,7 @@ void expr()
 void block(int *bsym, int *csym, int *case_sym, int *def_sym)
 {
     int a, b, c, d;
+    Sym *s;
 
     if (tok == TOK_IF) {
         /* if test */
@@ -1115,9 +1344,12 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym)
     } else if (tok == '{') {
         next();
         /* declarations */
+        s = local_stack;
         decl(VT_LOCAL);
         while (tok != '}')
             block(bsym, csym, case_sym, def_sym);
+        /* pop locally defined symbols */
+        sym_pop(&local_stack, s);
         next();
     } else if (tok == TOK_RETURN) {
         next();
@@ -1209,7 +1441,7 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym)
         next();
         a = expr_const();
         if (!case_sym)
-            error("switch expected");
+            expect("switch");
         gsym(*case_sym);
         oad(0x3d, a); /* cmp $xxx, %eax */
         *case_sym = psym(0x850f, 0); /* jne xxx */
@@ -1220,7 +1452,7 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym)
         next();
         skip(':');
         if (!def_sym)
-            error("switch expected");
+            expect("switch");
         if (*def_sym)
             error("too many 'default'");
         *def_sym = ind;
@@ -1228,19 +1460,15 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym)
     } else
     if (tok == TOK_GOTO) {
         next();
-        a = vat[tok];
-        if (a == 0) {
-            /* put forward definition */
-            a = VT_LABEL | VT_FORWARD;
-            vat[tok] = a;
-            vac[tok] = 0;
-        } else if (!(a & VT_LABEL))
-            error("invalid label name");
+        s = sym_find1(label_stack, tok);
+        /* put forward definition if needed */
+        if (!s)
+            s = sym_push1(&label_stack, tok, VT_FORWARD, 0);
         /* label already defined */
-        if (a & VT_FORWARD) 
-            vac[tok] = psym(0xe9, vac[tok]); /* jmp xxx */
+        if (s->t & VT_FORWARD) 
+            s->c = psym(0xe9, s->c); /* jmp xxx */
         else
-            oad(0xe9, vac[tok] - ind - 5); /* jmp xxx */
+            oad(0xe9, s->c - ind - 5); /* jmp xxx */
         next();
         skip(';');
     } else
@@ -1251,16 +1479,16 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym)
         if (tok == ':') {
             next();
             /* label case */
-            a = vat[b];
-            if (a != 0) {
-                if (!(a & VT_LABEL))
-                    error("invalid label name");
-                else if (!(a & VT_FORWARD))
+            s = sym_find1(label_stack, b);
+            if (s) {
+                if (!(s->t & VT_FORWARD))
                     error("multiple defined label");
-                gsym(vac[b]);
+                gsym(s->c);
+                s->c = ind;
+                s->t = 0;
+            } else {
+                sym_push1(&label_stack, b, 0, ind);
             }
-            vac[b] = ind;
-            vat[b] = VT_LABEL;
             block(bsym, csym, case_sym, def_sym);
         } else {
             /* expression case: go backward of one token */
@@ -1279,37 +1507,47 @@ void block(int *bsym, int *csym, int *case_sym, int *def_sym)
 void decl(l)
 {
     int *a, t, b, s, align, v, u, n;
+    Sym *sym, *slocal;
 
     while (b = ist()) {
-        if ((b & VT_ENUM) && tok == ';') {
+        if ((b & (VT_ENUM | VT_STRUCT)) && tok == ';') {
             /* we accept no variable after */
             next();
             continue;
         }
-
         while (1) { /* iterate thru each declaration */
             s = 1;
+            slocal = local_stack; /* save local stack position, to restore it */
             t = typ(&v, b, &s);
             if (tok == '{') {
                 /* patch forward references */
-                if (vat[v] & VT_FORWARD)
-                    gsym(vac[v]);
-                /* put function address */
-                vat[v] = VT_CONST | VT_LVAL | t;
-                vac[v] = ind;
+                if ((sym = sym_find(v)) && (sym->t & VT_FORWARD)) {
+                    gsym(sym->c);
+                    sym->c = ind;
+                    sym->t = VT_CONST | VT_LVAL | t;
+                } else {
+                    /* put function address */
+                    sym_push1(&global_stack, v, VT_CONST | VT_LVAL | t, ind);
+                }
                 loc = 0;
                 o(0xe58955); /* push   %ebp, mov    %esp, %ebp */
-                a = oad(0xec81, 0); /* sub $xxx, %esp */
+                a = (int *)oad(0xec81, 0); /* sub $xxx, %esp */
                 rsym = 0;
                 block(0, 0, 0, 0);
                 gsym(rsym);
                 o(0xc3c9); /* leave, ret */
                 *a = (-loc + 3) & -4; /* align local size to word & 
                                          save local variables */
+                sym_pop(&label_stack, 0); /* reset label stack */
+                sym_pop(&local_stack, 0); /* reset local stack */
                 break;
             } else {
+                /* reset local stack (needed because of dummy function
+                   parameters */
+                sym_pop(&local_stack, slocal);
                 if (t & VT_TYPEDEF) {
-                    vat[v] = t; /* save typedefed type */
+                    /* save typedefed type */
+                    sym_push(v, t, 0);
                 } else if (t & VT_FUNC) {
                     /* external function definition */
                     external_func(v, t);
@@ -1319,28 +1557,28 @@ void decl(l)
                         t |= VT_LVAL;
                     if (t & VT_EXTERN) {
                         /* external variable */
+                        /* XXX: factorize with external function def */
                         n = dlsym(NULL, get_tok_str(v));
                         if (!n)
                             error("unknown external variable");
-                        vat[v] = VT_CONST | t;
-                        vac[v] = n;
+                        sym_push(v, VT_CONST | t, n);
                     } else {
                         u = l;
                         if (t & VT_STATIC)
                             u = VT_CONST;
-                        vat[v] = u | t;
+                        u |= t;
                         if (t & VT_ARRAY)
                             t -= VT_PTRINC; 
-                        align = type_size(t);
+                        align = type_size(t, &align);
                         s *= align;
-                        if (u == VT_LOCAL) {
+                        if (u & VT_LOCAL) {
                             /* allocate space down on the stack */
                             loc = (loc - s) & -align;
-                            vac[v] = loc;
+                            sym_push(v, u, loc);
                         } else {
                             /* allocate space up in the data space */
                             glo = (glo + align - 1) & -align;
-                            vac[v] = glo;
+                            sym_push(v, u, glo);
                             glo += s;
                         }
                     }
@@ -1357,6 +1595,7 @@ void decl(l)
 
 int main(int c, char **v)
 {
+    Sym *s;
     int (*t)();
     if (c < 2) {
         printf("usage: tc src\n");
@@ -1384,8 +1623,6 @@ int main(int c, char **v)
 #endif
     glo = malloc(DATA_SIZE);
     prog = malloc(TEXT_SIZE);
-    vac = malloc(VAR_TABLE_SIZE);
-    vat = malloc(VAR_TABLE_SIZE);
     macro_stack = malloc(256);
     macro_stack_ptr = macro_stack;
     ind = prog;
@@ -1401,11 +1638,10 @@ int main(int c, char **v)
         return 0;
     }
 #else
-    t = vac[TOK_MAIN];
-#ifndef TINY
-    if (!t)
+    s = sym_find(TOK_MAIN);
+    if (!s)
         error("main() not defined");
-#endif
+    t = s->c;
     return (*t)(c - 1, v);
 #endif
 }