From b1854fdfb6b567fa61d544d8080e2acb4cc78dc1 Mon Sep 17 00:00:00 2001 From: Carl Worth Date: Tue, 25 May 2010 16:28:26 -0700 Subject: [PATCH] Implement simplified substitution for function-like macro invocation. This supports function-like macro invocation but without any argument substitution. This now makes test 11 through 14 pass. --- glcpp-lex.l | 14 +++- glcpp-parse.y | 174 ++++++++++++++++++++++++++++++++++++++++++++------ glcpp.h | 1 + 3 files changed, 168 insertions(+), 21 deletions(-) diff --git a/glcpp-lex.l b/glcpp-lex.l index 7b5cdd57a0f..b1980742d39 100644 --- a/glcpp-lex.l +++ b/glcpp-lex.l @@ -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 { diff --git a/glcpp-parse.y b/glcpp-parse.y index 830a6232d80..60b414e43a7 100644 --- a/glcpp-parse.y +++ b/glcpp-parse.y @@ -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 punctuator -%type IDENTIFIER OTHER +%type IDENTIFIER OTHER SPACE +%type identifier_list %type preprocessing_token %type 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 bd599d73011..043098b1347 100644 --- 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; -- 2.30.2