Added better (?) USERTYPE management.
authorRyan C. Gordon <icculus@icculus.org>
Mon, 22 Feb 2010 01:57:37 -0500
changeset 846 10eb8be2c919
parent 845 d10d31b9b695
child 847 cb7997b93a1a
Added better (?) USERTYPE management. Now handles scoping of typedefs, etc.
mojoshader_compiler.c
mojoshader_parser_hlsl.lemon
--- a/mojoshader_compiler.c	Mon Feb 22 01:56:12 2010 -0500
+++ b/mojoshader_compiler.c	Mon Feb 22 01:57:37 2010 -0500
@@ -506,6 +506,22 @@
 } CompilationUnitVariable;
 
 
+// This tracks typedefs and structs, and notes when they enter/leave scope.
+
+typedef struct UserTypeScopeStack
+{
+    const char *symbol;
+    const char *datatype;
+    struct UserTypeScopeStack *next;
+} UserTypeScopeStack;
+
+typedef struct UserTypeMap
+{
+    HashTable *types;
+    UserTypeScopeStack *scope;
+} UserTypeMap;
+
+
 // Compile state, passed around all over the place.
 
 typedef struct Context
@@ -521,8 +537,7 @@
     StringBucket *string_hashtable[256];
     const char *sourcefile;  // current source file that we're parsing.
     unsigned int sourceline; // current line in sourcefile that we're parsing.
-    const char *usertypes[512];  // !!! FIXME: dynamic allocation
-    int usertype_count;
+    UserTypeMap usertypes;
     CompilationUnit *ast;  // Abstract Syntax Tree
 } Context;
 
@@ -564,6 +579,91 @@
     printf("%s:%u: %s\n", ctx->sourcefile, ctx->sourceline, str);
 } // fail
 
+#define dbg printf
+
+
+static void usertypemap_nuke(const void *k, const void *v) { /* no-op. */ }
+
+static int create_usertypemap(Context *ctx)
+{
+    UserTypeMap *map = &ctx->usertypes;
+
+    map->scope = NULL;
+    map->types = hash_create(255, hash_hash_string, hash_keymatch_string,
+                             usertypemap_nuke, 1, ctx->malloc, ctx->free,
+                             ctx->malloc_data);
+    if (!map->types)
+    {
+        out_of_memory(ctx);
+        return 0;
+    } // if
+
+    return 1;
+} // create_usertypemap
+
+static void push_usertype(Context *ctx, const char *sym, const char *datatype)
+{
+    UserTypeMap *map = &ctx->usertypes;
+    UserTypeScopeStack *item;
+
+    dbg("push_usertype: %s -> %s\n", sym, datatype);
+
+    item = (UserTypeScopeStack *) Malloc(ctx, sizeof (UserTypeScopeStack));
+    if (item == NULL)
+        return;
+
+    if (sym != NULL)
+    {
+        if (hash_insert(map->types, sym, datatype) == -1)
+        {
+            Free(ctx, item);
+            return;
+        }
+    } // if
+
+    item->symbol = sym;  // cached strings, don't copy.
+    item->datatype = datatype;
+    item->next = map->scope;
+    map->scope = item;
+} // push_usertype
+
+static void pop_usertype(Context *ctx)
+{
+    UserTypeMap *map = &ctx->usertypes;
+    UserTypeScopeStack *item = map->scope;
+    if (!item)
+        return;
+    dbg("pop_usertype: %s -> %s\n", item->symbol, item->datatype);
+    if (item->symbol)
+        hash_remove(map->types, item->symbol);
+    map->scope = item->next;
+    Free(ctx, item);
+} // pop_usertype
+
+static void push_scope(Context *ctx)
+{
+    dbg("push_scope\n");
+    push_usertype(ctx, NULL, NULL);
+} // push_scope
+
+static void pop_scope(Context *ctx)
+{
+    dbg("pop_scope\n");
+    UserTypeMap *map = &ctx->usertypes;
+    assert(map->scope != NULL);
+    while ((map->scope) && (map->scope->symbol))
+        pop_usertype(ctx);
+} // push_scope
+
+static void destroy_usertypemap(Context *ctx)
+{
+    UserTypeMap *map = &ctx->usertypes;
+    while (map->scope)
+        pop_usertype(ctx);
+    hash_destroy(map->types);
+} // destroy_usertypemap
+
+
 // These functions are mostly for construction and cleanup of nodes in the
 //  parse tree. Mostly this is simple allocation and initialization, so we
 //  can do as little in the lemon code as possible, and then sort it all out
@@ -1308,30 +1408,16 @@
     // don't free (stmt) here, the class-specific functions do it.
 } // delete_statement
 
+// This is only for initial parsing: we only care that it exists at this point!
 static void add_usertype(Context *ctx, const char *sym)
 {
-    // !!! FIXME: error if this is a reserved keyword.
-    // !!! FIXME: dynamic allocation
-    assert(ctx->usertype_count < STATICARRAYLEN(ctx->usertypes));
-    ctx->usertypes[ctx->usertype_count++] = sym;
-    ctx->usertype_count++;
+    push_usertype(ctx, sym, NULL);
 } // add_usertype
 
