summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarl Worth <cworth@cworth.org>2010-05-25 16:28:26 -0700
committerCarl Worth <cworth@cworth.org>2010-05-25 16:28:26 -0700
commitb1854fdfb6b567fa61d544d8080e2acb4cc78dc1 (patch)
tree5bb3d217c3d94234f7b15f571b72ad59887763a6
parente6fb7827c96451d4a09dfda31979a6b9cb27301e (diff)
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.
-rw-r--r--glcpp-lex.l14
-rw-r--r--glcpp-parse.y174
-rw-r--r--glcpp.h1
3 files changed, 168 insertions, 21 deletions
diff --git a/glcpp-lex.l b/glcpp-lex.l
index 7b5cdd57a0..b1980742d3 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 830a6232d8..60b414e43a 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 <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 bd599d7301..043098b134 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;