Implement expansion of object-like macros.
authorCarl Worth <cworth@cworth.org>
Tue, 25 May 2010 22:24:59 +0000 (15:24 -0700)
committerCarl Worth <cworth@cworth.org>
Tue, 25 May 2010 22:24:59 +0000 (15:24 -0700)
For this we add an "active" string_list_t to the parser. This makes
the current expansion_list_t in the parser obsolete, but we don't
remove that yet.

With this change we can now start passing some actual tests, so we
turn on real testing in the test suite again. I expect to implement
things more or less in the same order as before, so the test suite now
halts on first error.

With this change the first 8 tests in the suite pass, (object-like
macros with chaining and recursion).

glcpp-parse.y
glcpp.h
tests/glcpp-test

index 957421b864e0002e815d97f66ac4b388d4044d32..b3ef177a6dac3153dcd7267be6a101c8a8b51204 100644 (file)
@@ -59,6 +59,12 @@ _string_list_append_item (string_list_t *list, const char *str);
 void
 _string_list_append_list (string_list_t *list, string_list_t *tail);
 
+void
+_string_list_push (string_list_t *list, const char *str);
+
+void
+_string_list_pop (string_list_t *list);
+
 int
 _string_list_contains (string_list_t *list, const char *member, int *index);
 
@@ -98,7 +104,8 @@ void
 _token_list_append_list (token_list_t *list, token_list_t *tail);
 
 void
-_token_list_print (token_list_t *list);
+_glcpp_parser_print_expanded_token_list (glcpp_parser_t *parser,
+                                        token_list_t *list);
 
 static void
 glcpp_parser_pop_expansion (glcpp_parser_t *parser);
@@ -144,21 +151,24 @@ glcpp_parser_lex (glcpp_parser_t *parser);
 
 input:
        /* empty */
-|      input line
+|      input line {
+               printf ("\n");
+       }
 ;
 
 line:
        control_line
 |      text_line {
-               _token_list_print ($1);
-               printf ("\n");
+               _glcpp_parser_print_expanded_token_list (parser, $1);
                talloc_free ($1);
        }
 |      HASH non_directive
 ;
 
 control_line:
-       HASH_DEFINE_OBJ IDENTIFIER replacement_list NEWLINE
+       HASH_DEFINE_OBJ IDENTIFIER replacement_list NEWLINE {
+               _define_object_macro (parser, $2, $3);
+       }
 |      HASH_DEFINE_FUNC IDENTIFIER '(' ')' replacement_list NEWLINE
 |      HASH_DEFINE_FUNC IDENTIFIER '(' identifier_list ')' replacement_list NEWLINE
 |      HASH_UNDEF IDENTIFIER NEWLINE
@@ -287,6 +297,42 @@ _string_list_append_item (string_list_t *list, const char *str)
        list->tail = node;
 }
 
+void
+_string_list_push (string_list_t *list, const char *str)
+{
+       string_node_t *node;
+
+       node = xtalloc (list, string_node_t);
+       node->str = xtalloc_strdup (node, str);
+       node->next = list->head;
+
+       if (list->tail == NULL) {
+               list->tail = node;
+       }
+       list->head = node;
+}
+
+void
+_string_list_pop (string_list_t *list)
+{
+       string_node_t *node;
+
+       node = list->head;
+
+       if (node == NULL) {
+               fprintf (stderr, "Internal error: _string_list_pop called on an empty list.\n");
+               exit (1);
+       }
+
+       list->head = node->next;
+       if (list->tail == node) {
+               assert (node->next == NULL);
+               list->tail = NULL;
+       }
+
+       talloc_free (node);
+}
+
 int
 _string_list_contains (string_list_t *list, const char *member, int *index)
 {
@@ -507,19 +553,6 @@ _token_list_append_list (token_list_t *list, token_list_t *tail)
        list->tail = tail->tail;
 }
 
-void
-_token_list_print (token_list_t *list)
-{
-       token_node_t *node;
-
-       if (list == NULL)
-               return;
-
-       for (node = list->head; node; node = node->next) {
-               _token_print (node->token);
-       }
-}
-
 void
 yyerror (void *scanner, const char *error)
 {
@@ -536,6 +569,7 @@ glcpp_parser_create (void)
        glcpp_lex_init_extra (parser, &parser->scanner);
        parser->defines = hash_table_ctor (32, hash_table_string_hash,
                                           hash_table_string_compare);
+       parser->active = _string_list_create (parser);
        parser->expansions = NULL;
 
        parser->just_printed_separator = 1;
@@ -605,6 +639,64 @@ glcpp_parser_classify_token (glcpp_parser_t *parser,
                return TOKEN_CLASS_OBJ_MACRO;
 }
 
+void
+_glcpp_parser_print_expanded_token (glcpp_parser_t *parser,
+                                   token_t *token)
+{
+       const char *identifier;
+       macro_t *macro;
+
+       /* We only expand identifiers */
+       if (token->type != IDENTIFIER) {
+               _token_print (token);
+               return;
+       }
+
+       /* Look up this identifier in the hash table. */
+       identifier = token->value.str;
+       macro = hash_table_find (parser->defines, identifier);
+
+       /* Not a macro, so just print directly. */
+       if (macro == NULL) {
+               printf ("%s", identifier);
+               return;
+       }
+
+       /* We're not (yet) supporting function-like macros. */
+       if (macro->is_function) {
+               printf ("%s", identifier);
+               return;
+       }
+
+       /* Finally, don't expand this macro if we're already actively
+        * expanding it, (to avoid infinite recursion). */
+       if (_string_list_contains (parser->active, identifier, NULL)) {
+               printf ("%s", identifier);
+               return;
+       }
+
+       _string_list_push (parser->active, identifier);
+       _glcpp_parser_print_expanded_token_list (parser,
+                                                macro->replacements);
+       _string_list_pop (parser->active);
+}
+
+void
+_glcpp_parser_print_expanded_token_list (glcpp_parser_t *parser,
+                                        token_list_t *list)
+{
+       token_node_t *node;
+
+       if (list == NULL)
+               return;
+
+       for (node = list->head; node; node = node->next) {
+               _glcpp_parser_print_expanded_token (parser, node->token);
+               if (node->next)
+                       printf (" ");
+       }
+}
+
 void
 _define_object_macro (glcpp_parser_t *parser,
                      const char *identifier,
diff --git a/glcpp.h b/glcpp.h
index 261254a17c420d440eb21e850e108276652bba54..bd599d73011bba83742f664368b621cdf72d878e 100644 (file)
--- a/glcpp.h
+++ b/glcpp.h
@@ -124,6 +124,7 @@ typedef struct skip_node {
 struct glcpp_parser {
        yyscan_t scanner;
        struct hash_table *defines;
+       string_list_t *active;
        expansion_node_t *expansions;
        int just_printed_separator;
        int need_newline;
index 8074e471197736e8632aa3461eee0831a2090443..630415521047841dcab22c8c1e04c69b85c741e5 100755 (executable)
@@ -1,13 +1,10 @@
 #!/bin/sh
 set -e
 
-echo "Caution: These results are just verifying parse-ability, not correctness!"
-
 for test in *.c; do
     echo "Testing $test"
     ../glcpp < $test > $test.out
     gcc -E $test -o $test.gcc
-#    grep -v '^#' < $test.gcc > $test.expected
-    grep -v '^[        ]*#' < $test > $test.expected
+    grep -v '^#' < $test.gcc > $test.expected
     diff -u $test.expected $test.out
 done