]> git.ipfire.org Git - thirdparty/gettext.git/commitdiff
xgettext: Perl: Avoid unbounded nesting_depth growth, part 2.
authorBruno Haible <bruno@clisp.org>
Mon, 18 Sep 2023 17:02:01 +0000 (19:02 +0200)
committerBruno Haible <bruno@clisp.org>
Tue, 19 Sep 2023 02:11:31 +0000 (04:11 +0200)
* gettext-tools/src/x-perl.c: Return from extract_balanced when a subroutine's
definition terminates.
(extract_balanced): Test whether the first token is 'sub'. If so, don't use
the usual symbol parsing and return when a token_type_lbrace was seen.

Test cases:
==========================================
sub import {
}
==========================================
sub import {
  if (!$module_loaded) {
    Texinfo::XSLoader::override("", "");
    Texinfo::XSLoader::override("", "");
    $module_loaded = 1;
  }
  goto &Exporter::import;
}
==========================================

gettext-tools/src/x-perl.c

index e1686ce9148346e3eab17a0b0116ab93915a5c5b..734ab3aaab7854920a79d53a563684ae048d27c9 100644 (file)
@@ -3106,6 +3106,11 @@ extract_balanced (message_list_ty *mlp,
                   flag_context_list_iterator_ty context_iter,
                   int arg, struct arglist_parser *argparser)
 {
+  /* Whether we are at the first token.  */
+  bool first = true;
+  /* Whether the first token was a "sub".  */
+  bool sub_seen = false;
+
   /* Whether to implicitly assume the next tokens are arguments even without
      a '('.  */
   bool next_is_argument = false;
@@ -3145,6 +3150,12 @@ extract_balanced (message_list_ty *mlp,
 
       tp = x_perl_lex (mlp);
 
+      if (first)
+        {
+          sub_seen = (tp->type == token_type_symbol
+                      && tp->sub_type == symbol_type_sub);
+        }
+
       if (delim == tp->type)
         {
           arglist_parser_done (argparser, arg);
@@ -3248,90 +3259,282 @@ extract_balanced (message_list_ty *mlp,
           next_is_argument = false;
           next_argparser = NULL;
           next_context_iter = null_context_list_iterator;
-          continue;
         }
-
-      switch (tp->type)
+      else
         {
-        case token_type_symbol:
-        case token_type_keyword_symbol:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type symbol (%d) \"%s\"\n",
-                   logical_file_name, tp->line_number, nesting_level,
-                   tp->string);
-          #endif
-
-          {
-            void *keyword_value;
+          switch (tp->type)
+            {
+            case token_type_symbol:
+              if (sub_seen)
+                break;
+              FALLTHROUGH;
+            case token_type_keyword_symbol:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type symbol (%d) \"%s\"\n",
+                       logical_file_name, tp->line_number, nesting_level,
+                       tp->string);
+              #endif
 
-            if (hash_find_entry (&keywords, tp->string, strlen (tp->string),
-                                 &keyword_value) == 0)
               {
-                const struct callshapes *shapes =
-                  (const struct callshapes *) keyword_value;
+                void *keyword_value;
 
-                next_shapes = shapes;
-                next_argparser = arglist_parser_alloc (mlp, shapes);
-              }
-            else
-              {
-                next_shapes = NULL;
-                next_argparser = arglist_parser_alloc (mlp, NULL);
+                if (hash_find_entry (&keywords, tp->string, strlen (tp->string),
+                                     &keyword_value) == 0)
+                  {
+                    const struct callshapes *shapes =
+                      (const struct callshapes *) keyword_value;
+
+                    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;
-          next_context_iter =
-            flag_context_list_iterator (
-              flag_context_list_table_lookup (
-                flag_context_list_table,
-                tp->string, strlen (tp->string)));
-          break;
+              next_is_argument = true;
+              next_context_iter =
+                flag_context_list_iterator (
+                  flag_context_list_table_lookup (
+                    flag_context_list_table,
+                    tp->string, strlen (tp->string)));
+              break;
 
-        case token_type_variable:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type variable (%d) \"%s\"\n",
-                   logical_file_name, tp->line_number, nesting_level,
-                   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_variable:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type variable (%d) \"%s\"\n",
+                       logical_file_name, tp->line_number, nesting_level,
+                       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_object:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type object (%d) \"%s->\"\n",
-                   logical_file_name, tp->line_number, nesting_level,
-                   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_object:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type object (%d) \"%s->\"\n",
+                       logical_file_name, tp->line_number, nesting_level,
+                       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_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.  */
+            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.  */
+                  ++nesting_depth;
+                  #if DEBUG_NESTING_DEPTH
+                  fprintf (stderr, "extract_balanced %d>> @%d\n", nesting_depth, line_number);
+                  #endif
+                  if (extract_balanced (mlp,
+                                        token_type_rparen, true,
+                                        false, false, false,
+                                        inner_context, next_context_iter,
+                                        1, next_argparser))
+                    {
+                      arglist_parser_done (argparser, arg);
+                      return true;
+                    }
+                  #if DEBUG_NESTING_DEPTH
+                  fprintf (stderr, "extract_balanced %d<< @%d\n", nesting_depth, line_number);
+                  #endif
+                  nesting_depth--;
+                  next_is_argument = false;
+                  next_argparser = NULL;
+                }
+              else
+                {
+                  /* Parse a parenthesized expression or comma expression.  */
+                  ++nesting_depth;
+                  #if DEBUG_NESTING_DEPTH
+                  fprintf (stderr, "extract_balanced %d>> @%d\n", nesting_depth, line_number);
+                  #endif
+                  if (extract_balanced (mlp,
+                                        token_type_rparen, true,
+                                        false, false, false,
+                                        inner_context, next_context_iter,
+                                        arg, arglist_parser_clone (argparser)))
+                    {
+                      arglist_parser_done (argparser, arg);
+                      if (next_argparser != NULL)
+                        free (next_argparser);
+                      free_token (tp);
+                      return true;
+                    }
+                  #if DEBUG_NESTING_DEPTH
+                  fprintf (stderr, "extract_balanced %d<< @%d\n", nesting_depth, line_number);
+                  #endif
+                  nesting_depth--;
+                  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;
+
+            case token_type_comma:
+            case token_type_fat_comma:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type comma (%d)\n",
+                       logical_file_name, tp->line_number, nesting_level);
+              #endif
+              if (arglist_parser_decidedp (argparser, arg))
+                {
+                  /* We have missed the argument.  */
+                  arglist_parser_done (argparser, arg);
+                  argparser = arglist_parser_alloc (mlp, NULL);
+                  arg = 0;
+                }
+              arg++;
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: arg: %d\n",
+                       real_file_name, tp->line_number, arg);
+              #endif
+              inner_context =
+                inherited_context (outer_context,
+                                   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;
+
+            case token_type_string:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type string (%d): \"%s\"\n",
+                       logical_file_name, tp->line_number, nesting_level,
+                       tp->string);
+              #endif
+
+              if (extract_all)
+                {
+                  char *string = collect_message (mlp, tp, EXIT_SUCCESS);
+                  lex_pos_ty pos;
+
+                  pos.file_name = logical_file_name;
+                  pos.line_number = tp->line_number;
+                  remember_a_message (mlp, NULL, string, true, false, inner_context,
+                                      &pos, NULL, tp->comment, true);
+                }
+              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);
+                      mixed_string_ty *ms =
+                        mixed_string_alloc_utf8 (string, lc_string,
+                                                 logical_file_name, tp->line_number);
+                      free (string);
+                      arglist_parser_remember (argparser, arg, ms, inner_context,
+                                               logical_file_name, tp->line_number,
+                                               tp->comment, true);
+                    }
+                }
+
+              if (arglist_parser_decidedp (argparser, arg))
+                {
+                  arglist_parser_done (argparser, arg);
+                  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;
+
+            case token_type_number:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type number (%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;
+              next_context_iter = null_context_list_iterator;
+              break;
+
+            case token_type_eof:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type EOF (%d)\n",
+                       logical_file_name, tp->line_number, nesting_level);
+              #endif
+              arglist_parser_done (argparser, arg);
+              if (next_argparser != NULL)
+                free (next_argparser);
+              next_argparser = NULL;
+              free_token (tp);
+              return true;
+
+            case token_type_lbrace:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type lbrace (%d)\n",
+                       logical_file_name, tp->line_number, nesting_level);
+              #endif
               ++nesting_depth;
               #if DEBUG_NESTING_DEPTH
               fprintf (stderr, "extract_balanced %d>> @%d\n", nesting_depth, line_number);
               #endif
               if (extract_balanced (mlp,
-                                    token_type_rparen, true,
+                                    token_type_rbrace, true,
                                     false, false, false,
-                                    inner_context, next_context_iter,
-                                    1, next_argparser))
+                                    null_context, null_context_list_iterator,
+                                    1, arglist_parser_alloc (mlp, NULL)))
                 {
                   arglist_parser_done (argparser, arg);
+                  if (next_argparser != NULL)
+                    free (next_argparser);
+                  free_token (tp);
                   return true;
                 }
               #if DEBUG_NESTING_DEPTH
