Index: gettext-tools/src/x-perl.c =================================================================== RCS file: /sources/gettext/gettext/gettext-tools/src/x-perl.c,v retrieving revision 1.37 retrieving revision 1.42 diff -c -3 -r1.37 -r1.42 *** gettext-tools/src/x-perl.c 8 May 2006 13:39:43 -0000 1.37 --- gettext-tools/src/x-perl.c 2 Oct 2006 12:18:15 -0000 1.42 *************** *** 759,766 **** int lineno); static token_ty *x_perl_lex (message_list_ty *mlp); static void x_perl_unlex (token_ty *tp); ! static bool extract_balanced (message_list_ty *mlp, int state, ! token_type_ty delim, flag_context_ty outer_context, flag_context_list_iterator_ty context_iter, int arg, struct arglist_parser *argparser); --- 759,767 ---- int lineno); static token_ty *x_perl_lex (message_list_ty *mlp); static void x_perl_unlex (token_ty *tp); ! static bool extract_balanced (message_list_ty *mlp, ! token_type_ty delim, bool eat_delim, ! bool comma_delim, flag_context_ty outer_context, flag_context_list_iterator_ty context_iter, int arg, struct arglist_parser *argparser); *************** *** 1369,1375 **** real_file_name, line_number); #endif ! if (extract_balanced (mlp, 0, token_type_rbrace, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL))) return; --- 1370,1376 ---- real_file_name, line_number); #endif ! if (extract_balanced (mlp, token_type_rbrace, true, false, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL))) return; *************** *** 1560,1566 **** else { x_perl_unlex (t1); ! if (extract_balanced (mlp, 1, token_type_rbrace, null_context, context_iter, 1, arglist_parser_alloc (mlp, &shapes))) return; --- 1561,1567 ---- else { x_perl_unlex (t1); ! if (extract_balanced (mlp, token_type_rbrace, true, false, null_context, context_iter, 1, arglist_parser_alloc (mlp, &shapes))) return; *************** *** 1591,1597 **** fprintf (stderr, "%s:%d: extracting balanced '{' after varname\n", real_file_name, line_number); #endif ! extract_balanced (mlp, 0, token_type_rbrace, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL)); break; --- 1592,1598 ---- fprintf (stderr, "%s:%d: extracting balanced '{' after varname\n", real_file_name, line_number); #endif ! extract_balanced (mlp, token_type_rbrace, true, false, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL)); break; *************** *** 1601,1607 **** fprintf (stderr, "%s:%d: extracting balanced '[' after varname\n", real_file_name, line_number); #endif ! extract_balanced (mlp, 0, token_type_rbracket, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL)); break; --- 1602,1608 ---- fprintf (stderr, "%s:%d: extracting balanced '[' after varname\n", real_file_name, line_number); #endif ! extract_balanced (mlp, token_type_rbracket, true, false, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL)); break; *************** *** 2782,2788 **** /* The file is broken into tokens. Scan the token stream, looking for a keyword, followed by a left paren, followed by a string. When we see this sequence, we have something to remember. We assume we are ! looking at a valid C or C++ program, and leave the complaints about the grammar to the compiler. Normal handling: Look for --- 2783,2789 ---- /* The file is broken into tokens. Scan the token stream, looking for a keyword, followed by a left paren, followed by a string. When we see this sequence, we have something to remember. We assume we are ! looking at a valid Perl program, and leave the complaints about the grammar to the compiler. Normal handling: Look for *************** *** 2791,2837 **** keyword ( ... msgid ... msgid_plural ... ) We use recursion because the arguments before msgid or between msgid ! and msgid_plural can contain subexpressions of the same form. */ /* Extract messages until the next balanced closing parenthesis. Extracted messages are added to MLP. ARG is the current argument list position, starts with 1. ARGPARSER is the corresponding argument list parser. ! Returns true for EOF, false otherwise. ! ! States are: ! ! 0 - initial state ! 1 - keyword has been seen ! 2 - extractable string has been seen ! 3 - a dot operator after an extractable string has been seen ! ! States 2 and 3 are "fragile", the parser will remain in state 2 ! as long as only opening parentheses are seen, a transition to ! state 3 is done on appearance of a dot operator, all other tokens ! will cause the parser to fall back to state 1 or 0, eventually ! with an error message about invalid intermixing of constant and ! non-constant strings. ! ! Likewise, state 3 is fragile. The parser will remain in state 3 ! as long as only closing parentheses are seen, a transition to state ! 2 is done on appearance of another (literal!) string, all other ! tokens will cause a warning. */ static bool ! extract_balanced (message_list_ty *mlp, int state, token_type_ty delim, flag_context_ty outer_context, flag_context_list_iterator_ty context_iter, int arg, struct arglist_parser *argparser) { - /* Number of left parentheses seen. */ - int paren_seen = 0; - /* Whether to implicitly assume the next tokens are arguments even without a '('. */ bool next_is_argument = false; /* Context iterator that will be used if the next token is a '('. */ flag_context_list_iterator_ty next_context_iter = --- 2792,2877 ---- keyword ( ... msgid ... msgid_plural ... ) We use recursion because the arguments before msgid or between msgid ! and msgid_plural can contain subexpressions of the same form. ! ! In Perl, parentheses around function arguments can be omitted. ! ! The general rules are: ! 1) Functions declared with a prototype take exactly the specified number ! of arguments. ! sub one_arg ($) { ... } ! sub two_args ($$) { ... } ! 2) When a function name is immediately followed by an opening parenthesis, ! the argument list ends at the corresponding closing parenthesis. ! ! If rule 1 and rule 2 are contradictory, i.e. when the program calls a ! function with an explicit argument list and the wrong number of arguments, ! the program is invalid: ! sub two_args ($$) { ... } ! foo two_args (x), y - invalid due to rules 1 and 2 ! ! Ambiguities are resolved as follows: ! 3) Some built-ins, such as 'abs', 'sqrt', 'sin', 'cos', ..., and functions ! declared with a prototype of exactly one argument take exactly one ! argument: ! foo sin x, y ==> foo (sin (x), y) ! sub one_arg ($) { ... } ! foo one_arg x, y, z ==> foo (one_arg (x), y, z) ! 4) Other identifiers, if not immediately followed by an opening ! parenthesis, consume the entire remaining argument list: ! foo bar x, y ==> foo (bar (x, y)) ! sub two_args ($$) { ... } ! foo two_args x, y ==> foo (two_args (x, y)) ! ! Other series of comma separated expressions without a function name at ! the beginning are comma expressions: ! sub two_args ($$) { ... } ! foo two_args x, (y, z) ==> foo (two_args (x, (y, z))) ! Note that the evaluation of comma expressions returns a list of values ! when in list context (e.g. inside the argument list of a function without ! prototype) but only one value when inside the argument list of a function ! with a prototype: ! sub print3 ($$$) { print @_ } ! print3 5, (6, 7), 8 ==> 578 ! print 5, (6, 7), 8 ==> 5678 ! ! Where rule 3 or 4 contradict rule 1 or 2, the program is invalid: ! sin (x, y) - invalid due to rules 2 and 3 ! sub one_arg ($) { ... } ! one_arg (x, y) - invalid due to rules 2 and 3 ! sub two_args ($$) { ... } ! foo two_args x, y, z - invalid due to rules 1 and 4 ! */ /* Extract messages until the next balanced closing parenthesis. Extracted messages are added to MLP. + DELIM can be either token_type_rbrace, token_type_rbracket, + token_type_rparen. Additionally, if COMMA_DELIM is true, parsing + stops at the next comma outside parentheses. + ARG is the current argument list position, starts with 1. ARGPARSER is the corresponding argument list parser. ! Returns true for EOF, false otherwise. */ static bool ! extract_balanced (message_list_ty *mlp, ! token_type_ty delim, bool eat_delim, bool comma_delim, flag_context_ty outer_context, flag_context_list_iterator_ty context_iter, int arg, struct arglist_parser *argparser) { /* Whether to implicitly assume the next tokens are arguments even without a '('. */ bool next_is_argument = false; + /* Parameters of the keyword just seen. Defined only when next_is_argument + is true. */ + const struct callshapes *next_shapes = NULL; + struct arglist_parser *next_argparser = NULL; + + /* Whether to not consider strings until the next comma. */ + bool skip_until_comma = false; /* Context iterator that will be used if the next token is a '('. */ flag_context_list_iterator_ty next_context_iter = *************** *** 2852,2858 **** for (;;) { - int my_last_token = last_token; /* The current token. */ token_ty *tp; --- 2892,2897 ---- *************** *** 2865,2887 **** xgettext_current_source_encoding = po_charset_utf8; arglist_parser_done (argparser, arg); xgettext_current_source_encoding = xgettext_global_source_encoding; #if DEBUG_PERL fprintf (stderr, "%s:%d: extract_balanced finished (%d)\n", logical_file_name, tp->line_number, --nesting_level); #endif ! free_token (tp); return false; } if (next_is_argument && tp->type != token_type_lparen) { /* An argument list starts, even though there is no '('. */ ! context_iter = next_context_iter; ! outer_context = inner_context; ! inner_context = ! inherited_context (outer_context, ! flag_context_list_iterator_advance ( ! &context_iter)); } switch (tp->type) --- 2904,2987 ---- xgettext_current_source_encoding = po_charset_utf8; arglist_parser_done (argparser, arg); xgettext_current_source_encoding = xgettext_global_source_encoding; + if (next_argparser != NULL) + free (next_argparser); #if DEBUG_PERL fprintf (stderr, "%s:%d: extract_balanced finished (%d)\n", logical_file_name, tp->line_number, --nesting_level); #endif ! if (eat_delim) ! free_token (tp); ! else ! /* Preserve the delimiter for the caller. */ ! x_perl_unlex (tp); return false; } + if (comma_delim && tp->type == token_type_comma) + { + xgettext_current_source_encoding = po_charset_utf8; + arglist_parser_done (argparser, arg); + xgettext_current_source_encoding = xgettext_global_source_encoding; + if (next_argparser != NULL) + free (next_argparser); + #if DEBUG_PERL + fprintf (stderr, "%s:%d: extract_balanced finished at comma (%d)\n", + logical_file_name, tp->line_number, --nesting_level); + #endif + x_perl_unlex (tp); + return false; + } + if (next_is_argument && tp->type != token_type_lparen) { /* An argument list starts, even though there is no '('. */ ! bool next_comma_delim; ! ! x_perl_unlex (tp); ! ! if (next_shapes != NULL) ! /* We know something about the function being called. Assume ! that it consumes only one argument if no argument number or ! total > 1 is specified. */ ! { ! size_t i; ! ! next_comma_delim = true; ! for (i = 0; i < next_shapes->nshapes; i++) ! { ! const struct callshape *shape = &next_shapes->shapes[i]; ! ! if (shape->argnum1 > 1 ! || shape->argnum2 > 1 ! || shape->argnumc > 1 ! || shape->argtotal > 1) ! next_comma_delim = false; ! } ! } ! else ! /* We know nothing about the function being called. It could be ! a function prototyped to take only one argument, or on the other ! hand it could be prototyped to take more than one argument or an ! arbitrary argument list or it could be unprototyped. Due to ! the way the parser works, assuming the first case gives the ! best results. */ ! next_comma_delim = true; ! ! if (extract_balanced (mlp, delim, false, next_comma_delim, ! inner_context, next_context_iter, ! 1, next_argparser)) ! { ! xgettext_current_source_encoding = po_charset_utf8; ! arglist_parser_done (argparser, arg); ! xgettext_current_source_encoding = xgettext_global_source_encoding; ! return true; ! } ! ! next_is_argument = false; ! next_argparser = NULL; ! next_context_iter = null_context_list_iterator; ! continue; } switch (tp->type) *************** *** 2902,2916 **** const struct callshapes *shapes = (const struct callshapes *) keyword_value; - xgettext_current_source_encoding = po_charset_utf8; - arglist_parser_done (argparser, arg); - xgettext_current_source_encoding = xgettext_global_source_encoding; - argparser = arglist_parser_alloc (mlp, shapes); - arg = 1; - last_token = token_type_keyword_symbol; ! ! state = 2; } } next_is_argument = true; --- 3002,3015 ---- const struct callshapes *shapes = (const struct callshapes *) keyword_value; last_token = token_type_keyword_symbol; ! next_shapes = shapes; ! next_argparser = arglist_parser_alloc (mlp, shapes); ! } ! else ! { ! next_shapes = NULL; ! next_argparser = arglist_parser_alloc (mlp, NULL); } } next_is_argument = true; *************** *** 2928,2971 **** #endif prefer_division_over_regexp = true; next_is_argument = false; next_context_iter = null_context_list_iterator; break; case token_type_lparen: #if DEBUG_PERL ! fprintf (stderr, "%s:%d: type left parentheses (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif ! ++paren_seen; ! ! if (extract_balanced (mlp, state, token_type_rparen, ! inner_context, next_context_iter, ! arg, arglist_parser_clone (argparser))) { ! xgettext_current_source_encoding = po_charset_utf8; ! arglist_parser_done (argparser, arg); ! xgettext_current_source_encoding = xgettext_global_source_encoding; ! free_token (tp); ! return true; } ! if (my_last_token == token_type_keyword_symbol) { ! xgettext_current_source_encoding = po_charset_utf8; ! arglist_parser_done (argparser, arg); ! xgettext_current_source_encoding = xgettext_global_source_encoding; ! argparser = arglist_parser_alloc (mlp, NULL); } ! next_is_argument = false; next_context_iter = null_context_list_iterator; break; case token_type_rparen: #if DEBUG_PERL ! fprintf (stderr, "%s:%d: type right parentheses(%d)\n", logical_file_name, tp->line_number, nesting_level); #endif - --paren_seen; next_is_argument = false; next_context_iter = null_context_list_iterator; break; --- 3027,3092 ---- #endif prefer_division_over_regexp = true; next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = null_context_list_iterator; break; case token_type_lparen: #if DEBUG_PERL ! fprintf (stderr, "%s:%d: type left parenthesis (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif ! if (next_is_argument) { ! /* Parse the argument list of a function call. */ ! if (extract_balanced (mlp, token_type_rparen, true, false, ! inner_context, next_context_iter, ! 1, next_argparser)) ! { ! xgettext_current_source_encoding = po_charset_utf8; ! arglist_parser_done (argparser, arg); ! xgettext_current_source_encoding = xgettext_global_source_encoding; ! return true; ! } ! next_is_argument = false; ! next_argparser = NULL; } ! else { ! /* Parse a parenthesized expression or comma expression. */ ! if (extract_balanced (mlp, token_type_rparen, true, false, ! inner_context, next_context_iter, ! arg, arglist_parser_clone (argparser))) ! { ! xgettext_current_source_encoding = po_charset_utf8; ! arglist_parser_done (argparser, arg); ! xgettext_current_source_encoding = xgettext_global_source_encoding; ! if (next_argparser != NULL) ! free (next_argparser); ! free_token (tp); ! return true; ! } ! next_is_argument = false; ! if (next_argparser != NULL) ! free (next_argparser); ! next_argparser = NULL; } ! skip_until_comma = true; next_context_iter = null_context_list_iterator; break; case token_type_rparen: #if DEBUG_PERL ! fprintf (stderr, "%s:%d: type right parenthesis (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; + skip_until_comma = true; next_context_iter = null_context_list_iterator; break; *************** *** 2994,2999 **** --- 3115,3124 ---- flag_context_list_iterator_advance ( &context_iter)); next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; + skip_until_comma = false; next_context_iter = passthrough_context_list_iterator; break; *************** *** 3015,3030 **** remember_a_message (mlp, NULL, string, inner_context, &pos, savable_comment); xgettext_current_source_encoding = xgettext_global_source_encoding; } ! else if (state) { ! char *string = collect_message (mlp, tp, EXIT_FAILURE); ! xgettext_current_source_encoding = po_charset_utf8; ! arglist_parser_remember (argparser, arg, ! string, inner_context, ! logical_file_name, tp->line_number, ! savable_comment); ! xgettext_current_source_encoding = xgettext_global_source_encoding; } if (arglist_parser_decidedp (argparser, arg)) --- 3140,3175 ---- remember_a_message (mlp, NULL, string, inner_context, &pos, savable_comment); xgettext_current_source_encoding = xgettext_global_source_encoding; } ! else if (!skip_until_comma) { ! /* Need to collect the complete string, with error checking, ! only if the argument ARG is used in ARGPARSER. */ ! bool must_collect = false; ! { ! size_t nalternatives = argparser->nalternatives; ! size_t i; ! for (i = 0; i < nalternatives; i++) ! { ! struct partial_call *cp = &argparser->alternative[i]; ! ! if (arg == cp->argnumc ! || arg == cp->argnum1 || arg == cp->argnum2) ! must_collect = true; ! } ! } ! ! if (must_collect) ! { ! char *string = collect_message (mlp, tp, EXIT_FAILURE); ! ! xgettext_current_source_encoding = po_charset_utf8; ! arglist_parser_remember (argparser, arg, ! string, inner_context, ! logical_file_name, tp->line_number, ! savable_comment); ! xgettext_current_source_encoding = xgettext_global_source_encoding; ! } } if (arglist_parser_decidedp (argparser, arg)) *************** *** 3033,3042 **** arglist_parser_done (argparser, arg); xgettext_current_source_encoding = xgettext_global_source_encoding; argparser = arglist_parser_alloc (mlp, NULL); - state = 0; } next_is_argument = false; next_context_iter = null_context_list_iterator; break; --- 3178,3189 ---- arglist_parser_done (argparser, arg); xgettext_current_source_encoding = xgettext_global_source_encoding; argparser = arglist_parser_alloc (mlp, NULL); } next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = null_context_list_iterator; break; *************** *** 3048,3053 **** --- 3195,3203 ---- xgettext_current_source_encoding = po_charset_utf8; arglist_parser_done (argparser, arg); xgettext_current_source_encoding = xgettext_global_source_encoding; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; free_token (tp); return true; *************** *** 3056,3072 **** fprintf (stderr, "%s:%d: type lbrace (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif ! if (extract_balanced (mlp, 0, token_type_rbrace, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL))) { xgettext_current_source_encoding = po_charset_utf8; arglist_parser_done (argparser, arg); xgettext_current_source_encoding = xgettext_global_source_encoding; free_token (tp); return true; } next_is_argument = false; next_context_iter = null_context_list_iterator; break; --- 3206,3227 ---- fprintf (stderr, "%s:%d: type lbrace (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif ! if (extract_balanced (mlp, token_type_rbrace, true, false, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL))) { xgettext_current_source_encoding = po_charset_utf8; arglist_parser_done (argparser, arg); xgettext_current_source_encoding = xgettext_global_source_encoding; + if (next_argparser != NULL) + free (next_argparser); free_token (tp); return true; } next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = null_context_list_iterator; break; *************** *** 3076,3083 **** logical_file_name, tp->line_number, nesting_level); #endif next_is_argument = false; next_context_iter = null_context_list_iterator; - state = 0; break; case token_type_lbracket: --- 3231,3240 ---- logical_file_name, tp->line_number, nesting_level); #endif next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = null_context_list_iterator; break; case token_type_lbracket: *************** *** 3085,3101 **** fprintf (stderr, "%s:%d: type lbracket (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif ! if (extract_balanced (mlp, 0, token_type_rbracket, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL))) { xgettext_current_source_encoding = po_charset_utf8; arglist_parser_done (argparser, arg); xgettext_current_source_encoding = xgettext_global_source_encoding; free_token (tp); return true; } next_is_argument = false; next_context_iter = null_context_list_iterator; break; --- 3242,3263 ---- fprintf (stderr, "%s:%d: type lbracket (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif ! if (extract_balanced (mlp, token_type_rbracket, true, false, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL))) { xgettext_current_source_encoding = po_charset_utf8; arglist_parser_done (argparser, arg); xgettext_current_source_encoding = xgettext_global_source_encoding; + if (next_argparser != NULL) + free (next_argparser); free_token (tp); return true; } next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = null_context_list_iterator; break; *************** *** 3105,3112 **** logical_file_name, tp->line_number, nesting_level); #endif next_is_argument = false; next_context_iter = null_context_list_iterator; - state = 0; break; case token_type_semicolon: --- 3267,3276 ---- logical_file_name, tp->line_number, nesting_level); #endif next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = null_context_list_iterator; break; case token_type_semicolon: *************** *** 3114,3120 **** fprintf (stderr, "%s:%d: type semicolon (%d)\n", logical_file_name, tp->line_number, nesting_level); #endif - state = 0; /* The ultimate sign. */ xgettext_current_source_encoding = po_charset_utf8; --- 3278,3283 ---- *************** *** 3128,3133 **** --- 3291,3299 ---- outer_context = null_context; context_iter = null_context_list_iterator; next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = passthrough_context_list_iterator; inner_context = inherited_context (outer_context, *************** *** 3141,3146 **** --- 3307,3315 ---- logical_file_name, tp->line_number, nesting_level); #endif next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = null_context_list_iterator; break; *************** *** 3150,3157 **** logical_file_name, tp->line_number, nesting_level); #endif next_is_argument = false; next_context_iter = null_context_list_iterator; - state = 0; break; case token_type_named_op: --- 3319,3328 ---- logical_file_name, tp->line_number, nesting_level); #endif next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = null_context_list_iterator; break; case token_type_named_op: *************** *** 3161,3168 **** tp->string); #endif next_is_argument = false; next_context_iter = null_context_list_iterator; - state = 0; break; case token_type_regex_op: --- 3332,3341 ---- tp->string); #endif next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = null_context_list_iterator; break; case token_type_regex_op: *************** *** 3171,3176 **** --- 3344,3352 ---- logical_file_name, tp->line_number, nesting_level); #endif next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = null_context_list_iterator; break; *************** *** 3180,3187 **** logical_file_name, tp->line_number, nesting_level); #endif next_is_argument = false; next_context_iter = null_context_list_iterator; - state = 0; break; default: --- 3356,3365 ---- logical_file_name, tp->line_number, nesting_level); #endif next_is_argument = false; + if (next_argparser != NULL) + free (next_argparser); + next_argparser = NULL; next_context_iter = null_context_list_iterator; break; default: *************** *** 3223,3229 **** /* Eat tokens until eof is seen. When extract_balanced returns due to an unbalanced closing brace, just restart it. */ ! while (!extract_balanced (mlp, 0, token_type_rbrace, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL))) ; --- 3401,3407 ---- /* Eat tokens until eof is seen. When extract_balanced returns due to an unbalanced closing brace, just restart it. */ ! while (!extract_balanced (mlp, token_type_rbrace, true, false, null_context, null_context_list_iterator, 1, arglist_parser_alloc (mlp, NULL))) ;