Added stringify operator ("#") to preprocessor.
authorRyan C. Gordon <icculus@icculus.org>
Wed, 24 Feb 2010 21:20:37 -0500
changeset 865 4eb06464212d
parent 864 aa3bec973a21
child 866 3fc643a4f742
Added stringify operator ("#") to preprocessor.
mojoshader_internal.h
mojoshader_lexer.re
mojoshader_preprocessor.c
unit_tests/preprocessor/output/stringify-operator-basic
unit_tests/preprocessor/output/stringify-operator-basic.correct
--- a/mojoshader_internal.h	Wed Feb 24 14:00:25 2010 -0500
+++ b/mojoshader_internal.h	Wed Feb 24 21:20:37 2010 -0500
@@ -408,6 +408,7 @@
     TOKEN_GEQ,
     TOKEN_EQL,
     TOKEN_NEQ,
+    TOKEN_HASH,
     TOKEN_HASHHASH,
 
     // This is returned at the end of input...no more to process.
@@ -475,6 +476,7 @@
     Token tokenval;
     int pushedback;
     const unsigned char *lexer_marker;
+    int is_macro;
     int report_whitespace;
     int asm_comments;
     unsigned int orig_length;
--- a/mojoshader_lexer.re	Wed Feb 24 14:00:25 2010 -0500
+++ b/mojoshader_lexer.re	Wed Feb 24 21:20:37 2010 -0500
@@ -128,6 +128,7 @@
     ">="            { RET(TOKEN_GEQ); }
     "=="            { RET(TOKEN_EQL); }
     "!="            { RET(TOKEN_NEQ); }
+    "#"             { RET(TOKEN_HASH); }
     "##"            { RET(TOKEN_HASHHASH); }
     "("             { RET('('); }
     ")"             { RET(')'); }
--- a/mojoshader_preprocessor.c	Wed Feb 24 14:00:25 2010 -0500
+++ b/mojoshader_preprocessor.c	Wed Feb 24 21:20:37 2010 -0500
@@ -146,6 +146,7 @@
         TOKENCASE(TOKEN_GEQ);
         TOKENCASE(TOKEN_EQL);
         TOKENCASE(TOKEN_NEQ);
+        TOKENCASE(TOKEN_HASH);
         TOKENCASE(TOKEN_HASHHASH);
         TOKENCASE(TOKEN_PP_INCLUDE);
         TOKENCASE(TOKEN_PP_LINE);
@@ -533,7 +534,8 @@
 
 static int push_source(Context *ctx, const char *fname, const char *source,
                        unsigned int srclen, unsigned int linenum,
-                       MOJOSHADER_includeClose close_callback, Define *defs)
+                       MOJOSHADER_includeClose close_callback, Define *defs,
+                       const int is_macro)
 {
     if (srclen == 0)
         return 1;  // nothing to do: just pretend you did it.
@@ -564,6 +566,7 @@
     state->defines = defs;
     state->next = ctx->include_stack;
     state->asm_comments = ctx->asm_comments;
+    state->is_macro = is_macro;
 
     print_debug_lexing_position(state);
 
@@ -681,13 +684,14 @@
         } // else
     } // if
 
-    if ((okay) && (!push_source(ctx, fname, source, sourcelen, 1, NULL, NULL)))
+    if ((okay) && (!push_source(ctx,fname,source,sourcelen,1,NULL,NULL,0)))
         okay = 0;
 
     if ((okay) && (define_include != NULL))
     {
         okay = push_source(ctx, "<predefined macros>", define_include,
-                           define_include_len, 1, close_define_include, NULL);
+                           define_include_len, 1, close_define_include,
+                           NULL, 0);
     } // if
 
     if (!okay)
@@ -837,7 +841,7 @@
     } // if
 
     MOJOSHADER_includeClose callback = ctx->close_callback;