@@ -3339,20 +3542,46 @@ extract_balanced (message_list_ty *mlp,
               #endif
               nesting_depth--;
               next_is_argument = false;
+              if (next_argparser != NULL)
+                free (next_argparser);
               next_argparser = NULL;
-            }
-          else
-            {
-              /* Parse a parenthesized expression or comma expression.  */
+              if (sub_seen)
+                {
+                  /* Go back to the caller.  We don't want to recurse each time we
+                     parsed a    sub name... { ... }    definition.  */
+                  arglist_parser_done (argparser, arg);
+                  free_token (tp);
+                  return false;
+                }
+              next_context_iter = null_context_list_iterator;
+              break;
+
+            case token_type_rbrace:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type rbrace (%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;
+              next_context_iter = null_context_list_iterator;
+              break;
+
+            case token_type_lbracket:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type lbracket (%d)\n",
+                       logical_file_name, tp->line_number, nesting_level);
+              #endif
               ++nesting_depth;
               #if DEBUG_NESTING_DEPTH
               fprintf (stderr, "extract_balanced %d>> @%d\n", nesting_depth, line_number);
               #endif
               if (extract_balanced (mlp,
-                                    token_type_rparen, true,
+                                    token_type_rbracket, true,
                                     false, false, false,
-                                    inner_context, next_context_iter,
-                                    arg, arglist_parser_clone (argparser)))
+                                    null_context, null_context_list_iterator,
+                                    1, arglist_parser_alloc (mlp, NULL)))
                 {
                   arglist_parser_done (argparser, arg);
                   if (next_argparser != NULL)
@@ -3368,322 +3597,118 @@ extract_balanced (message_list_ty *mlp,
               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;
-
-        case token_type_comma:
-        case token_type_fat_comma:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type comma (%d)\n",
-                   logical_file_name, tp->line_number, nesting_level);
-          #endif
-          if (arglist_parser_decidedp (argparser, arg))
-            {
-              /* We have missed the argument.  */
-              arglist_parser_done (argparser, arg);
-              argparser = arglist_parser_alloc (mlp, NULL);
-              arg = 0;
-            }
-          arg++;
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: arg: %d\n",
-                   real_file_name, tp->line_number, arg);
-          #endif
-          inner_context =
-            inherited_context (outer_context,
-                               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;
-
-        case token_type_string:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type string (%d): \"%s\"\n",
-                   logical_file_name, tp->line_number, nesting_level,
-                   tp->string);
-          #endif
-
-          if (extract_all)
-            {
-              char *string = collect_message (mlp, tp, EXIT_SUCCESS);
-              lex_pos_ty pos;
-
-              pos.file_name = logical_file_name;
-              pos.line_number = tp->line_number;
-              remember_a_message (mlp, NULL, string, true, false, inner_context,
-                                  &pos, NULL, tp->comment, true);
-            }
-          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];
+              next_context_iter = null_context_list_iterator;
+              break;
 
