Add a type system.

We now track the declared type of variables and functions.
diff --git a/libacc/acc.cpp b/libacc/acc.cpp
index 83fc2fe..0d26a5c 100644
--- a/libacc/acc.cpp
+++ b/libacc/acc.cpp
@@ -1492,8 +1492,6 @@
                 probe.pText = (char*) pText;
                 Token* pValue = (Token*) hashmapGet(mpMap, &probe);
                 if (pValue) {
-                    // printf("intern - found existing %s for %d\n",
-                    // pValue->pText, pValue->id);
                     return pValue->id;
                 }
             }
@@ -1508,7 +1506,6 @@
             pToken->id = mTokens.size() + TOKEN_BASE;
             mTokens.push_back(pToken);
             hashmapPut(mpMap, pToken, pToken);
-            // printf("intern - new token %s %d\n", pToken->pText, pToken->id);
             return pToken->id;
         }
 
@@ -1657,6 +1654,10 @@
             * ensure(1) = c;
         }
 
+        void append(String& other) {
+            appendBytes(other.getUnwrapped(), other.len());
+        }
+
         char* orphan() {
             char* result = mpBase;
             mpBase = 0;
@@ -1759,12 +1760,15 @@
         int oldCh;
     };
 
+    struct Type;
+
     struct VariableInfo {
         void* pAddress;
         void* pForward; // For a forward direction, linked list of data to fix up
         tokenid_t tok;
         size_t level;
         VariableInfo* pOldDefinition;
+        Type* pType;
     };
 
     class SymbolStack {
@@ -1820,6 +1824,12 @@
             return pNewV;
         }
 
