changeset 33398:6714a426f484

new nonterminal for storing lists of separator tokens in the parser * separator-list.h: New file. * libinterp/parse-tree/module.mk: Update. * parse.h, oct-parse.yy (sep_list_type): New nonterminal type. (sep_no_nl, opt_sep_no_nl, nl, opt_nl, sep, opt_sep): Declare as sep_list_type instead of punct_type. Update rules to create separator list objects instead of returning characters. In rules that use them, delete as yet unsaved separator_list objects. (punct_type): Delete now unused nonterminal type. (base_parser::set_stmt_print_flag, base_parser::append_statement_list): New overloads that accept separator token and separator_list objects. (base_parser::append_function_def_list): Accept separator_list instead of char. (base_parser::make_try_command): Update to use separator list instead of character.
author John W. Eaton <jwe@octave.org>
date Fri, 12 Apr 2024 14:52:31 -0400
parents b4c4f0d65b8a
children 29c07704f120
files libinterp/parse-tree/module.mk libinterp/parse-tree/oct-parse.yy libinterp/parse-tree/parse.h libinterp/parse-tree/separator-list.h
diffstat 4 files changed, 317 insertions(+), 131 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/parse-tree/module.mk	Sat Apr 13 12:57:34 2024 -0400
+++ b/libinterp/parse-tree/module.mk	Fri Apr 12 14:52:31 2024 -0400
@@ -42,6 +42,7 @@
   %reldir%/pt-unop.h \
   %reldir%/pt-walk.h \
   %reldir%/pt.h \
+  %reldir%/separator-list.h \
   %reldir%/token.h
 
 
--- a/libinterp/parse-tree/oct-parse.yy	Sat Apr 13 12:57:34 2024 -0400
+++ b/libinterp/parse-tree/oct-parse.yy	Fri Apr 12 14:52:31 2024 -0400
@@ -73,6 +73,7 @@
 #include "parse.h"
 #include "pt-all.h"
 #include "pt-eval.h"
+#include "separator-list.h"
 #include "symtab.h"
 #include "token.h"
 #include "unwind-prot.h"
@@ -141,8 +142,10 @@
   // The type of the basic tokens returned by the lexer.
   octave::token *tok;
 
+  // Lists of separators with optional comment lists attached.
+  octave::separator_list *sep_list_type;
+
   // Types for the nonterminals we generate.
-  char punct_type;
   octave::tree *tree_type;
   octave::tree_matrix *tree_matrix_type;
   octave::tree_cell *tree_cell_type;
@@ -247,7 +250,7 @@
 %type <tok> param_list_beg param_list_end
 %type <tok> function_beg classdef_beg arguments_beg indirect_ref_op
 %type <tok> properties_beg methods_beg events_beg enumeration_beg
-%type <punct_type> sep_no_nl opt_sep_no_nl nl opt_nl sep opt_sep
+%type <sep_list_type> sep_no_nl opt_sep_no_nl nl opt_nl sep opt_sep
 %type <tree_type> input
 %type <tree_constant_type> string constant magic_colon
 %type <tree_anon_fcn_handle_type> anon_fcn_handle
@@ -444,7 +447,10 @@
 
 simple_list     : opt_sep_no_nl
                   {
-                    OCTAVE_YYUSE ($1);
+                    // FIXME: should we return an empty statement list
+                    // with the separators attached?  For now, delete
+                    // the unused list.
+                    delete $1;
 
                     $$ = nullptr;
                   }
@@ -461,7 +467,8 @@
 statement_list  : opt_sep opt_list
                   {
                     // FIXME: Need to capture separator list here.
-                    OCTAVE_YYUSE ($1);
+                    // For now, delete the unused list.
+                    delete $1;
 
                     $$ = $2;
                   }
@@ -473,7 +480,13 @@
                 ;
 
 list            : list1 opt_sep
-                  { $$ = parser.set_stmt_print_flag ($1, $2, true); }
+                  {
+                    $$ = parser.set_stmt_print_flag ($1, $2, true);
+
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
+                  }
                 ;
 
 list1           : statement
