Implement simplified substitution for function-like macro invocation.
authorCarl Worth <cworth@cworth.org>
Tue, 25 May 2010 23:28:26 +0000 (16:28 -0700)
committerCarl Worth <cworth@cworth.org>
Tue, 25 May 2010 23:28:26 +0000 (16:28 -0700)
This supports function-like macro invocation but without any argument
substitution. This now makes test 11 through 14 pass.

glcpp-lex.l
glcpp-parse.y
glcpp.h

index 7b5cdd57a0fb0646f41fb1dd4b7ab062e52f7b86..b1980742d39ffd65794431b9b63898f1c1fb7d61 100644 (file)
@@ -144,6 +144,18 @@ HEXADECIMAL_INTEGER        0[xX][0-9a-fA-F]+[uU]?
        return IDENTIFIER;
 }
 
+"(" {
+       return '(';
+}
+
+")" {
+       return ')';
+}
+
+"," {
+       return ',';
+}
+
 {OTHER}+ {
        yylval.str = xtalloc_strdup (yyextra, yytext);
        return OTHER;
@@ -151,7 +163,7 @@ HEXADECIMAL_INTEGER 0[xX][0-9a-fA-F]+[uU]?
 
 {HSPACE}+ {
        yylval.str = xtalloc_strdup (yyextra, yytext);
-       return OTHER;
+       return SPACE;
 }
 
 \n {
index 830a6232d80294295f9a6a5a495a545ef74d2e51..60b414e43a7849a2867b214d1c08b95a2651eba6 100644 (file)
@@ -127,20 +127,14 @@ glcpp_parser_lex (glcpp_parser_t *parser);
 
 %}
 
-%union {
-       int ival;
-       char *str;
-       token_t *token;
-       token_list_t *token_list;
-}
-
 %parse-param {glcpp_parser_t *parser}
 %lex-param {glcpp_parser_t *parser}
 
-%token HASH_DEFINE_FUNC HASH_DEFINE_OBJ HASH IDENTIFIER NEWLINE OTHER HASH_UNDEF
+%token HASH HASH_DEFINE_FUNC HASH_DEFINE_OBJ HASH_UNDEF IDENTIFIER NEWLINE OTHER SPACE
 %token LEFT_SHIFT RIGHT_SHIFT LESS_OR_EQUAL GREATER_OR_EQUAL EQUAL NOT_EQUAL AND OR PASTE
 %type <ival> punctuator
-%type <str> IDENTIFIER OTHER
+%type <str> IDENTIFIER OTHER SPACE
+%type <string_list> identifier_list
 %type <token> preprocessing_token
 %type <token_list> pp_tokens replacement_list text_line
 
@@ -169,8 +163,12 @@ control_line:
        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_DEFINE_FUNC IDENTIFIER '(' ')' replacement_list NEWLINE {
+               _define_function_macro (parser, $2, NULL, $5);
+       }
+|      HASH_DEFINE_FUNC IDENTIFIER '(' identifier_list ')' replacement_list NEWLINE {
+               _define_function_macro (parser, $2, $4, $6);
+       }
 |      HASH_UNDEF IDENTIFIER NEWLINE {
                string_list_t *macro = hash_table_find (parser->defines, $2);
                if (macro) {
@@ -186,8 +184,16 @@ control_line:
 ;
 
 identifier_list:
-       IDENTIFIER
-|      identifier_list ',' IDENTIFIER
+       IDENTIFIER {
+               $$ = _string_list_create (parser);
+               _string_list_append_item ($$, $1);
+               talloc_steal ($$, $1);
+       }
+|      identifier_list ',' IDENTIFIER {
+               $$ = $1;        
+               _string_list_append_item ($$, $3);
+               talloc_steal ($$, $3);
+       }
 ;
 
 text_line:
@@ -227,6 +233,9 @@ preprocessing_token:
 |      OTHER {
                $$ = _token_create_str (parser, OTHER, $1);
        }
+|      SPACE {
+               $$ = _token_create_str (parser, OTHER, $1);
+       }
 ;
 
 punctuator:
@@ -649,7 +658,14 @@ glcpp_parser_classify_token (glcpp_parser_t *parser,
                return TOKEN_CLASS_OBJ_MACRO;
 }
 
-void
+/* Print a non-macro token, or the expansion of an object-like macro.
+ *
+ * Returns 0 if this token is completely printed.
+ *
+ * Returns 1 in the case that 'token' is a function-like macro that
+ * needs further expansion.
+ */
+static int
 _glcpp_parser_print_expanded_token (glcpp_parser_t *parser,
                                    token_t *token)
 {
@@ -659,7 +675,7 @@ _glcpp_parser_print_expanded_token (glcpp_parser_t *parser,
        /* We only expand identifiers */
        if (token->type != IDENTIFIER) {
                _token_print (token);
-               return;
+               return 0;
        }
 
        /* Look up this identifier in the hash table. */
@@ -669,20 +685,135 @@ _glcpp_parser_print_expanded_token (glcpp_parser_t *parser,
        /* Not a macro, so just print directly. */
        if (macro == NULL) {
                printf ("%s", identifier);
-               return;
+               return 0;
        }
 
-       /* We're not (yet) supporting function-like macros. */
+       /* For function-like macros return 1 for further processing. */
        if (macro->is_function) {
-               printf ("%s", identifier);
-               return;
+               return 1;
        }
 
        /* 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 0;
+       }
+
+       _string_list_push (parser->active, identifier);
+       _glcpp_parser_print_expanded_token_list (parser,
+                                                macro->replacements);
+       _string_list_pop (parser->active);
+
+       return 0;
+}
+
+typedef enum function_status
+{
+       FUNCTION_STATUS_SUCCESS,
+       FUNCTION_NOT_A_FUNCTION,
+       FUNCTION_UNBALANCED_PARENTHESES
+} function_status_t;
+
+/* Find a set of function-like macro arguments by looking for a
+ * balanced set of parentheses. Upon return *node will be the last
+ * consumed node, such that further processing can continue with
+ * node->next.
+ *
+ * Return values:
+ *
+ *   FUNCTION_STATUS_SUCCESS:
+ *
+ *     Successfully parsed a set of function arguments.        
+ *
+ *   FUNCTION_NOT_A_FUNCTION:
+ *
+ *     Macro name not followed by a '('. This is not an error, but
+ *     simply that the macro name should be treated as a non-macro.
+ *
+ *   FUNCTION_UNBLANCED_PARENTHESES
+ *
+ *     Macro name is not followed by a balanced set of parentheses.
+ */
+static function_status_t
+_find_arguments (token_node_t **node_ret, argument_list_t **arguments)
+{
+       token_node_t *node = *node_ret, *last;
+       int paren_count;
+       int arg_count;
+
+       last = node;
+       node = node->next;
+
+       /* Ignore whitespace before first parenthesis. */
+       while (node && node->token->type == SPACE)
+               node = node->next;
+
+       if (node == NULL || node->token->type != '(')
+               return FUNCTION_NOT_A_FUNCTION;
+
+       paren_count = 0;
+       arg_count = 0;
+       do {
+               if (node->token->type == '(')
+               {
+                       paren_count++;
+               }
+               else if (node->token->type == ')')
+               {
+                       paren_count--;
+               }
+               else if (node->token->type == ',' &&
+                        paren_count == 1)
+               {
+                       arg_count++;
+               }
+
+               last = node;
+               node = node->next;
+
+       } while (node && paren_count);
+
+       if (node && paren_count)
+               return FUNCTION_UNBALANCED_PARENTHESES;
+
+       *node_ret = last;
+
+       return FUNCTION_STATUS_SUCCESS;
+}
+
+/* Prints the expansion of *node (consuming further tokens from the
+ * list as necessary). Upon return *node will be the last consumed
+ * node, such that further processing can continue with node->next. */
+static void
+_glcpp_parser_print_expanded_function (glcpp_parser_t *parser,
+                                      token_node_t **node_ret)
+{
+       macro_t *macro;
+       token_node_t *node;
+       const char *identifier;
+       argument_list_t *arguments;
+       function_status_t status;
+
+       node = *node_ret;
+       identifier = node->token->value.str;
+
+       macro = hash_table_find (parser->defines, identifier);
+
+       assert (macro->is_function);
+
+       status = _find_arguments (node_ret, &arguments);
+
+       switch (status) {
+       case FUNCTION_STATUS_SUCCESS:
+               break;
+       case FUNCTION_NOT_A_FUNCTION:
                printf ("%s", identifier);
                return;
+       case FUNCTION_UNBALANCED_PARENTHESES:
+               fprintf (stderr, "Error: Macro %s call has unbalanced parentheses\n",
+                        identifier);
+               exit (1);
        }
 
        _string_list_push (parser->active, identifier);
@@ -696,12 +827,15 @@ _glcpp_parser_print_expanded_token_list (glcpp_parser_t *parser,
                                         token_list_t *list)
 {
        token_node_t *node;
+       function_status_t function_status;
 
        if (list == NULL)
                return;
 
        for (node = list->head; node; node = node->next) {
-               _glcpp_parser_print_expanded_token (parser, node->token);
+               if (_glcpp_parser_print_expanded_token (parser, node->token))
+                       _glcpp_parser_print_expanded_function (parser, &node);
+
                if (node->next)
                        printf (" ");
        }
diff --git a/glcpp.h b/glcpp.h
index bd599d73011bba83742f664368b621cdf72d878e..043098b13475f0a251ad0dce6114dd255238c111 100644 (file)
--- a/glcpp.h
+++ b/glcpp.h
@@ -51,6 +51,7 @@ typedef union YYSTYPE
 {
        int ival;
        char *str;
+       string_list_t *string_list;
        token_t *token;
        token_list_t *token_list;
 } YYSTYPE;