-static int is_usertype(const Context *ctx, const char *token,
-                       const unsigned int tokenlen)
+static int is_usertype(const Context *ctx, const char *token)
 {
-
-    // !!! FIXME: dynamic allocation
-    // !!! FIXME: should probably redesign this anyhow.
-    int i;
-    for (i = 0; i < ctx->usertype_count; i++)
-    {
-        const char *type = ctx->usertypes[i];
-        if (strncmp(type, token, tokenlen) == 0)
-            return type[tokenlen] == '\0';
-    } // for
-
-    return 0;
+    const void *value;
+    return hash_find(ctx->usertypes.types, token, &value);
 } // is_usertype
 
 
@@ -1510,7 +1596,7 @@
 } // is_semantic
 #endif
 
-static int convert_to_lemon_token(const Context *ctx, const char *token,
+static int convert_to_lemon_token(Context *ctx, const char *token,
                                   unsigned int tokenlen, const Token tokenval)
 {
     switch (tokenval)
@@ -1756,7 +1842,9 @@
 
             #undef tokencmp
 
-            if (is_usertype(ctx, token, tokenlen))
+            // get a canonical copy of the string now, as we'll need it.
+            token = cache_string(ctx, token, tokenlen);
+            if (is_usertype(ctx, token))
                 return TOKEN_HLSL_USERTYPE;
             return TOKEN_HLSL_IDENTIFIER;
 
@@ -1789,8 +1877,8 @@
     if (ctx->preprocessor != NULL)
         preprocessor_end(ctx->preprocessor);
     // !!! FIXME: free ctx->errors
-    // !!! FIXME: free ctx->usertypes
     delete_compilation_unit(ctx, ctx->ast);
+    destroy_usertypemap(ctx);
     free_string_cache(ctx);
 } // destroy_context
 
@@ -1823,6 +1911,8 @@
 
     // !!! FIXME: check if (ctx.preprocessor == NULL)...
 
+    create_usertypemap(&ctx);  // !!! FIXME: check for failure.
+
     void *pParser = ParseHLSLAlloc(m, d);
 
     #if DEBUG_COMPILER_PARSER
@@ -1876,6 +1966,14 @@
         } // switch
 
         ParseHLSL(pParser, lemon_token, data, &ctx);
+
+        // this probably isn't perfect, but it's good enough for surviving
+        //  the parse. We'll sort out correctness once we have a tree.
+        if (lemon_token == TOKEN_HLSL_LBRACE)
+            push_scope(&ctx);
+        else if (lemon_token == TOKEN_HLSL_RBRACE)
+            pop_scope(&ctx);
+
     } while (tokenval != TOKEN_EOI);
 
     ParseHLSLFree(pParser, f, d);
--- a/mojoshader_parser_hlsl.lemon	Mon Feb 22 01:56:12 2010 -0500
+++ b/mojoshader_parser_hlsl.lemon	Mon Feb 22 01:57:37 2010 -0500
@@ -97,8 +97,8 @@
 %type typedef { Typedef * }
 %destructor typedef { delete_typedef(ctx, $$); }
 // !!! FIXME: should CONST be here, or in datatype?
-typedef(A) ::= TYPEDEF CONST datatype(B) scalar_or_array(C). { A = new_typedef(ctx, 1, B, C); }
-typedef(A) ::= TYPEDEF datatype(B) scalar_or_array(C). { A = new_typedef(ctx, 0, B, C); }
+typedef(A) ::= TYPEDEF CONST datatype(B) scalar_or_array(C). { A = new_typedef(ctx, 1, B, C); add_usertype(ctx, C->identifier); }
+typedef(A) ::= TYPEDEF datatype(B) scalar_or_array(C). { A = new_typedef(ctx, 0, B, C); add_usertype(ctx, C->identifier); }
 
 %type function_signature { FunctionSignature * }
 %destructor function_signature { delete_function_signature(ctx, $$); }
@@ -222,12 +222,11 @@
 
 %type struct_declaration { StructDeclaration * }
 %destructor struct_declaration { delete_struct_declaration(ctx, $$); }
-struct_declaration(A) ::= STRUCT IDENTIFIER(B) LBRACE struct_member_list(C) RBRACE.
-{
-    A = new_struct_declaration(ctx, B.string, C);
-    // !!! FIXME: we need to decide what scope we are in and make sure this is only a valid usertype at that point.
-    add_usertype(ctx, B.string);
-}
+struct_declaration(A) ::= struct_intro(B) LBRACE struct_member_list(C) RBRACE. { A = new_struct_declaration(ctx, B, C); }
+
+// This has to be separate from struct_declaration so that the struct is in the usertypemap when parsing its members.
+%type struct_intro { const char * }
+struct_intro(A) ::= STRUCT IDENTIFIER(B). { A = B.string; add_usertype(ctx, A); }
 
 %type struct_member_list { StructMembers * }
 %destructor struct_member_list { delete_struct_member(ctx, $$); }