@@ -490,7 +503,9 @@
 
 fcn_list        : fcn_list1 opt_sep
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     $$ = $1;
                   }
@@ -595,6 +610,7 @@
                   { $$ = nullptr; }
                 | ','
                   {
+                    // FIXME: Need to capture separator token info here.
                     OCTAVE_YYUSE ($1);
 
                     $$ = nullptr;
@@ -603,18 +619,21 @@
                   { $$ = $1; }
                 | arg_list ','
                   {
+                    // FIXME: Need to capture separator token info here.
                     OCTAVE_YYUSE ($2);
 
                     $$ = $1;
                   }
                 | ',' arg_list
                   {
+                    // FIXME: Need to capture separator token info here.
                     OCTAVE_YYUSE ($1);
 
                     $$ = $2;
                   }
                 | ',' arg_list ','
                   {
+                    // FIXME: Need to capture separator token info here.
                     OCTAVE_YYUSE ($1, $3);
 
                     $$ = $2;
@@ -1028,25 +1047,17 @@
                 ;
 
 if_clause       : IF opt_sep expression stmt_begin statement_list
-                  {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.make_if_clause ($1, $3, $5);
-                  }
+                  { $$ = parser.make_if_clause ($1, $2, $3, $5); }
                 ;
 
 elseif_clause   : ELSEIF opt_sep expression stmt_begin statement_list
-                  {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.make_if_clause ($1, $3, $5);
-                  }
+                  { $$ = parser.make_if_clause ($1, $2, $3, $5); }
                 ;
 
 else_clause     : // empty
                   { $$ = nullptr; }
                 | ELSE statement_list
-                  { $$ = parser.make_if_clause ($1, nullptr, $2); }
+                  { $$ = parser.make_if_clause ($1, nullptr, nullptr, $2); }
                 ;
 
 // ================
@@ -1055,7 +1066,9 @@
 
 switch_command  : SWITCH expression opt_sep case_list END
                   {
-                    OCTAVE_YYUSE ($3);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $3;
 
                     if (! ($$ = parser.finish_switch_command ($1, $2, $4, $5)))
                       {
@@ -1083,7 +1096,9 @@
 
 switch_case     : CASE opt_sep expression stmt_begin statement_list
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     $$ = parser.make_switch_case ($1, $3, $5);
                   }
@@ -1121,6 +1136,7 @@
                   }
                 | FOR '(' assign_lhs '=' expression ')' statement_list END
                   {
+                    // FIXME: Need to capture token info here.
                     OCTAVE_YYUSE ($2, $4, $6);
 
                     if (! ($$ = parser.make_for_command ($1, $2, $3, $4, $5, nullptr, nullptr, $6, $7, $8)))
@@ -1131,6 +1147,7 @@
                   }
                 | PARFOR assign_lhs '=' expression stmt_begin statement_list END
                   {
+                    // FIXME: Need to capture token info here.
                     OCTAVE_YYUSE ($3);
 
                     if (! ($$ = parser.make_for_command ($1, nullptr, $2, $3, $4, nullptr, nullptr, nullptr, $6, $7)))
@@ -1141,6 +1158,7 @@
                   }
                 | PARFOR '(' assign_lhs '=' expression ',' expression ')' statement_list END
                   {
+                    // FIXME: Need to capture token info here.
                     OCTAVE_YYUSE ($2, $4, $6, $8);
 
                     if (! ($$ = parser.make_for_command ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)))
@@ -1199,13 +1217,13 @@
                   {
                     if (! ($$ = parser.make_try_command ($1, $2, $3, $4, $5, $6)))
                       {
-                        // make_try_command deleted $2 and $5.
+                        // make_try_command deleted $2, $4, and $5.
                         YYABORT;
                       }
                   }
                 | TRY statement_list END
                   {
-                    if (! ($$ = parser.make_try_command ($1, $2, nullptr, 0, nullptr, $3)))
+                    if (! ($$ = parser.make_try_command ($1, $2, nullptr, nullptr, nullptr, $3)))
                       {
                         // make_try_command deleted $2.
                         YYABORT;
@@ -1389,7 +1407,9 @@
 
 file            : begin_file opt_nl opt_list END_OF_INPUT
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     if (lexer.m_reading_fcn_file)
                       {
@@ -1418,7 +1438,10 @@
                   }
                 | begin_file opt_nl classdef parsing_local_fcns opt_sep opt_fcn_list END_OF_INPUT
                   {
-                    OCTAVE_YYUSE ($2, $5);
+                    // FIXME: Need to capture separator lists here.
+                    // For now, delete the unused lists.
+                    delete $2;
+                    delete $5;
 
                     // Unused symbol table context.
                     lexer.m_symtab_context.pop ();
@@ -1521,13 +1544,17 @@
 
 function        : function_beg fcn_name opt_param_list opt_sep function_body function_end
                   {
-                    OCTAVE_YYUSE ($4);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $4;
 
                     $$ = parser.make_function ($1, nullptr, nullptr, $2, $3, $5, $6);
                   }
                 | function_beg return_list '=' fcn_name opt_param_list opt_sep function_body function_end
                   {
-                    OCTAVE_YYUSE ($6);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $6;
 
                     $$ = parser.make_function ($1, $2, $3, $4, $5, $7, $8);
                   }
@@ -1537,7 +1564,9 @@
                   { $$ = $2; }
                 | function_body1 opt_sep at_first_executable_stmt opt_list
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     $$ = parser.append_function_body ($1, $4);
                   }
@@ -1564,7 +1593,10 @@
 
 arguments_block : arguments_beg opt_sep args_attr_list args_validation_list opt_sep END
                   {
-                    OCTAVE_YYUSE ($2, $5);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused lists.
+                    delete $2;
+                    delete $5;
 
                     if (! ($$ = parser.make_arguments_block ($1, $3, $4, $6)))
                       {
@@ -1606,7 +1638,9 @@
                     }
                   | args_validation_list sep arg_name arg_validation
                     {
-                      OCTAVE_YYUSE ($2);
+                      // FIXME: Need to capture SEP here.
+                      // For now, delete the unused list.
+                      delete $2;
 
                       $4->arg_name ($3);
                       $$ = parser.append_args_validation_list ($1, $4);
@@ -1698,7 +1732,9 @@
 
 classdef        : classdef_beg attr_list identifier opt_sep superclass_list class_body END
                   {
-                    OCTAVE_YYUSE ($4);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $4;
 
                     lexer.m_parsing_classdef = false;
 
@@ -1714,7 +1750,9 @@
                   { $$ = nullptr; }
                 | '(' attr_list1 ')' opt_sep
                   {
-                    OCTAVE_YYUSE ($4);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $4;
 
                     $$ = $2->mark_in_delims (*($1), *($3));
                   }
@@ -1744,7 +1782,9 @@
                   }
                 | superclass_list1 opt_sep
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     lexer.m_parsing_classdef_decl = false;
                     lexer.m_parsing_classdef_superclass = false;
@@ -1770,7 +1810,9 @@
                   }
                 | class_body1 opt_sep
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     lexer.m_classdef_element_names_are_keywords = false;
                     $$ = $1;
@@ -1787,25 +1829,33 @@
                   { $$ = parser.make_classdef_body ($1); }
                 | class_body1 opt_sep properties_block
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     $$ = parser.append_classdef_properties_block ($1, $3);
                   }
                 | class_body1 opt_sep methods_block
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     $$ = parser.append_classdef_methods_block ($1, $3);
                   }
                 | class_body1 opt_sep events_block
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     $$ = parser.append_classdef_events_block ($1, $3);
                   }
                 | class_body1 opt_sep enum_block
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     $$ = parser.append_classdef_enum_block ($1, $3);
                   }
@@ -1814,7 +1864,9 @@
 properties_block
                 : properties_beg opt_sep attr_list property_list END
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     if (! ($$ = parser.make_classdef_properties_block ($1, $3, $4, $5)))
                       {
@@ -1838,7 +1890,9 @@
                   }
                 | property_list1 opt_sep
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     lexer.m_classdef_element_names_are_keywords = true;
                     $$ = $1;
@@ -1850,7 +1904,9 @@
                   { $$ = parser.make_classdef_property_list ($1); }
                 | property_list1 sep class_property
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture SEP here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     // We don't look ahead to grab end-of-line comments.
                     // Instead, they are grabbed when we see the
@@ -1885,7 +1941,9 @@
 
 methods_block   : methods_beg opt_sep attr_list method_list END
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     if (! ($$ = parser.make_classdef_methods_block ($1, $3, $4, $5)))
                       {
@@ -1943,7 +2001,9 @@
                   }
                 | method_list1 opt_sep
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     lexer.m_classdef_element_names_are_keywords = true;
                     $$ = $1;
@@ -1954,7 +2014,9 @@
                   { $$ = parser.make_classdef_method_list ($1); }
                 | method_list1 opt_sep method
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     $$ = parser.append_classdef_method ($1, $3);
                   }
