From b15f269fbe990827471f29a030acfbc85232fc2c Mon Sep 17 00:00:00 2001
From: bellard <bellard>
Date: Tue, 30 Oct 2001 22:43:19 +0000
Subject: [PATCH] added enum, typedefs, goto, switch

---
 tcc.c | 207 ++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 172 insertions(+), 35 deletions(-)

diff --git a/tcc.c b/tcc.c
index d95257ed..fd75f2bb 100644
--- a/tcc.c
+++ b/tcc.c
@@ -15,7 +15,7 @@
    prog: output code
    astk: arg position stack
 */
-int tok, *vac, *vat, rsym, 
+int tok, tok1, *vac, *vat, rsym, 
     prog, ind, loc, glo, file, vt, 
     vc, *macro_stack, *macro_stack_ptr, line_num;
 char *idtable, *idptr, *filename;
@@ -62,7 +62,10 @@ char *idtable, *idptr, *filename;
 #define VT_STATIC   0x10000   /* 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 */
 
 /* token values */
 #define TOK_INT      256
@@ -97,8 +100,8 @@ char *idtable, *idptr, *filename;
 #define TOK_DOUBLE   281
 #define TOK_STRUCT   282
 #define TOK_UNION    283
-#define TOK_TYPEDEF  284
 
+#define TOK_TYPEDEF  284
 #define TOK_DEFAULT  285
 #define TOK_ENUM     286
 
@@ -214,6 +217,13 @@ void next()
     int c, v;
     char *q, *p;
 
+    /* special 'ungettok' case for label parsing */
+    if (tok1) {
+        tok = tok1;
+        tok1 = 0;
+        return;
+    }
+
     while(1) {
         c = inp();
 #ifndef TINY
@@ -543,32 +553,72 @@ void gen_op(op, l)
     }
 }
 
+int expr_const()
+{
+    expr_eq();
+    if ((vt & (VT_CONST | VT_LVAL)) != VT_CONST)
+        error("constant expected");
+    return vc;
+}
+
 /* 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.
  */
 int ist()
 {
-    int t;
+    int t, n, v;
+
     t = 0;
     while(1) {
-        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_TYPEDEF) {
-            error("unsupported type");
-        } else if (tok == TOK_EXTERN) {
-            t |= VT_EXTERN;
-        } else if (tok == TOK_STATIC) {
-            t |= VT_STATIC;
-        } else if (tok == TOK_UNSIGNED) {
-            t |= VT_UNSIGNED;
+        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 {
-            break;
+            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_EXTERN) {
+                t |= VT_EXTERN;
+            } else if (tok == TOK_STATIC) {
+                t |= VT_STATIC;
+            } else if (tok == TOK_UNSIGNED) {
+                t |= VT_UNSIGNED;
+            } else if (tok == TOK_TYPEDEF) {
+                t |= VT_TYPEDEF;
+            } else {
+                v = vat[tok];
+                if (!(v & VT_TYPEDEF))
+                    break;
+                t = v & ~VT_TYPEDEF;
+            }
+            next();
         }
-        next();
         t |= 2;
     }
     return t;
@@ -630,14 +680,13 @@ int typ(int *v, int t, int *array_size_ptr)
             if (t & VT_ARRAY) 
                 error("multi dimension arrays not supported");
             next();
-            vc = 0;
+            n = 0;
             if (tok != ']') {
-                expr();
+                n = expr_const();
                 if (array_size_ptr)
-                    *array_size_ptr = vc;
+                    *array_size_ptr = n;
             }
-            if ((vt & (VT_CONST | VT_LVAL)) != VT_CONST | 
-                (vc <= 0 & array_size_ptr != 0))
+            if (n <= 0 & array_size_ptr != 0)
                 error("invalid array size");
             skip(']');
             t = (t + VT_PTRINC) | VT_ARRAY;
@@ -1030,7 +1079,7 @@ void expr()
 
 #endif
 
-void block(int *bsym, int *csym)
+void block(int *bsym, int *csym, int *case_sym, int *def_sym)
 {
     int a, b, c, d;
 
@@ -1041,13 +1090,13 @@ void block(int *bsym, int *csym)
         expr();
         skip(')');
         a = gtst(1, 0);
-        block(bsym, csym);
+        block(bsym, csym, case_sym, def_sym);
         c = tok;
         if (c == TOK_ELSE) {
             next();
             d = psym(0xe9, 0); /* jmp */
             gsym(a);
-            block(bsym, csym);
+            block(bsym, csym, case_sym, def_sym);
             gsym(d); /* patch else jmp */
         } else
             gsym(a);
@@ -1059,7 +1108,7 @@ void block(int *bsym, int *csym)
         skip(')');
         a = gtst(1, 0);
         b = 0;
-        block(&a, &b);
+        block(&a, &b, case_sym, def_sym);
         oad(0xe9, d - ind - 5); /* jmp */
         gsym(a);
         gsym_addr(b, d);
@@ -1068,7 +1117,7 @@ void block(int *bsym, int *csym)
         /* declarations */
         decl(VT_LOCAL);
         while (tok != '}')
-            block(bsym, csym);
+            block(bsym, csym, case_sym, def_sym);
         next();
     } else if (tok == TOK_RETURN) {
         next();
@@ -1118,7 +1167,7 @@ void block(int *bsym, int *csym)
             gsym(e);
         }
         skip(')');