+        VariableInfo* add(Type* pType) {
+            VariableInfo* pVI = add(pType->id);
+            pVI->pType = pType;
+            return pVI;
+        }
+
         void forEach(bool (*fn)(VariableInfo*, void*), void* context) {
             for (size_t i = 0; i < mStack.size(); i++) {
                 if (! fn(mStack[i], context)) {
@@ -1865,6 +1875,10 @@
     SymbolStack mGlobals;
     SymbolStack mLocals;
 
+    Type* mkpInt;
+    Type* mkpChar;
+    Type* mkpVoid;
+
     InputStream* file;
 
     CodeBuf codeBuf;
@@ -1975,11 +1989,16 @@
         * (char*) 0 = 0;
     }
 
-    VariableInfo* VI(tokenid_t t) {
-        if ( t < TOK_SYMBOL || t-TOK_SYMBOL >= mTokenTable.size()) {
+    void assert(bool isTrue) {
+        if (!isTrue) {
             internalError();
         }
-        // printf("Looking up %s %d\n", nameof(t), t);
+    }
+
+    VariableInfo* VI(tokenid_t t) {
+        if ( t < TOK_SYMBOL || ((size_t) (t-TOK_SYMBOL)) >= mTokenTable.size()) {
+            internalError();
+        }
         VariableInfo* pV = mTokenTable[t].mpVariableInfo;
         if (pV && pV->tok != t) {
             internalError();
@@ -2209,7 +2228,8 @@
         {
             String buf;
             decodeToken(buf, tok);
-            printf("%s\n", buf.getUnwrapped());        }
+            fprintf(stderr, "%s\n", buf.getUnwrapped());
+        }
 #endif
     }
 
@@ -2296,6 +2316,14 @@
         next();
     }
 
+    bool accept(intptr_t c) {
+        if (tok == c) {
+            next();
+            return true;
+        }
+        return false;
+    }
+
     /* l is one if '=' parsing wanted (quick hack) */
     void unary(intptr_t l) {
         intptr_t n, t, a;
@@ -2553,105 +2581,223 @@
         }
     }
 
-    typedef int Type;
-    static const Type TY_UNKNOWN = 0;
-    static const Type TY_INT = 1;
-    static const Type TY_CHAR = 2;
-    static const Type TY_VOID = 3;
-    static const int TY_BASE_TYPE_MASK = 0xf;
-    static const int TY_INDIRECTION_MASK = 0xf0;
-    static const int TY_INDIRECTION_SHIFT = 4;
-    static const int MAX_INDIRECTION_COUNT = 15;
+    enum TypeTag {
+        TY_INT, TY_CHAR, TY_VOID, TY_POINTER, TY_FUNC, TY_PARAM
+    };
 
-    Type getBaseType(Type t) {
-        return t & TY_BASE_TYPE_MASK;
+    struct Type {
+        TypeTag tag;
+        tokenid_t id; // For function arguments
+        Type* pHead;
+        Type* pTail;
+    };
+
+    Type* createType(TypeTag tag, Type* pHead, Type* pTail, Arena& arena) {
+        assert(tag >= TY_INT && tag <= TY_PARAM);
+        Type* pType = (Type*) arena.alloc(sizeof(Type));
+        memset(pType, 0, sizeof(*pType));
+        pType->tag = tag;
+        pType->pHead = pHead;
+        pType->pTail = pTail;
+        return pType;
     }
 
-    int getIndirectionCount(Type t) {
-        return (TY_INDIRECTION_MASK & t) >> TY_INDIRECTION_SHIFT;
+    void decodeType(String& buffer, Type* pType) {
+        if (pType == NULL) {
+            buffer.appendCStr("null");
+            return;
+        }
+        buffer.append('(');
+        String temp;
+        if (pType->id != 0) {
+            decodeToken(temp, pType->id);
+            buffer.append(temp);
+            buffer.append(' ');
+        }
+        bool printHead = false;
+        bool printTail = false;
+        switch (pType->tag) {
+            case TY_INT:
+                buffer.appendCStr("int");
+                break;
+            case TY_CHAR:
+                buffer.appendCStr("char");
+                break;
+            case TY_VOID:
+                buffer.appendCStr("void");
+                break;
+            case TY_POINTER:
+                buffer.appendCStr("*");
+                printHead = true;
+                break;
+            case TY_FUNC:
+                buffer.appendCStr("func");
+                printHead = true;
+                printTail = true;
+                break;
+            case TY_PARAM:
+                buffer.appendCStr("param");
+                printHead = true;
+                printTail = true;
+                break;
+            default:
+                String temp;
+                temp.printf("Unknown tag %d", pType->tag);
+                buffer.append(temp);
+                break;
+        }
+        if (printHead) {
+            buffer.append(' ');
+            decodeType(buffer, pType->pHead);
+        }
+        if (printTail) {
+            buffer.append(' ');
+            decodeType(buffer, pType->pTail);
+        }
+        buffer.append(')');
     }
 
-    void setIndirectionCount(Type& t, int count) {
-        t = ((TY_INDIRECTION_MASK & (count << TY_INDIRECTION_SHIFT))
-                | (t & ~TY_INDIRECTION_MASK));
+    void printType(Type* pType) {
+        String buffer;
+        decodeType(buffer, pType);
+        fprintf(stderr, "%s\n", buffer.getUnwrapped());
     }
 
-    bool acceptType(Type& t) {
-        t = TY_UNKNOWN;
+    Type* acceptPrimitiveType(Arena& arena) {
+        Type* pType;
         if (tok == TOK_INT) {
-            t = TY_INT;
+            pType = mkpInt;
         } else if (tok == TOK_CHAR) {
-            t = TY_CHAR;
+            pType = mkpChar;
         } else if (tok == TOK_VOID) {
-            t = TY_VOID;
+            pType = mkpVoid;
         } else {
-            return false;
+            return NULL;
         }
         next();
-        return true;
+        return pType;
     }
 
-    Type acceptPointerDeclaration(Type& base) {
-        Type t = base;
-        int indirectionCount = 0;
-        while (tok == '*' && indirectionCount <= MAX_INDIRECTION_COUNT) {
+    Type* acceptDeclaration(const Type* pBaseType, Arena& arena) {
+        Type* pType = createType(pBaseType->tag, pBaseType->pHead,
+                                 pBaseType->pTail, arena);
+        tokenid_t declName;
+        if (pType) {
+            pType = acceptDecl2(pType, declName, arena);
+            pType->id = declName;
+            // fprintf(stderr, "Parsed a declaration:       ");
+            // printType(pType);
+        }
+        return pType;
+    }
+
+    Type* expectDeclaration(const Type* pBaseType, Arena& arena) {
+        Type* pType = acceptDeclaration(pBaseType, arena);
+        if (! pType) {
+            error("Expected a declaration");
+        }
+        return pType;
+    }
+
+    Type* acceptDecl2(Type* pType, tokenid_t& declName, Arena& arena) {
+        while (tok == '*') {
+            pType = createType(TY_POINTER, pType, NULL, arena);
             next();
-            indirectionCount++;
         }
-        if (indirectionCount > MAX_INDIRECTION_COUNT) {
-            error("Too many levels of pointer. Max %d", MAX_INDIRECTION_COUNT);
-        }
-        setIndirectionCount(t, indirectionCount);
-        return t;
+        pType = acceptDecl3(pType, declName, arena);
+        return pType;
     }
 
-    void expectType(Type& t) {
-        if (!acceptType(t)) {
+    Type* acceptDecl3(Type* pType, tokenid_t& declName, Arena& arena) {
+        if (accept('(')) {
+            pType = acceptDecl2(pType, declName, arena);
+            skip(')');
+        } else {
+            declName = acceptSymbol();
+        }
+        while (tok == '(') {
+            // Function declaration
+            skip('(');
+            Type* pTail = acceptArgs(arena);
+            pType = createType(TY_FUNC, pType, pTail, arena);
+            skip(')');
+        }
+        return pType;
+    }
+
+    Type* acceptArgs(Arena& arena) {
+        Type* pHead = NULL;
+        Type* pTail = NULL;
+        for(;;) {
+            Type* pBaseArg = acceptPrimitiveType(arena);
+            if (pBaseArg) {
+                Type* pArg = acceptDeclaration(pBaseArg, arena);
+                if (pArg) {
+                    Type* pParam = createType(TY_PARAM, pArg, NULL, arena);
+                    if (!pHead) {
+                        pHead = pParam;
+                        pTail = pParam;
+                    } else {
+                        pTail->pTail = pParam;
+                        pTail = pParam;
+                    }
+                }
+            }
+            if (! accept(',')) {
+                break;
+            }
+        }
+        return pHead;
+    }
+
+    Type* expectPrimitiveType(Arena& arena) {
+        Type* pType = acceptPrimitiveType(arena);
+        if (!pType) {
             String buf;
             decodeToken(buf, tok);
             error("Expected a type, got %s", buf.getUnwrapped());
         }
+        return pType;
     }
 
-    void addGlobalSymbol() {
-        VariableInfo* pVI = VI(tok);
+    void addGlobalSymbol(Type* pDecl) {
+        tokenid_t t = pDecl->id;
+        VariableInfo* pVI = VI(t);
         if(pVI && pVI->pAddress) {
-            reportDuplicate();
+            reportDuplicate(t);
         }
-        mGlobals.add(tok);
+        mGlobals.add(pDecl);
     }
 
-    void reportDuplicate() {
-        error("Duplicate definition of %s", nameof(tok));
+    void reportDuplicate(tokenid_t t) {
+        error("Duplicate definition of %s", nameof(t));
     }
 
-    void addLocalSymbol() {
-        if (mLocals.isDefinedAtCurrentLevel(tok)) {
-            reportDuplicate();
+    void addLocalSymbol(Type* pDecl) {
+        tokenid_t t = pDecl->id;
+        if (mLocals.isDefinedAtCurrentLevel(t)) {
+            reportDuplicate(t);
         }
-        mLocals.add(tok);
+        mLocals.add(pDecl);
     }
 
     void localDeclarations() {
         intptr_t a;
-        Type base;
+        Type* pBaseType;
 
-        while (acceptType(base)) {
+        while ((pBaseType = acceptPrimitiveType(mLocalArena)) != NULL) {
             while (tok != ';' && tok != EOF) {
-                Type t = acceptPointerDeclaration(t);
-                int variableAddress = 0;
-                if (checkSymbol()) {
-                    addLocalSymbol();
-                    if (tok) {
-                        loc = loc + 4;
-                        variableAddress = -loc;
-                        VI(tok)->pAddress = (void*) variableAddress;
-                    }
+                Type* pDecl = expectDeclaration(pBaseType, mLocalArena);
+                if (!pDecl) {
+                    break;
                 }
-                next();
-                if (tok == '=') {
+                int variableAddress = 0;
+                addLocalSymbol(pDecl);
+                loc = loc + 4;
+                variableAddress = -loc;
+                VI(pDecl->id)->pAddress = (void*) variableAddress;
+                if (accept('=')) {
                     /* assignment */
-                    next();
                     expr();
                     pGen->storeR0(variableAddress);
                 }
@@ -2672,7 +2818,11 @@
         } else if (token == TOK_NUM) {
             buffer.printf("numeric constant");
         } else if (token >= 0 && token < 256) {
-            buffer.printf("char \'%c\'", token);
+            if (token < 32) {
+                buffer.printf("'\\x%02x'", token);
+            } else {
+                buffer.printf("'%c'", token);
+            }
         } else if (token >= TOK_KEYWORD && token < TOK_SYMBOL) {
             buffer.printf("keyword \"%s\"", nameof(token));
         } else {
@@ -2690,32 +2840,43 @@
         return result;
     }
 
+    tokenid_t acceptSymbol() {
+        tokenid_t result = 0;
+        if (tok >= TOK_SYMBOL) {
+            result = tok;
+            next();
+        } else {
+            String temp;
+            decodeToken(temp, tok);
+            error("Expected symbol. Got %s", temp.getUnwrapped());
+        }
+        return result;
+    }
+
     void globalDeclarations() {
         while (tok != EOF) {
-            Type base;
-            expectType(base);
-            Type t = acceptPointerDeclaration(t);
-            if (tok < TOK_SYMBOL) {
-                error("Unexpected token %d", tok);
+            Type* pBaseType = expectPrimitiveType(mGlobalArena);
+            if (!pBaseType) {
                 break;
             }
-            if (! isDefined(tok)) {
-                addGlobalSymbol();
+            Type* pDecl = expectDeclaration(pBaseType, mGlobalArena);
+            if (!pDecl) {
+                break;
             }
-            VariableInfo* name = VI(tok);
+            if (! isDefined(pDecl->id)) {
+                addGlobalSymbol(pDecl);
+            }
+            VariableInfo* name = VI(pDecl->id);
             if (name && name->pAddress) {
-                error("Already defined global %s",
-                      mTokenString.getUnwrapped());
+                error("Already defined global %s", nameof(pDecl->id));
             }
-            next();
-            if (tok == ',' || tok == ';' || tok == '=') {
+            if (pDecl->tag < TY_FUNC) {
                 // it's a variable declaration
                 for(;;) {
-                    if (name) {
+                    if (name && !name->pAddress) {
                         name->pAddress = (int*) allocGlobalSpace(4);
                     }
-                    if (tok == '=') {
-                        next();
+                    if (accept('=')) {
                         if (tok == TOK_NUM) {
                             if (name) {
                                 * (int*) name->pAddress = tokc;
@@ -2725,17 +2886,21 @@
                             error("Expected an integer constant");
                         }
                     }
-                    if (tok != ',') {
+                    if (!accept(',')) {
                         break;
                     }
-                    skip(',');
-                    t = acceptPointerDeclaration(t);
-                    addGlobalSymbol();
-                    name = VI(tok);
-                    next();
+                    pDecl = expectDeclaration(pBaseType, mGlobalArena);
+                    if (!pDecl) {
+                        break;
+                    }
+                    if (! isDefined(pDecl->id)) {
+                        addGlobalSymbol(pDecl);
+                    }
+                    name = VI(pDecl->id);
                 }
                 skip(';');
             } else {
+                // Function declaration
                 if (name) {
                     /* patch forward references (XXX: does not work for function
                      pointers) */
@@ -2743,28 +2908,18 @@
                     /* put function address */
                     name->pAddress = (void*) codeBuf.getPC();
                 }
-                skip('(');
+                // Calculate stack offsets for parameters
                 mLocals.pushLevel();
                 intptr_t a = 8;
                 int argCount = 0;
-                while (tok != ')' && tok != EOF) {
-                    Type aType;
-                    expectType(aType);
-                    aType = acceptPointerDeclaration(aType);
-                    if (checkSymbol()) {
-                        addLocalSymbol();
-                        if (tok) {
-                            /* read param name and compute offset */
-                            VI(tok)->pAddress = (void*) a;
-                            a = a + 4;
-                        }
-                    }
-                    next();
-                    if (tok == ',')
-                        next();
+                for (Type* pP = pDecl->pTail; pP; pP = pP->pTail) {
+                    Type* pArg = pP->pHead;
+                    addLocalSymbol(pArg);
+                    /* read param name and compute offset */
+                    VI(pArg->id)->pAddress = (void*) a;
+                    a = a + 4;
                     argCount++;
                 }
-                skip(')');
                 rsym = loc = 0;
                 a = pGen->functionEntry(argCount);
                 block(0, true);
@@ -2878,6 +3033,7 @@
         mLocals.setTokenTable(&mTokenTable);
 
         internKeywords();
+        createPrimitiveTypes();
         codeBuf.init(ALLOC_SIZE);
         setArchitecture(NULL);
         if (!pGen) {
@@ -2904,6 +3060,12 @@
         return result;
     }
 
+    void createPrimitiveTypes() {
+        mkpInt = createType(TY_INT, NULL, NULL, mGlobalArena);
+        mkpChar = createType(TY_CHAR, NULL, NULL, mGlobalArena);
+        mkpVoid = createType(TY_VOID, NULL, NULL, mGlobalArena);
+    }
+
     void checkForUndefinedForwardReferences() {
         mGlobals.forEach(static_ufrcFn, this);
     }