@@ -1962,7 +2024,9 @@
 
 events_block    : events_beg opt_sep attr_list event_list END
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     if (! ($$ = parser.make_classdef_events_block ($1, $3, $4, $5)))
                       {
@@ -1986,7 +2050,9 @@
                   }
                 | event_list1 opt_sep
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     lexer.m_classdef_element_names_are_keywords = true;
                     $$ = $1;
@@ -1997,7 +2063,9 @@
                   { $$ = parser.make_classdef_event_list ($1); }
                 | event_list1 opt_sep class_event
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     $$ = parser.append_classdef_event ($1, $3);
                   }
@@ -2009,7 +2077,9 @@
 
 enum_block      : enumeration_beg opt_sep attr_list enum_list END
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     if (! ($$ = parser.make_classdef_enum_block ($1, $3, $4, $5)))
                       {
@@ -2033,7 +2103,9 @@
                   }
                 | enum_list1 opt_sep
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     lexer.m_classdef_element_names_are_keywords = true;
                     $$ = $1;
@@ -2044,7 +2116,9 @@
                   { $$ = parser.make_classdef_enum_list ($1); }
                 | enum_list1 opt_sep class_enum
                   {
-                    OCTAVE_YYUSE ($2);
+                    // FIXME: Need to capture separator list here.
+                    // For now, delete the unused list.
+                    delete $2;
 
                     $$ = parser.append_classdef_enum ($1, $3);
                   }