-        block(&a, &b);
+        block(&a, &b, case_sym, def_sym);
         oad(0xe9, c - ind - 5); /* jmp */
         gsym(a);
         gsym_addr(b, c);
@@ -1128,7 +1177,7 @@ void block(int *bsym, int *csym)
         a = 0;
         b = 0;
         d = ind;
-        block(&a, &b);
+        block(&a, &b, case_sym, def_sym);
         skip(TOK_WHILE);
         skip('(');
         gsym(b);
@@ -1138,11 +1187,91 @@ void block(int *bsym, int *csym)
         skip(')');
         gsym(a);
     } else
+    if (tok == TOK_SWITCH) {
+        next();
+        skip('(');
+        expr();
+        gv();
+        skip(')');
+        a = 0;
+        b = 0;
+        c = 0;
+        block(&a, csym, &b, &c);
+        /* if no default, jmp after switch */
+        if (c == 0)
+            c = ind;
+        /* default label */
+        gsym_addr(b, c);
+        /* break label */
+        gsym(a);
+    } else
+    if (tok == TOK_CASE) {
+        next();
+        a = expr_const();
+        if (!case_sym)
+            error("switch expected");
+        gsym(*case_sym);
+        oad(0x3d, a); /* cmp $xxx, %eax */
+        *case_sym = psym(0x850f, 0); /* jne xxx */
+        skip(':');
+        block(bsym, csym, case_sym, def_sym);
+    } else 
+    if (tok == TOK_DEFAULT) {
+        next();
+        skip(':');
+        if (!def_sym)
+            error("switch expected");
+        if (*def_sym)
+            error("too many 'default'");
+        *def_sym = ind;
+        block(bsym, csym, case_sym, 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");
+        /* label already defined */
+        if (a & VT_FORWARD) 
+            vac[tok] = psym(0xe9, vac[tok]); /* jmp xxx */
+        else
+            oad(0xe9, vac[tok] - ind - 5); /* jmp xxx */
+        next();
+        skip(';');
+    } else
 #endif
     {
-        if (tok != ';')
-            expr();
-        skip(';');
+        b = tok;
+        next();
+        if (tok == ':') {
+            next();
+            /* label case */
+            a = vat[b];
+            if (a != 0) {
+                if (!(a & VT_LABEL))
+                    error("invalid label name");
+                else if (!(a & VT_FORWARD))
+                    error("multiple defined label");
+                gsym(vac[b]);
+            }
+            vac[b] = ind;
+            vat[b] = VT_LABEL;
+            block(bsym, csym, case_sym, def_sym);
+        } else {
+            /* expression case: go backward of one token */
+            /* XXX: currently incorrect if number/string/char */
+            tok1 = tok;
+            tok = b;
+            if (tok != ';') {
+                expr();
+            }
+            skip(';');
+        }
     }
 }
 
@@ -1152,6 +1281,12 @@ void decl(l)
     int *a, t, b, s, align, v, u, n;
 
     while (b = ist()) {
+        if ((b & VT_ENUM) && tok == ';') {
+            /* we accept no variable after */
+            next();
+            continue;
+        }
+
         while (1) { /* iterate thru each declaration */
             s = 1;
             t = typ(&v, b, &s);
@@ -1166,14 +1301,16 @@ void decl(l)
                 o(0xe58955); /* push   %ebp, mov    %esp, %ebp */
                 a = oad(0xec81, 0); /* sub $xxx, %esp */
                 rsym = 0;
-                block(0, 0);
+                block(0, 0, 0, 0);
                 gsym(rsym);
                 o(0xc3c9); /* leave, ret */
                 *a = (-loc + 3) & -4; /* align local size to word & 
                                          save local variables */
                 break;
             } else {
-                if (t & VT_FUNC) {
+                if (t & VT_TYPEDEF) {
+                    vat[v] = t; /* save typedefed type */
+                } else if (t & VT_FUNC) {
                     /* external function definition */
                     external_func(v, t);
                 } else {