-    if (!push_source(ctx, filename, newdata, newbytes, 1, callback, NULL))
+    if (!push_source(ctx, filename, newdata, newbytes, 1, callback, NULL, 0))
     {
         assert(ctx->out_of_memory);
         ctx->close_callback(newdata, ctx->malloc, ctx->free, ctx->malloc_data);
@@ -1220,7 +1224,7 @@
     const char *fname = NULL;
     const char *val = NULL;
     size_t vallen = 0;
-
+    int is_macro = 0;
     IncludeState *state = ctx->include_stack;
     char *sym = (char *) alloca(state->tokenlen+1);
     memcpy(sym, state->token, state->tokenlen);
@@ -1244,6 +1248,8 @@
         if (def == NULL)
             return 0;   // just send the token through unchanged.
 
+        is_macro = 1;
+
         if (def->paramcount != 0)
         {
             const int expected = (def->paramcount < 0) ? 0 : def->paramcount;
@@ -1339,7 +1345,7 @@
     val = def->definition;
     vallen = strlen(val);
     fname = state->filename;
-    if (!push_source(ctx, fname, val, vallen, state->line, NULL, params))
+    if (!push_source(ctx,fname,val,vallen,state->line,NULL,params,is_macro))
     {
         assert(ctx->out_of_memory);
         goto handle_pp_identifier_failed;
@@ -1767,6 +1773,48 @@
 } // handle_pp_endif
 
 
+static void handle_pp_stringify(Context *ctx)
+{
+    IncludeState *state = ctx->include_stack;
+    const Define *def = NULL;
+
+    assert(state->is_macro);
+
+    if (lexer(state) == TOKEN_IDENTIFIER)
+    {
+        char *sym = (char *) alloca(state->tokenlen+1);
+        memcpy(sym, state->token, state->tokenlen);
+        sym[state->tokenlen] = '\0';
+
+        for (def = state->defines; def != NULL; def = def->next)
+        {
+            assert(def->paramcount == 0);  // args can't have args!
+            if (strcmp(def->identifier, sym) == 0)
+                break;
+        } // while
+    } // if
+
+    if (def == NULL)
+    {
+        pushback(state);
+        fail(ctx, "'#' is not followed by a macro parameter");
+        return;  // we just fail(), drop the '#' token and continue on.
+    } // if
+
+    const size_t deflen = strlen(def->definition);
+    char *litstring = (char *) Malloc(ctx, deflen + 2);
+    if (!litstring)
+        return;
+
+    litstring[0] = '\"';
+    memcpy(litstring + 1, def->definition, deflen);
+    litstring[deflen + 1] = '\"';
+    const char *filename = state->filename;
+    push_source(ctx, filename, litstring, deflen + 2, state->line,
+                close_define_include, NULL, 0);
+} // handle_pp_stringify
+
+
 static void unterminated_pp_condition(Context *ctx)
 {
     IncludeState *state = ctx->include_stack;
@@ -1816,7 +1864,7 @@
         const Conditional *cond = state->conditional_stack;
         const int skipping = ((cond != NULL) && (cond->skipping));
 
-        Token token = lexer(state);
+        const Token token = lexer(state);
 
         if (token != TOKEN_IDENTIFIER)
             ctx->recursion_count = 0;
@@ -1910,6 +1958,13 @@
             continue;  // will return at top of loop.
         } // else if
 
+        // stringify operator ("#") ... only during macro replacement.
+        else if ((token == TOKEN_HASH) && (state->is_macro))
+        {
+            handle_pp_stringify(ctx);
+            continue;
+        } // else if
+
         else if (token == TOKEN_IDENTIFIER)
         {
             if (handle_pp_identifier(ctx))
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/unit_tests/preprocessor/output/stringify-operator-basic	Wed Feb 24 21:20:37 2010 -0500
@@ -0,0 +1,3 @@
+// will output "RIGHT"
+#define x(z) #z
+x(RIGHT)
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/unit_tests/preprocessor/output/stringify-operator-basic.correct	Wed Feb 24 21:20:37 2010 -0500
@@ -0,0 +1,1 @@
+"RIGHT"
\ No newline at end of file