@@ -2084,33 +2158,17 @@
                 ;
 
 sep_no_nl       : ','
-                  {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = ',';
-                  }
+                  { $$ = new octave::separator_list (*($1)); }
                 | ';'
-                  {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = ';';
-                  }
+                  { $$ = new octave::separator_list (*($1)); }
                 | sep_no_nl ','
-                  {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = $1;
-                  }
+                  { $$ = $1->append (*($2)); }
                 | sep_no_nl ';'
-                  {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = $1;
-                  }
+                  { $$ = $1->append (*($2)); }
                 ;
 
 opt_sep_no_nl   : // empty
-                  { $$ = 0; }
+                  { $$ = nullptr; }
                 | sep_no_nl
                   { $$ = $1; }
                 ;
@@ -2122,59 +2180,27 @@
                 ;
 
 nl              : '\n'
-                  {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = '\n';
-                  }
+                  { $$ = new octave::separator_list (*($1)); }
                 | nl '\n'
-                  {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = $1;
-                  }
+                  { $$ = $1->append (*($2)); }
                 ;
 
 sep             : ','
-                  {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = ',';
-                  }
+                  { $$ = new octave::separator_list (*($1)); }
                 | ';'
-                  {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = ';';
-                  }
+                  { $$ = new octave::separator_list (*($1)); }
                 | '\n'
-                  {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = '\n';
-                  }
+                  { $$ = new octave::separator_list (*($1)); }
                 | sep ','
-                  {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = $1;
-                  }
+                  { $$ = $1->append (*($2)); }
                 | sep ';'
-                  {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = $1;
-                  }
+                  { $$ = $1->append (*($2)); }
                 | sep '\n'
-                  {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = $1;
-                  }
+                  { $$ = $1->append (*($2)); }
                 ;
 
 opt_sep         : // empty
-                  { $$ = 0; }
+                  { $$ = nullptr; }
                 | sep
                   { $$ = $1; }
                 ;