-                    if (arg == cp->argnumc
-                        || arg == cp->argnum1 || arg == cp->argnum2)
-                      must_collect = true;
-                  }
-              }
+            case token_type_rbracket:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type rbracket (%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;
+              next_context_iter = null_context_list_iterator;
+              break;
 
-              if (must_collect)
-                {
-                  char *string = collect_message (mlp, tp, EXIT_FAILURE);
-                  mixed_string_ty *ms =
-                    mixed_string_alloc_utf8 (string, lc_string,
-                                             logical_file_name, tp->line_number);
-                  free (string);
-                  arglist_parser_remember (argparser, arg, ms, inner_context,
-                                           logical_file_name, tp->line_number,
-                                           tp->comment, true);
-                }
-            }
+            case token_type_semicolon:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type semicolon (%d)\n",
+                       logical_file_name, tp->line_number, nesting_level);
+              #endif
 
-          if (arglist_parser_decidedp (argparser, arg))
-            {
+              /* The ultimate sign.  */
               arglist_parser_done (argparser, arg);
               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;
-
-        case token_type_number:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type number (%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;
-          next_context_iter = null_context_list_iterator;
-          break;
-
-        case token_type_eof:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type EOF (%d)\n",
-                   logical_file_name, tp->line_number, nesting_level);
-          #endif
-          arglist_parser_done (argparser, arg);
-          if (next_argparser != NULL)
-            free (next_argparser);
-          next_argparser = NULL;
-          free_token (tp);
-          return true;
-
-        case token_type_lbrace:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type lbrace (%d)\n",
-                   logical_file_name, tp->line_number, nesting_level);
-          #endif
-          ++nesting_depth;
-          #if DEBUG_NESTING_DEPTH
-          fprintf (stderr, "extract_balanced %d>> @%d\n", nesting_depth, line_number);
-          #endif
-          if (extract_balanced (mlp,
-                                token_type_rbrace, true,
-                                false, false, false,
-                                null_context, null_context_list_iterator,
-                                1, arglist_parser_alloc (mlp, NULL)))
-            {
-              arglist_parser_done (argparser, arg);
+              /* FIXME: Instead of resetting outer_context here, it may be better
+                 to recurse in the next_is_argument handling above, waiting for
+                 the next semicolon or other statement terminator.  */
+              outer_context = null_context;
+              context_iter = null_context_list_iterator;
+              next_is_argument = false;
               if (next_argparser != NULL)
                 free (next_argparser);
-              free_token (tp);
-              return true;
-            }
-          #if DEBUG_NESTING_DEPTH
-          fprintf (stderr, "extract_balanced %d<< @%d\n", nesting_depth, line_number);
-          #endif
-          nesting_depth--;
-          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_rbrace:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type rbrace (%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;
-          next_context_iter = null_context_list_iterator;
-          break;
+              next_argparser = NULL;
+              next_context_iter = passthrough_context_list_iterator;
+              inner_context =
+                inherited_context (outer_context,
+                                   flag_context_list_iterator_advance (
+                                     &context_iter));
+              break;
 
-        case token_type_lbracket:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type lbracket (%d)\n",
-                   logical_file_name, tp->line_number, nesting_level);
-          #endif
-          ++nesting_depth;
-          #if DEBUG_NESTING_DEPTH
-          fprintf (stderr, "extract_balanced %d>> @%d\n", nesting_depth, line_number);
-          #endif
-          if (extract_balanced (mlp,
-                                token_type_rbracket, true,
-                                false, false, false,
-                                null_context, null_context_list_iterator,
-                                1, arglist_parser_alloc (mlp, NULL)))
-            {
-              arglist_parser_done (argparser, arg);
+            case token_type_dereference:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type dereference (%d)\n",
+                       logical_file_name, tp->line_number, nesting_level);
+              #endif
+              next_is_argument = false;
               if (next_argparser != NULL)
                 free (next_argparser);