@@ -2321,7 +2347,7 @@
 static LIST_T *
 list_append (LIST_T *list, const token& /*sep_tok*/, ELT_T elt)
 {
-  // FIXME XXX! need to capture SEP_TOK here
+  // FIXME: Need to capture SEP_TOK here
   list->push_back (elt);
   return list;
 }
@@ -3076,7 +3102,7 @@
 // Build a try-catch command.
 
 tree_command *
-base_parser::make_try_command (token *try_tok, tree_statement_list *body, token *catch_tok, char catch_sep, tree_statement_list *cleanup_stmts, token *end_tok)
+base_parser::make_try_command (token *try_tok, tree_statement_list *body, token *catch_tok, separator_list *catch_sep_list, tree_statement_list *cleanup_stmts, token *end_tok)
 {
   tree_command *retval = nullptr;
 
@@ -3084,10 +3110,30 @@
     {
       tree_identifier *id = nullptr;
 
-      // Look for exception ID.  Could this be done in the grammar or
-      // does that create another shift-reduce conflict?
-
-      if (! catch_sep && cleanup_stmts && ! cleanup_stmts->empty ())
+      // Look for exception ID.  Note that adding a separate rule to
+      // match
+      //
+      //   try
+      //     try-body
+      //   catch IDENTIFIER
+      //     catch-body
+      //   end
+      //
+      // in the grammar leads to yet another shift-reduce conflict, so
+      // instead we only match
+      //
+      //   try
+      //     try-body
+      //   catch
+      //     catch-body
+      //   end
+      //
+      // and then recognize the first form above by checking that the
+      // first element of CATCH-BODY is an identifier and that there is
+      // is no separator (comma, semicolon, or newline) between the
+      // CATCH token and the identifier.
+
+      if (! catch_sep_list && cleanup_stmts && ! cleanup_stmts->empty ())
         {
           tree_statement *stmt = cleanup_stmts->front ();
 
@@ -3109,11 +3155,16 @@
 
       token tmp_catch_tok = catch_tok ? *catch_tok : token ();
 
+      // FIXME: Need to capture separator list here.
+      // For now, delete the unused list.
+      delete catch_sep_list;
+
       retval = new tree_try_catch_command (*try_tok, body, tmp_catch_tok, id, cleanup_stmts, *end_tok);
     }
   else
     {
       delete body;
+      delete catch_sep_list;
       delete cleanup_stmts;
 
       end_token_error (end_tok, token::try_catch_end);
@@ -3311,7 +3362,7 @@
 // Build an if, elseif, or else clause.
 
 tree_if_clause *
-base_parser::make_if_clause (token *tok, tree_expression *expr, tree_statement_list *list)
+base_parser::make_if_clause (token *if_tok, separator_list *if_sep_list, tree_expression *expr, tree_statement_list *list)
 {
   if (expr)
     {
@@ -3320,7 +3371,11 @@
       maybe_convert_to_braindead_shortcircuit (expr);
     }
 
-  return new tree_if_clause (*tok, expr, list);
+  // FIXME: Need to capture separator list here.
+  // For now, delete the unused list.
+  delete if_sep_list;
+
+  return new tree_if_clause (*if_tok, expr, list);
 }
 
 tree_if_command_list *
@@ -4581,7 +4636,7 @@
 tree_decl_elt *
 base_parser::make_decl_elt (tree_identifier *id, token */*eq_op*/, tree_expression *expr)
 {
-  // FIXME XXX! need to capture EQ_OP here.
+  // FIXME: Need to capture EQ_OP here.
   return expr ? new tree_decl_elt (id, expr) : new tree_decl_elt (id);
 }
 
@@ -4880,17 +4935,17 @@
 }
 
 tree_statement_list *
-base_parser::set_stmt_print_flag (tree_statement_list *list, char sep, bool warn_missing_semi)
+base_parser::set_stmt_print_flag (tree_statement_list *list, int sep_char, bool warn_missing_semi)
 {
   tree_statement *tmp = list->back ();
 
-  switch (sep)
+  switch (sep_char)
     {
     case ';':
       tmp->set_print_flag (false);
       break;
 
-    case 0:
+    case '\0':
     case ',':
     case '\n':
       tmp->set_print_flag (true);
@@ -4915,6 +4970,20 @@
   return list;
 }
 
+tree_statement_list *
+base_parser::set_stmt_print_flag (tree_statement_list *list, const token& sep_tok, bool warn_missing_semi)
+{
+  return set_stmt_print_flag (list, sep_tok.token_id (), warn_missing_semi);
+}
+
+tree_statement_list *
+base_parser::set_stmt_print_flag (tree_statement_list *list, separator_list *sep_list, bool warn_missing_semi)
+{
+  return (sep_list
+          ? set_stmt_print_flag (list, sep_list->front (), warn_missing_semi)
+          : set_stmt_print_flag (list, '\0', warn_missing_semi));
+}
+
 // Finish building a statement.
 template <typename T>
 tree_statement *
@@ -4930,9 +4999,34 @@
 }
 
 tree_statement_list *
-base_parser::append_statement_list (tree_statement_list *list, char sep, tree_statement *stmt, bool warn_missing_semi)
-{
-  set_stmt_print_flag (list, sep, warn_missing_semi);
+base_parser::append_statement_list (tree_statement_list *list, int sep_char, tree_statement *stmt, bool warn_missing_semi)
+{
+  set_stmt_print_flag (list, sep_char, warn_missing_semi);
+
+  // FIXME: need to capture SEP_CHAR here.
+  return list_append (list, stmt);
+}
+
+tree_statement_list *
+base_parser::append_statement_list (tree_statement_list *list, token *sep_tok, tree_statement *stmt, bool warn_missing_semi)
+{
+  if (sep_tok)
+    set_stmt_print_flag (list, *sep_tok, warn_missing_semi);
+  else
+    set_stmt_print_flag (list, '\0', warn_missing_semi);
+
+  // FIXME: need to capture SEP_TOK here.
+  return list_append (list, stmt);
+}
+
+tree_statement_list *
+base_parser::append_statement_list (tree_statement_list *list, separator_list *sep_list, tree_statement *stmt, bool warn_missing_semi)
+{
+  set_stmt_print_flag (list, sep_list, warn_missing_semi);
+
+  // FIXME: need to capture separator list here.
+  // For now, delete the unused list.
+  delete sep_list;
 
   return list_append (list, stmt);
 }
@@ -4946,10 +5040,14 @@
 }
 
 tree_statement_list *
-base_parser::append_function_def_list (tree_statement_list *list, char, tree_function_def *fcn_def)
+base_parser::append_function_def_list (tree_statement_list *list, separator_list *sep_list, tree_function_def *fcn_def)
 {
   tree_statement *stmt = make_statement (fcn_def);
 
+  // FIXME: Need to capture separator list here.
+  // For now, delete the unused list.
+  delete sep_list;
+
   return list_append (list, stmt);
 }
 
--- a/libinterp/parse-tree/parse.h	Sat Apr 13 12:57:34 2024 -0400
+++ b/libinterp/parse-tree/parse.h	Fri Apr 12 14:52:31 2024 -0400
@@ -49,6 +49,7 @@
 OCTAVE_BEGIN_NAMESPACE(octave)
 
 class parse_exception;
+class separator_list;
 class tree;
 class tree_anon_fcn_handle;
 class tree_arg_size_spec;
@@ -288,7 +289,7 @@
 
   // Build a try-catch command.
   OCTINTERP_API tree_command *
-  make_try_command (token *try_tok, tree_statement_list *body, token *catch_tok, char catch_sep, tree_statement_list *cleanup, token *end_tok);
+  make_try_command (token *try_tok, tree_statement_list *body, token *catch_tok, separator_list *catch_sep_list, tree_statement_list *cleanup, token *end_tok);
 
   // Build a while command.
   OCTINTERP_API tree_command *
@@ -326,7 +327,7 @@
 
   // Build an elseif clause.
   OCTINTERP_API tree_if_clause *
-  make_if_clause (token *if_tok, tree_expression *expr, tree_statement_list *list);
+  make_if_clause (token *if_tok, separator_list *if_sep_list, tree_expression *expr, tree_statement_list *list);
 
   OCTINTERP_API tree_if_command_list *
   append_if_clause (tree_if_command_list *list, tree_if_clause *clause);
@@ -582,7 +583,15 @@
 
   // Set the print flag for a statement based on the separator type.
   OCTINTERP_API tree_statement_list *
-  set_stmt_print_flag (tree_statement_list *, char, bool);
+  set_stmt_print_flag (tree_statement_list *list, int sep_char, bool warn_missing_semi);
+
+  // Set the print flag for a statement based on the separator type.
+  OCTINTERP_API tree_statement_list *
+  set_stmt_print_flag (tree_statement_list *list, const token& sep_tok, bool warn_missing_semi);
+
+  // Set the print flag for a statement based on the separator type.
+  OCTINTERP_API tree_statement_list *
+  set_stmt_print_flag (tree_statement_list *list, separator_list *sep_list, bool warn_missing_semi);
 
   // Finish building a statement.
   template <typename T>
@@ -594,7 +603,15 @@
 
   // Append a statement to an existing statement list.
   OCTINTERP_API tree_statement_list *
-  append_statement_list (tree_statement_list *list, char sep, tree_statement *stmt, bool warn_missing_semi);
+  append_statement_list (tree_statement_list *list, int sep_char, tree_statement *stmt, bool warn_missing_semi);
+
+  // Append a statement to an existing statement list.
+  OCTINTERP_API tree_statement_list *
+  append_statement_list (tree_statement_list *list, token *sep_tok, tree_statement *stmt, bool warn_missing_semi);
+
+  // Append a statement to an existing statement list.
+  OCTINTERP_API tree_statement_list *
+  append_statement_list (tree_statement_list *list, separator_list *sep_list, tree_statement *stmt, bool warn_missing_semi);
 
   // Create a statement list containing only function_def commands.
   OCTINTERP_API tree_statement_list *
@@ -603,7 +620,7 @@
   // Append a function_def command to an existing statement list (that
   // should contain only other function_def commands).
   OCTINTERP_API tree_statement_list *
-  append_function_def_list (tree_statement_list *list, char sep, tree_function_def *fcn_def);
+  append_function_def_list (tree_statement_list *list, separator_list *sep_list, tree_function_def *fcn_def);
 
   OCTINTERP_API tree_argument_list *
   make_argument_list (tree_expression *expr);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libinterp/parse-tree/separator-list.h	Fri Apr 12 14:52:31 2024 -0400
@@ -0,0 +1,70 @@
+////////////////////////////////////////////////////////////////////////
+//
+// Copyright (C) 2024 The Octave Project Developers
+//
+// See the file COPYRIGHT.md in the top-level directory of this
+// distribution or <https://octave.org/copyright/>.
+//
+// This file is part of Octave.
+//
+// Octave is free software: you can redistribute it and/or modify it
+// under the terms of the GNU General Public License as published by
+// the Free Software Foundation, either version 3 of the License, or
+// (at your option) any later version.
+//
+// Octave is distributed in the hope that it will be useful, but
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with Octave; see the file COPYING.  If not, see
+// <https://www.gnu.org/licenses/>.
+//
+////////////////////////////////////////////////////////////////////////
+
+#if ! defined (octave_separator_list_h)
+#define octave_separator_list_h 1
+
+#include "octave-config.h"
+
+#include <list>
+
+#include "token.h"
+
+OCTAVE_BEGIN_NAMESPACE(octave)
+
+class separator_list : public std::list<token>
+{
+public:
+
+  separator_list (const token& tok)
+  {
+    append (tok);
+  }
+
+  OCTAVE_DEFAULT_CONSTRUCT_COPY_MOVE_DELETE (separator_list)
+
+  separator_list *append (const token& tok)
+  {
+    push_back (tok);
+
+    return this;
+  }
+
+  int first_sep_char () const
+  {
+    token tok = front ();
+
+    return tok.token_id ();
+  }
+
+  separator_list * dup () const
+  {
+    return new separator_list (*this);
+  }
+};
+
+OCTAVE_END_NAMESPACE(octave)
+
+#endif