-              free_token (tp);
-              return true;
-            }
-          #if DEBUG_NESTING_DEPTH
-          fprintf (stderr, "extract_balanced %d<< @%d\n", nesting_depth, line_number);
-          #endif
-          nesting_depth--;
-          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_rbracket:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type rbracket (%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;
-          next_context_iter = null_context_list_iterator;
-          break;
-
-        case token_type_semicolon:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type semicolon (%d)\n",
-                   logical_file_name, tp->line_number, nesting_level);
-          #endif
-
-          /* The ultimate sign.  */
-          arglist_parser_done (argparser, arg);
-          argparser = arglist_parser_alloc (mlp, NULL);
-
-          /* FIXME: Instead of resetting outer_context here, it may be better
-             to recurse in the next_is_argument handling above, waiting for
-             the next semicolon or other statement terminator.  */
-          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,
-                               flag_context_list_iterator_advance (
-                                 &context_iter));
-          break;
+              next_argparser = NULL;
+              next_context_iter = null_context_list_iterator;
+              break;
 
-        case token_type_dereference:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type dereference (%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;
-          next_context_iter = null_context_list_iterator;
-          break;
+            case token_type_dot:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type dot (%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;
+              next_context_iter = null_context_list_iterator;
+              break;
 
-        case token_type_dot:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type dot (%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;
-          next_context_iter = null_context_list_iterator;
-          break;
+            case token_type_named_op:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type named operator (%d): %s\n",
+                       logical_file_name, tp->line_number, nesting_level,
+                       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_named_op:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type named operator (%d): %s\n",
-                   logical_file_name, tp->line_number, nesting_level,
-                   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:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type regex operator (%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;
+              next_context_iter = null_context_list_iterator;
+              break;
 
-        case token_type_regex_op:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type regex operator (%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;
-          next_context_iter = null_context_list_iterator;
-          break;
+            case token_type_other:
+              #if DEBUG_PERL
+              fprintf (stderr, "%s:%d: type other (%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;
+              next_context_iter = null_context_list_iterator;
+              break;
 
-        case token_type_other:
-          #if DEBUG_PERL
-          fprintf (stderr, "%s:%d: type other (%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;
-          next_context_iter = null_context_list_iterator;
-          break;
+            default:
+              fprintf (stderr, "%s:%d: unknown token type %d\n",
+                       real_file_name, tp->line_number, (int) tp->type);
+              abort ();
+            }
 
-        default:
-          fprintf (stderr, "%s:%d: unknown token type %d\n",
-                   real_file_name, tp->line_number, (int) tp->type);
-          abort ();
+          free_token (tp);
         }
 
-      free_token (tp);
+      first = false;
     }
 }