changeset 33296:70b7f1c285c7

refactor comment handling in the lexer and parser These changes simplify the grammar rules in the parser by eliminating the "stash_comment" rule that previously grabbed comments from the lexer at select locations. All .m file comments are now stored in tokens created by the lexer. Most tree_* objects processed by the parser now store tokens in order to capture comment information, though some separator tokens are not handled yet (commas, semicolons, newlines, and structure element reference operators). These tokens will be handled by a future change. The tree_print_code class will also be updated for this new way of handling comments in a later change.n These changes affect the lexer, parser, octave_user_fcn, and most tree_* classes: cdef-class.cc, ov-usr-fcn.cc, ov-usr-fcn.h, lex.h, lex.ll, oct-parse.yy, parse.h, pt-arg-list.h, pt-args-block.h, pt-assign.h, pt-classdef.cc, pt-classdef.h, pt-cmd.h, pt-colon.cc, pt-colon.h, pt-eval.cc, pt-except.cc, pt-except.h, pt-exp.h, pt-id.cc, pt-id.h, pt-idx.cc, pt-idx.h, pt-loop.cc, pt-loop.h, pt-misc.h, pt-pr-code.cc, pt-pr-code.h, pt-select.cc, pt-select.h, pt-spmd.cc, pt-spmd.h, pt-stmt.cc, pt-stmt.h, pt-walk.cc, pt-walk.h, pt.cc, and pt.h.
author John W. Eaton <jwe@octave.org>
date Mon, 01 Apr 2024 23:27:43 -0400
parents 979a51024c94
children 0376082b659f
files libinterp/octave-value/cdef-class.cc libinterp/octave-value/ov-usr-fcn.cc libinterp/octave-value/ov-usr-fcn.h libinterp/parse-tree/lex.h libinterp/parse-tree/lex.ll libinterp/parse-tree/oct-parse.yy libinterp/parse-tree/parse.h libinterp/parse-tree/pt-arg-list.h libinterp/parse-tree/pt-args-block.h libinterp/parse-tree/pt-assign.h libinterp/parse-tree/pt-classdef.cc libinterp/parse-tree/pt-classdef.h libinterp/parse-tree/pt-cmd.h libinterp/parse-tree/pt-colon.cc libinterp/parse-tree/pt-colon.h libinterp/parse-tree/pt-eval.cc libinterp/parse-tree/pt-except.cc libinterp/parse-tree/pt-except.h libinterp/parse-tree/pt-exp.h libinterp/parse-tree/pt-id.cc libinterp/parse-tree/pt-id.h libinterp/parse-tree/pt-idx.cc libinterp/parse-tree/pt-idx.h libinterp/parse-tree/pt-loop.cc libinterp/parse-tree/pt-loop.h libinterp/parse-tree/pt-misc.h libinterp/parse-tree/pt-pr-code.cc libinterp/parse-tree/pt-pr-code.h libinterp/parse-tree/pt-select.cc libinterp/parse-tree/pt-select.h libinterp/parse-tree/pt-spmd.cc libinterp/parse-tree/pt-spmd.h libinterp/parse-tree/pt-stmt.cc libinterp/parse-tree/pt-stmt.h libinterp/parse-tree/pt-walk.cc libinterp/parse-tree/pt-walk.h libinterp/parse-tree/pt.cc libinterp/parse-tree/pt.h
diffstat 38 files changed, 1344 insertions(+), 1773 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/octave-value/cdef-class.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/octave-value/cdef-class.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -969,7 +969,7 @@
 
       // Method blocks
 
-      std::list<tree_classdef_methods_block *> mb_list = b->methods_list ();
+      std::list<tree_classdef_methods_block *> mb_list = b->method_list ();
 
       load_path& lp = interp.get_load_path ();
 
@@ -1083,7 +1083,7 @@
       //        evaluating default value expressions.
 
       std::list<tree_classdef_properties_block *> pb_list
-        = b->properties_list ();
+        = b->property_list ();
 
       for (auto& pb_p : pb_list)
         {
--- a/libinterp/octave-value/ov-usr-fcn.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/octave-value/ov-usr-fcn.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -45,6 +45,7 @@
 #include "ov-usr-fcn.h"
 #include "ov.h"
 #include "pager.h"
+#include "pt-cmd.h"
 #include "pt-eval.h"
 #include "pt-jump.h"
 #include "pt-misc.h"
@@ -215,7 +216,6 @@
  octave::tree_parameter_list *rl, octave::tree_statement_list *cl)
   : octave_user_code ("", "", scope, cl, ""),
     m_param_list (pl), m_ret_list (rl),
-    m_lead_comm (), m_trail_comm (),
     m_location_line (0), m_location_column (0),
     m_system_fcn_file (false),
     m_num_named_args (m_param_list ? m_param_list->size () : 0),
@@ -231,8 +231,6 @@
 {
   delete m_param_list;
   delete m_ret_list;
-  delete m_lead_comm;
-  delete m_trail_comm;
 }
 
 std::string
@@ -266,6 +264,39 @@
   return this;
 }
 
+void
+octave_user_function::attach_trailing_comments (const octave::comment_list& lst)
+{
+  if (m_cmd_list && ! m_cmd_list->empty ())
+    {
+      octave::tree_statement *last_stmt = m_cmd_list->back ();
+
+      octave::tree_command *cmd = last_stmt->command ();
+
+      octave::tree_no_op_command *no_op_cmd = dynamic_cast <octave::tree_no_op_command *> (cmd);
+
+      if (no_op_cmd && (no_op_cmd->is_end_of_fcn_or_script () || no_op_cmd->is_end_of_file ()))
+        no_op_cmd->attach_trailing_comments (lst);
+    }
+}
+
+octave::comment_list octave_user_function::trailing_comments () const
+{
+  if (m_cmd_list && ! m_cmd_list->empty ())
+    {
+      octave::tree_statement *last_stmt = m_cmd_list->back ();
+
+      octave::tree_command *cmd = last_stmt->command ();
+
+      octave::tree_no_op_command *no_op_cmd = dynamic_cast <octave::tree_no_op_command *> (cmd);
+
+      if (no_op_cmd && (no_op_cmd->is_end_of_fcn_or_script () || no_op_cmd->is_end_of_file ()))
+        return no_op_cmd->trailing_comments ();
+    }
+
+  return octave::comment_list ();
+}
+
 // If there is no explicit end statement at the end of the function,
 // relocate the no_op that was generated for the end of file condition
 // to appear on the next line after the last statement in the file, or
--- a/libinterp/octave-value/ov-usr-fcn.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/octave-value/ov-usr-fcn.h	Mon Apr 01 23:27:43 2024 -0400
@@ -35,6 +35,7 @@
 #include "ov-fcn.h"
 #include "ov-typeinfo.h"
 #include "symscope.h"
+#include "token.h"
 #include "unwind-prot.h"
 
 class string_vector;
@@ -223,6 +224,11 @@
 
   octave_user_function * define_ret_list (octave::tree_parameter_list *t);
 
+  void set_fcn_tok (const octave::token& fcn_tok) { m_fcn_tok = fcn_tok; }
+  void set_eq_tok (const octave::token& eq_tok) { m_eq_tok = eq_tok; }
+
+  void attach_trailing_comments (const octave::comment_list& lst);
+
   void stash_fcn_location (int line, int col)
   {
     m_location_line = line;
@@ -245,10 +251,6 @@
 
   void stash_parent_fcn_scope (const octave::symbol_scope& ps);
 
-  void stash_leading_comment (octave::comment_list *lc) { m_lead_comm = lc; }
-
-  void stash_trailing_comment (octave::comment_list *tc) { m_trail_comm = tc; }
-
   std::string profiler_name () const;
 
   std::string parent_fcn_name () const
@@ -385,9 +387,8 @@
 
   octave::tree_parameter_list * return_list () { return m_ret_list; }
 
-  octave::comment_list * leading_comment () { return m_lead_comm; }
-
-  octave::comment_list * trailing_comment () { return m_trail_comm; }
+  octave::comment_list leading_comments () const { return m_fcn_tok.leading_comments (); }
+  octave::comment_list trailing_comments () const;
 
   // If is_special_expr is true, retrieve the sigular expression that forms the
   // body.  May be null (even if is_special_expr is true).
@@ -418,11 +419,10 @@
   // this function.
   octave::tree_parameter_list *m_ret_list;
 
-  // The comments preceding the FUNCTION token.
-  octave::comment_list *m_lead_comm;
-
-  // The comments preceding the ENDFUNCTION token.
-  octave::comment_list *m_trail_comm;
+  // FIXME: Should we also be caching the final token (END or EOF) as
+  // m_end_tok?
+  octave::token m_fcn_tok;
+  octave::token m_eq_tok;
 
   // Location where this function was defined.
   int m_location_line;
--- a/libinterp/parse-tree/lex.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/lex.h	Mon Apr 01 23:27:43 2024 -0400
@@ -483,7 +483,7 @@
   std::string m_current_input_line;
 
   // The text of the current comment, used to gather comment lines
-  // before storing in m_comment_buf.
+  // before storing in m_comment_list.
   std::string m_comment_text;
 
   // The text of functions entered on the command line.
@@ -559,52 +559,8 @@
     bool m_eof;
   };
 
-  // Collect comment text.
-
-  class comment_buffer
-  {
-  public:
-
-    comment_buffer () : m_comment_list (nullptr) { }
-
-    OCTAVE_DISABLE_COPY_MOVE (comment_buffer)
-
-    ~comment_buffer () { delete m_comment_list; }
-
-    void append (const std::string& s, comment_elt::comment_type t, bool uses_hash_char)
-    {
-      if (! m_comment_list)
-        m_comment_list = new comment_list ();
-
-      m_comment_list->append (s, t, uses_hash_char);
-    }
-
-    // Caller is expected to delete the returned value.
-
-    comment_list * get_comment_list ()
-    {
-      comment_list *retval = m_comment_list;
-
-      m_comment_list = nullptr;
-
-      return retval;
-    }
-
-    void reset ()
-    {
-      delete m_comment_list;
-
-      m_comment_list = nullptr;
-    }
-
-  private:
-
-    comment_list *m_comment_list;
-  };
-
   base_lexer (interpreter& interp)
-    : lexical_feedback (interp), m_scanner (nullptr), m_input_buf (),
-      m_comment_buf ()
+    : lexical_feedback (interp), m_scanner (nullptr), m_input_buf ()
   {
     init ();
   }
@@ -661,7 +617,12 @@
 
   void finish_comment (comment_elt::comment_type typ);
 
-  comment_list * get_comment_list () { return m_comment_buf.get_comment_list (); }
+  comment_list get_comment_list ()
+  {
+    comment_list retval = m_comment_list;
+    m_comment_list.clear ();
+    return retval;
+  }
 
   int handle_close_bracket (int bracket_type);
 
@@ -717,8 +678,8 @@
   // Object that reads and buffers input.
   input_buffer m_input_buf;
 
-  // Object that collects comment text.
-  comment_buffer m_comment_buf;
+  // List of collected comments.
+  comment_list m_comment_list;
 
   virtual std::string input_source () const { return "unknown"; }
 
--- a/libinterp/parse-tree/lex.ll	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/lex.ll	Mon Apr 01 23:27:43 2024 -0400
@@ -991,7 +991,7 @@
         curr_lexer->m_looking_for_object_index = true;
         curr_lexer->m_at_beginning_of_statement = false;
 
-        octave::token *tok = new octave::token (DQ_STRING, curr_lexer->m_string_text, curr_lexer->m_tok_beg, curr_lexer->m_tok_end);
+        octave::token *tok = new octave::token (DQ_STRING, curr_lexer->m_string_text, curr_lexer->m_tok_beg, curr_lexer->m_tok_end, curr_lexer->get_comment_list ());
 
         curr_lexer->m_string_text = "";
 
@@ -1141,7 +1141,7 @@
         curr_lexer->m_looking_for_object_index = true;
         curr_lexer->m_at_beginning_of_statement = false;
 
-        octave::token *tok = new octave::token (SQ_STRING, curr_lexer->m_string_text, curr_lexer->m_tok_beg, curr_lexer->m_tok_end);
+        octave::token *tok = new octave::token (SQ_STRING, curr_lexer->m_string_text, curr_lexer->m_tok_beg, curr_lexer->m_tok_end, curr_lexer->get_comment_list ());
 
         curr_lexer->m_string_text = "";
 
@@ -1407,7 +1407,7 @@
 
                 curr_lexer->m_looking_for_object_index = true;
 
-                tok = new octave::token (FCN_HANDLE, ident, curr_lexer->m_tok_beg, curr_lexer->m_tok_end);
+                tok = new octave::token (FCN_HANDLE, ident, curr_lexer->m_tok_beg, curr_lexer->m_tok_end, curr_lexer->get_comment_list ());
 
                 return curr_lexer->handle_token (tok);
               }
@@ -2441,7 +2441,7 @@
 
     lexical_feedback::reset ();
 
-    m_comment_buf.reset ();
+    m_comment_list.clear ();
   }
 
   void
@@ -2479,7 +2479,7 @@
         syntax_error (msg);
       }
 
-    token *tok = new token (END_OF_INPUT, m_tok_beg, m_tok_end);
+    token *tok = new token (END_OF_INPUT, m_tok_beg, m_tok_end, get_comment_list ());
 
     return handle_token (tok);
   }
@@ -2644,47 +2644,47 @@
             return nullptr;
           }
 
-        tok = new token (kw->tok_id, token::simple_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::simple_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case end_try_catch_kw:
-        tok = new token (kw->tok_id, token::try_catch_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::try_catch_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case end_unwind_protect_kw:
-        tok = new token (kw->tok_id, token::unwind_protect_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::unwind_protect_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case endfor_kw:
-        tok = new token (kw->tok_id, token::for_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::for_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case endfunction_kw:
-        tok = new token (kw->tok_id, token::function_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::function_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case endif_kw:
-        tok = new token (kw->tok_id, token::if_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::if_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case endparfor_kw:
-        tok = new token (kw->tok_id, token::parfor_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::parfor_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case endswitch_kw:
-        tok = new token (kw->tok_id, token::switch_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::switch_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case endwhile_kw:
-        tok = new token (kw->tok_id, token::while_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::while_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
@@ -2692,33 +2692,33 @@
 #if defined (DISABLE_ARGUMENTS_VALIDATION_BLOCK)
         return nullptr;
 #else
-        tok = new token (kw->tok_id, token::arguments_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::arguments_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 #endif
 
       case endclassdef_kw:
-        tok = new token (kw->tok_id, token::classdef_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::classdef_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case endenumeration_kw:
-        tok = new token (kw->tok_id, token::enumeration_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::enumeration_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case endevents_kw:
-        tok = new token (kw->tok_id, token::events_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::events_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case endmethods_kw:
-        tok = new token (kw->tok_id, token::methods_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::methods_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
       case endproperties_kw:
-        tok = new token (kw->tok_id, token::properties_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::properties_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
@@ -2821,7 +2821,7 @@
         break;
 
       case endspmd_kw:
-        tok = new token (kw->tok_id, token::spmd_end, m_tok_beg, m_tok_end);
+        tok = new token (kw->tok_id, token::spmd_end, m_tok_beg, m_tok_end, get_comment_list ());
         m_at_beginning_of_statement = true;
         break;
 
@@ -2830,9 +2830,9 @@
           if ((m_reading_fcn_file || m_reading_script_file
                || m_reading_classdef_file)
               && ! m_fcn_file_full_name.empty ())
-            tok = new token (kw->tok_id, m_fcn_file_full_name, m_tok_beg, m_tok_end);
+            tok = new token (kw->tok_id, m_fcn_file_full_name, m_tok_beg, m_tok_end, get_comment_list ());
           else
-            tok = new token (kw->tok_id, "stdin", m_tok_beg, m_tok_end);
+            tok = new token (kw->tok_id, "stdin", m_tok_beg, m_tok_end, get_comment_list ());
         }
         break;
 
@@ -2840,7 +2840,7 @@
         {
           int l = m_tok_beg.line ();
           octave_value ov_value (static_cast<double> (l));
-          tok = new token (kw->tok_id, ov_value, "", m_tok_beg, m_tok_end);
+          tok = new token (kw->tok_id, ov_value, "", m_tok_beg, m_tok_end, get_comment_list ());
         }
         break;
 
@@ -2849,7 +2849,7 @@
       }
 
     if (! tok)
-      tok = new token (kw->tok_id, true, m_tok_beg, m_tok_end);
+            tok = new token (kw->tok_id, true, m_tok_beg, m_tok_end, get_comment_list ());
 
     return tok;
   }
@@ -3036,7 +3036,7 @@
 
     update_token_positions (flex_yyleng ());
 
-    token *tok = new token (NUMBER, ov_value, yytxt, m_tok_beg, m_tok_end);
+    token *tok = new token (NUMBER, ov_value, yytxt, m_tok_beg, m_tok_end, get_comment_list ());
 
     return handle_token (tok);
   }
@@ -3158,7 +3158,7 @@
                   ? octave_value (Complex (0.0, value))
                   : octave_value (value));
 
-    token *tok = new token (NUMBER, ov_value, yytxt, m_tok_beg, m_tok_end);
+    token *tok = new token (NUMBER, ov_value, yytxt, m_tok_beg, m_tok_end, get_comment_list ());
 
     return handle_token (tok);
   }
@@ -3229,7 +3229,7 @@
 
     update_token_positions (flex_yyleng ());
 
-    token *tok = new token (NUMBER, ov_value, yytxt, m_tok_beg, m_tok_end);
+    token *tok = new token (NUMBER, ov_value, yytxt, m_tok_beg, m_tok_end, get_comment_list ());
 
     return handle_token (tok);
   }
@@ -3307,7 +3307,7 @@
     if (looks_like_copyright (m_comment_text))
       typ = comment_elt::copyright;
 
-    m_comment_buf.append (m_comment_text, typ, m_comment_uses_hash_char);
+    m_comment_list.append (m_comment_text, typ, m_comment_uses_hash_char);
 
     m_comment_text = "";
     m_comment_uses_hash_char = false;
@@ -3373,7 +3373,7 @@
         return syntax_error (msg);
       }
 
-    token *tok = new token (SUPERCLASSREF, meth, cls, m_tok_beg, m_tok_end);
+    token *tok = new token (SUPERCLASSREF, meth, cls, m_tok_beg, m_tok_end, get_comment_list ());
 
     m_filepos.increment_column (flex_yyleng ());
 
@@ -3388,7 +3388,7 @@
 
     m_looking_for_object_index = true;
 
-    token *tok = new token (METAQUERY, cls, m_tok_beg, m_tok_end);
+    token *tok = new token (METAQUERY, cls, m_tok_beg, m_tok_end, get_comment_list ());
 
     m_filepos.increment_column (flex_yyleng ());
 
@@ -3403,7 +3403,7 @@
 
     m_looking_for_object_index = true;
 
-    token *tok = new token (FQ_IDENT, ident, m_tok_beg, m_tok_end);
+    token *tok = new token (FQ_IDENT, ident, m_tok_beg, m_tok_end, get_comment_list ());
 
     m_filepos.increment_column (flex_yyleng ());
 
@@ -3427,7 +3427,7 @@
 
     if (m_looking_at_indirect_ref)
       {
-        token *tok = new token (STRUCT_ELT, ident, m_tok_beg, m_tok_end);
+        token *tok = new token (STRUCT_ELT, ident, m_tok_beg, m_tok_end, get_comment_list ());
 
         m_looking_for_object_index = true;
 
@@ -3452,7 +3452,7 @@
         return handle_token (tok);
       }
 
-    tok = new token (NAME, ident, m_tok_beg, m_tok_end);
+    tok = new token (NAME, ident, m_tok_beg, m_tok_end, get_comment_list ());
 
     // For compatibility with Matlab, the following symbols are
     // handled specially so that things like
@@ -3889,7 +3889,7 @@
 
     update_token_positions (flex_yyleng ());
 
-    token *tok = new token (tok_id, m_tok_beg, m_tok_end);
+    token *tok = new token (tok_id, m_tok_beg, m_tok_end, get_comment_list ());
 
     m_looking_for_object_index = false;
     m_at_beginning_of_statement = bos;
@@ -3923,7 +3923,7 @@
   int
   base_lexer::finish_command_arg ()
   {
-    token *tok = new token (SQ_STRING, m_string_text, m_tok_beg, m_tok_end);
+    token *tok = new token (SQ_STRING, m_string_text, m_tok_beg, m_tok_end, get_comment_list ());
 
     m_string_text = "";
     m_command_arg_paren_count = 0;
--- a/libinterp/parse-tree/oct-parse.yy	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/oct-parse.yy	Mon Apr 01 23:27:43 2024 -0400
@@ -141,9 +141,6 @@
   // The type of the basic tokens returned by the lexer.
   octave::token *tok;
 
-  // Comment strings that we need to deal with mid-rule.
-  octave::comment_list *comment_type;
-
   // Types for the nonterminals we generate.
   char punct_type;
   octave::tree *tree_type;
@@ -190,10 +187,10 @@
   octave::tree_classdef_property* tree_classdef_property_type;
   octave::tree_classdef_property_list* tree_classdef_property_list_type;
   octave::tree_classdef_properties_block* tree_classdef_properties_block_type;
-  octave::tree_classdef_methods_list* tree_classdef_methods_list_type;
+  octave::tree_classdef_method_list* tree_classdef_method_list_type;
   octave::tree_classdef_methods_block* tree_classdef_methods_block_type;
   octave::tree_classdef_event* tree_classdef_event_type;
-  octave::tree_classdef_events_list* tree_classdef_events_list_type;
+  octave::tree_classdef_event_list* tree_classdef_event_list_type;
   octave::tree_classdef_events_block* tree_classdef_events_block_type;
   octave::tree_classdef_enum* tree_classdef_enum_type;
   octave::tree_classdef_enum_list* tree_classdef_enum_list_type;
@@ -242,12 +239,11 @@
 // %token VARARGIN VARARGOUT
 
 // Nonterminals we construct.
-%type <dummy_type> indirect_ref_op
 %type <dummy_type> push_fcn_symtab push_script_symtab begin_file
-%type <dummy_type> param_list_beg param_list_end stmt_begin anon_fcn_begin
+%type <dummy_type> stmt_begin anon_fcn_begin
 %type <dummy_type> parsing_local_fcns parse_error at_first_executable_stmt
-%type <comment_type> stash_comment
-%type <tok> function_beg classdef_beg arguments_beg
+%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 <tree_type> input
@@ -275,8 +271,8 @@
 %type <tree_classdef_type> classdef
 %type <tree_command_type> file
 %type <tree_if_command_type> if_command
-%type <tree_if_clause_type> elseif_clause else_clause
-%type <tree_if_command_list_type> if_cmd_list1 if_cmd_list
+%type <tree_if_command_list_type> if_clause_list
+%type <tree_if_clause_type> if_clause elseif_clause else_clause
 %type <tree_switch_command_type> switch_command
 %type <tree_switch_case_type> switch_case default_case
 %type <tree_switch_case_list_type> case_list1 case_list
@@ -295,10 +291,10 @@
 %type <tree_classdef_property_type> class_property
 %type <tree_classdef_property_list_type> property_list property_list1
 %type <tree_classdef_properties_block_type> properties_block
-%type <tree_classdef_methods_list_type> methods_list methods_list1
+%type <tree_classdef_method_list_type> method_list method_list1
 %type <tree_classdef_methods_block_type> methods_block
 %type <tree_classdef_event_type> class_event
-%type <tree_classdef_events_list_type> events_list events_list1
+%type <tree_classdef_event_list_type> event_list event_list1
 %type <tree_classdef_events_block_type> events_block
 %type <tree_classdef_enum_type> class_enum
 %type <tree_classdef_enum_list_type> enum_list enum_list1
@@ -311,7 +307,6 @@
 %type <tree_arg_size_spec_type> size_spec
 %type <tree_identifier_type> class_name
 %type <tree_arg_validation_fcns_type> validation_fcns
-%type <tree_expression_type> default_value
 %type <octave_user_function_type> method_decl1
 
 // Precedence and associativity.
@@ -338,7 +333,6 @@
 
 %destructor { } <tok>
 %destructor { } <punct_type>
-%destructor { } <comment_type>
 %destructor { } <>
 
 %destructor { delete $$; } <tree_type>
@@ -384,10 +378,10 @@
 %destructor { delete $$; } <tree_classdef_property_type>
 %destructor { delete $$; } <tree_classdef_property_list_type>
 %destructor { delete $$; } <tree_classdef_properties_block_type>
-%destructor { delete $$; } <tree_classdef_methods_list_type>
+%destructor { delete $$; } <tree_classdef_method_list_type>
 %destructor { delete $$; } <tree_classdef_methods_block_type>
 %destructor { delete $$; } <tree_classdef_event_type>
-%destructor { delete $$; } <tree_classdef_events_list_type>
+%destructor { delete $$; } <tree_classdef_event_list_type>
 %destructor { delete $$; } <tree_classdef_events_block_type>
 %destructor { delete $$; } <tree_classdef_enum_type>
 %destructor { delete $$; } <tree_classdef_enum_list_type>
@@ -523,7 +517,7 @@
 
 word_list_cmd   : identifier word_list
                   {
-                    $$ = parser.make_index_expression ($1, $2, '(');
+                    $$ = parser.make_index_expression ($1, nullptr, $2, nullptr, '(');
                     if (! $$)
                       {
                         // make_index_expression deleted $1 and $2.
@@ -536,7 +530,7 @@
 word_list       : string
                   { $$ = parser.make_argument_list ($1); }
                 | word_list string
-                  { $$ = parser.append_argument_list ($1, $2); }
+                  { $$ = parser.append_argument_list ($1, nullptr, $2); }
                 ;
 
 // ===========
@@ -569,30 +563,26 @@
                 ;
 
 matrix          : '[' matrix_rows ']'
-                  { $$ = parser.finish_matrix ($2, $1, $3); }
+                  { $$ = parser.finish_matrix ($1, $2, $3); }
                 ;
 
 matrix_rows     : cell_or_matrix_row
                   { $$ = parser.make_matrix ($1); }
                 | matrix_rows ';' cell_or_matrix_row
                   {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.append_matrix_row ($1, $3);
+                    $$ = parser.append_matrix_row ($1, $2, $3);
                   }
                 ;
 
 cell            : '{' cell_rows '}'
-                  { $$ = parser.finish_cell ($2, $1, $3); }
+                  { $$ = parser.finish_cell ($1, $2, $3); }
                 ;
 
 cell_rows       : cell_or_matrix_row
                   { $$ = parser.make_cell ($1); }
                 | cell_rows ';' cell_or_matrix_row
                   {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.append_cell_row ($1, $3);
+                    $$ = parser.append_cell_row ($1, $2, $3);
                   }
                 ;
 
@@ -685,9 +675,7 @@
                   { $$ = $1; }
                 | '(' expression ')'
                   {
-                    OCTAVE_YYUSE ($1, $3);
-
-                    $$ = $2->mark_in_parens ();
+                    $$ = $2->mark_in_delims (*($1), *($3));
                   }
                 ;
 
@@ -697,9 +685,7 @@
 
 magic_tilde     : '~'
                   {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = parser.make_black_hole ();
+                    $$ = parser.make_black_hole ($1);
                   }
                 ;
 
@@ -711,44 +697,35 @@
                   { $$ = parser.make_argument_list ($1); }
                 | arg_list ',' magic_colon
                   {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.append_argument_list ($1, $3);
+                    $$ = parser.append_argument_list ($1, $2, $3);
                   }
                 | arg_list ',' magic_tilde
                   {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.append_argument_list ($1, $3);
+                    $$ = parser.append_argument_list ($1, $2, $3);
                   }
                 | arg_list ',' expression
                   {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.append_argument_list ($1, $3);
+                    $$ = parser.append_argument_list ($1, $2, $3);
                   }
                 ;
 
 indirect_ref_op : '.'
                   {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = 0;
                     lexer.m_looking_at_indirect_ref = true;
+                    $$ = $1;
                   }
                 ;
 
 oper_expr       : primary_expr
                   { $$ = $1; }
                 | oper_expr PLUS_PLUS
-                  { $$ = parser.make_postfix_op (PLUS_PLUS, $1, $2); }
+                  { $$ = parser.make_postfix_op ($1, $2); }
                 | oper_expr MINUS_MINUS
-                  { $$ = parser.make_postfix_op (MINUS_MINUS, $1, $2); }
+                  { $$ = parser.make_postfix_op ($1, $2); }
                 | oper_expr '(' ')'
                   {
-                    OCTAVE_YYUSE ($2, $3);
-
-                    $$ = parser.make_index_expression ($1, nullptr, '(');
+                    $$ = parser.make_index_expression ($1, $2, nullptr, $3, '(');
+
                     if (! $$)
                       {
                         // make_index_expression deleted $1.
@@ -757,9 +734,8 @@
                   }
                 | oper_expr '(' arg_list ')'
                   {
-                    OCTAVE_YYUSE ($2, $4);
-
-                    $$ = parser.make_index_expression ($1, $3, '(');
+                    $$ = parser.make_index_expression ($1, $2, $3, $4, '(');
+
                     if (! $$)
                       {
                         // make_index_expression deleted $1 and $3.
@@ -768,9 +744,8 @@
                   }
                 | oper_expr '{' '}'
                   {
-                    OCTAVE_YYUSE ($2, $3);
-
-                    $$ = parser.make_index_expression ($1, nullptr, '{');
+                    $$ = parser.make_index_expression ($1, $2, nullptr, $3, '{');
+
                     if (! $$)
                       {
                         // make_index_expression deleted $1.
@@ -779,9 +754,8 @@
                   }
                 | oper_expr '{' arg_list '}'
                   {
-                    OCTAVE_YYUSE ($2, $4);
-
-                    $$ = parser.make_index_expression ($1, $3, '{');
+                    $$ = parser.make_index_expression ($1, $2, $3, $4, '{');
+
                     if (! $$)
                       {
                         // make_index_expression deleted $1 and $3.
@@ -789,62 +763,57 @@
                       }
                   }
                 | oper_expr HERMITIAN
-                  { $$ = parser.make_postfix_op (HERMITIAN, $1, $2); }
+                  { $$ = parser.make_postfix_op ($1, $2); }
                 | oper_expr TRANSPOSE
-                  { $$ = parser.make_postfix_op (TRANSPOSE, $1, $2); }
+                  { $$ = parser.make_postfix_op ($1, $2); }
                 | oper_expr indirect_ref_op STRUCT_ELT
-                  { $$ = parser.make_indirect_ref ($1, $3->text ()); }
+                  { $$ = parser.make_indirect_ref ($1, $2, $3); }
                 | oper_expr indirect_ref_op '(' expression ')'
-                  {
-                    OCTAVE_YYUSE ($3, $5);
-
-                    $$ = parser.make_indirect_ref ($1, $4);
-                  }
+                  { $$ = parser.make_indirect_ref ($1, $2, $3, $4, $5); }
                 | PLUS_PLUS oper_expr %prec UNARY
-                  { $$ = parser.make_prefix_op (PLUS_PLUS, $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 | MINUS_MINUS oper_expr %prec UNARY
-                  { $$ = parser.make_prefix_op (MINUS_MINUS, $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 | '~' oper_expr %prec UNARY
-                  { $$ = parser.make_prefix_op ('~', $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 | '!' oper_expr %prec UNARY
-                  { $$ = parser.make_prefix_op ('!', $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 | '+' oper_expr %prec UNARY
-                  { $$ = parser.make_prefix_op ('+', $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 | '-' oper_expr %prec UNARY
-                  { $$ = parser.make_prefix_op ('-', $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 | oper_expr POW power_expr
-                  { $$ = parser.make_binary_op (POW, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | oper_expr EPOW power_expr
-                  { $$ = parser.make_binary_op (EPOW, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | oper_expr '+' oper_expr
-                  { $$ = parser.make_binary_op ('+', $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | oper_expr '-' oper_expr
-                  { $$ = parser.make_binary_op ('-', $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | oper_expr '*' oper_expr
-                  { $$ = parser.make_binary_op ('*', $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | oper_expr '/' oper_expr
-                  { $$ = parser.make_binary_op ('/', $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | oper_expr EMUL oper_expr
-                  { $$ = parser.make_binary_op (EMUL, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | oper_expr EDIV oper_expr
-                  { $$ = parser.make_binary_op (EDIV, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | oper_expr LEFTDIV oper_expr
-                  { $$ = parser.make_binary_op (LEFTDIV, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | oper_expr ELEFTDIV oper_expr
-                  { $$ = parser.make_binary_op (ELEFTDIV, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 ;
 
 power_expr      : primary_expr
                   { $$ = $1; }
                 | power_expr PLUS_PLUS
-                  { $$ = parser.make_postfix_op (PLUS_PLUS, $1, $2); }
+                  { $$ = parser.make_postfix_op ($1, $2); }
                 | power_expr MINUS_MINUS
-                  { $$ = parser.make_postfix_op (MINUS_MINUS, $1, $2); }
+                  { $$ = parser.make_postfix_op ($1, $2); }
                 | power_expr '(' ')'
                   {
-                    OCTAVE_YYUSE ($2, $3);
-
-                    $$ = parser.make_index_expression ($1, nullptr, '(');
+                    $$ = parser.make_index_expression ($1, $2, nullptr, $3, '(');
+
                     if (! $$)
                       {
                         // make_index_expression deleted $1.
@@ -853,9 +822,8 @@
                   }
                 | power_expr '(' arg_list ')'
                   {
-                    OCTAVE_YYUSE ($2, $4);
-
-                    $$ = parser.make_index_expression ($1, $3, '(');
+                    $$ = parser.make_index_expression ($1, $2, $3, $4, '(');
+
                     if (! $$)
                       {
                         // make_index_expression deleted $1 and $3.
@@ -864,9 +832,8 @@
                   }
                 | power_expr '{' '}'
                   {
-                    OCTAVE_YYUSE ($2, $3);
-
-                    $$ = parser.make_index_expression ($1, nullptr, '{');
+                    $$ = parser.make_index_expression ($1, $2, nullptr, $3, '{');
+
                     if (! $$)
                       {
                         // make_index_expression deleted $1.
@@ -875,9 +842,8 @@
                   }
                 | power_expr '{' arg_list '}'
                   {
-                    OCTAVE_YYUSE ($2, $4);
-
-                    $$ = parser.make_index_expression ($1, $3, '{');
+                    $$ = parser.make_index_expression ($1, $2, $3, $4, '{');
+
                     if (! $$)
                       {
                         // make_index_expression deleted $1 and $3.
@@ -885,32 +851,26 @@
                       }
                   }
                 | power_expr indirect_ref_op STRUCT_ELT
-                  { $$ = parser.make_indirect_ref ($1, $3->text ()); }
+                  { $$ = parser.make_indirect_ref ($1, $2, $3); }
                 | power_expr indirect_ref_op '(' expression ')'
-                  {
-                    OCTAVE_YYUSE ($3, $5);
-
-                    $$ = parser.make_indirect_ref ($1, $4);
-                  }
+                  { $$ = parser.make_indirect_ref ($1, $2, $3, $4, $5); }
                 | PLUS_PLUS power_expr %prec POW
-                  { $$ = parser.make_prefix_op (PLUS_PLUS, $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 | MINUS_MINUS power_expr %prec POW
-                  { $$ = parser.make_prefix_op (MINUS_MINUS, $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 | '~' power_expr %prec POW
-                  { $$ = parser.make_prefix_op ('~', $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 | '!' power_expr %prec POW
-                  { $$ = parser.make_prefix_op ('!', $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 | '+' power_expr %prec POW
-                  { $$ = parser.make_prefix_op ('+', $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 | '-' power_expr %prec POW
-                  { $$ = parser.make_prefix_op ('-', $2, $1); }
+                  { $$ = parser.make_prefix_op ($1, $2); }
                 ;
 
 colon_expr      : oper_expr ':' oper_expr
                   {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.make_colon_expression ($1, $3);
+                    $$ = parser.make_colon_expression ($1, $2, $3);
 
                     if (! $$)
                       {
@@ -920,9 +880,7 @@
                   }
                 | oper_expr ':' oper_expr ':' oper_expr
                   {
-                    OCTAVE_YYUSE ($2, $4);
-
-                    $$ = parser.make_colon_expression ($1, $5, $3);
+                    $$ = parser.make_colon_expression ($1, $2, $3, $4, $5);
 
                     if (! $$)
                       {
@@ -937,25 +895,25 @@
                 | colon_expr
                   { $$ = $1; }
                 | simple_expr EXPR_LT simple_expr
-                  { $$ = parser.make_binary_op (EXPR_LT, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | simple_expr EXPR_LE simple_expr
-                  { $$ = parser.make_binary_op (EXPR_LE, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | simple_expr EXPR_EQ simple_expr
-                  { $$ = parser.make_binary_op (EXPR_EQ, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | simple_expr EXPR_GE simple_expr
-                  { $$ = parser.make_binary_op (EXPR_GE, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | simple_expr EXPR_GT simple_expr
-                  { $$ = parser.make_binary_op (EXPR_GT, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | simple_expr EXPR_NE simple_expr
-                  { $$ = parser.make_binary_op (EXPR_NE, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | simple_expr EXPR_AND simple_expr
-                  { $$ = parser.make_binary_op (EXPR_AND, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | simple_expr EXPR_OR simple_expr
-                  { $$ = parser.make_binary_op (EXPR_OR, $1, $2, $3); }
+                  { $$ = parser.make_binary_op ($1, $2, $3); }
                 | simple_expr EXPR_AND_AND simple_expr
-                  { $$ = parser.make_boolean_op (EXPR_AND_AND, $1, $2, $3); }
+                  { $$ = parser.make_boolean_op ($1, $2, $3); }
                 | simple_expr EXPR_OR_OR simple_expr
-                  { $$ = parser.make_boolean_op (EXPR_OR_OR, $1, $2, $3); }
+                  { $$ = parser.make_boolean_op ($1, $2, $3); }
                 ;
 
 assign_lhs      : simple_expr
@@ -973,31 +931,31 @@
                 ;
 
 assign_expr     : assign_lhs '=' expression
-                  { $$ = parser.make_assign_op ('=', $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs ADD_EQ expression
-                  { $$ = parser.make_assign_op (ADD_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs SUB_EQ expression
-                  { $$ = parser.make_assign_op (SUB_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs MUL_EQ expression
-                  { $$ = parser.make_assign_op (MUL_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs DIV_EQ expression
-                  { $$ = parser.make_assign_op (DIV_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs LEFTDIV_EQ expression
-                  { $$ = parser.make_assign_op (LEFTDIV_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs POW_EQ expression
-                  { $$ = parser.make_assign_op (POW_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs EMUL_EQ expression
-                  { $$ = parser.make_assign_op (EMUL_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs EDIV_EQ expression
-                  { $$ = parser.make_assign_op (EDIV_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs ELEFTDIV_EQ expression
-                  { $$ = parser.make_assign_op (ELEFTDIV_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs EPOW_EQ expression
-                  { $$ = parser.make_assign_op (EPOW_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs AND_EQ expression
-                  { $$ = parser.make_assign_op (AND_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 | assign_lhs OR_EQ expression
-                  { $$ = parser.make_assign_op (OR_EQ, $1, $2, $3); }
+                  { $$ = parser.make_assign_op ($1, $2, $3); }
                 ;
 
 expression      : simple_expr
@@ -1052,12 +1010,12 @@
 
 declaration     : GLOBAL decl_init_list
                   {
-                    $$ = parser.make_decl_command (GLOBAL, $1, $2);
+                    $$ = parser.make_decl_command ($1, $2);
                     lexer.m_looking_at_decl_list = false;
                   }
                 | PERSISTENT decl_init_list
                   {
-                    $$ = parser.make_decl_command (PERSISTENT, $1, $2);
+                    $$ = parser.make_decl_command ($1, $2);
                     lexer.m_looking_at_decl_list = false;
                   }
                 ;
@@ -1088,49 +1046,45 @@
 // If statement
 // ============
 
-if_command      : IF stash_comment if_cmd_list END
+if_command      : if_clause_list else_clause END
                   {
-                    if (! ($$ = parser.finish_if_command ($1, $3, $4, $2)))
+                    if (! ($$ = parser.finish_if_command ($1, $2, $3)))
                       {
-                        // finish_if_command deleted $3.
+                        // finish_if_command deleted $1 and $2.
                         YYABORT;
                       }
                   }
                 ;
 
-if_cmd_list     : if_cmd_list1
-                  { $$ = $1; }
-                | if_cmd_list1 else_clause
-                  { $$ = parser.append_if_clause ($1, $2); }
-                ;
-
-if_cmd_list1    : expression stmt_begin opt_sep opt_list
-                  {
-                    OCTAVE_YYUSE ($3);
-
-                    parser.maybe_convert_to_braindead_shortcircuit ($1);
-
-                    $$ = parser.start_if_command ($1, $4);
-                  }
-                | if_cmd_list1 elseif_clause
+if_clause_list  : if_clause
+                  { $$ = parser.start_if_command ($1); }
+                | if_clause_list elseif_clause
                   { $$ = parser.append_if_clause ($1, $2); }
                 ;
 
-elseif_clause   : ELSEIF stash_comment opt_sep expression stmt_begin opt_sep opt_list
+if_clause       : IF opt_sep expression stmt_begin opt_sep opt_list
                   {
-                    OCTAVE_YYUSE ($3, $6);
-
-                    parser.maybe_convert_to_braindead_shortcircuit ($4);
-
-                    $$ = parser.make_elseif_clause ($1, $4, $7, $2);
+                    OCTAVE_YYUSE ($2, $5);
+
+                    $$ = parser.make_if_clause ($1, $3, $6);
                   }
                 ;
 
-else_clause     : ELSE stash_comment opt_sep opt_list
+elseif_clause   : ELSEIF opt_sep expression stmt_begin opt_sep opt_list
                   {
-                    OCTAVE_YYUSE ($3);
-
-                    $$ = parser.make_else_clause ($1, $2, $4);
+                    OCTAVE_YYUSE ($2, $5);
+
+                    $$ = parser.make_if_clause ($1, $3, $6);
+                  }
+                ;
+
+else_clause     : // empty
+                  { $$ = nullptr; }
+                | ELSE opt_sep opt_list
+                  {
+                    OCTAVE_YYUSE ($2);
+
+                    $$ = parser.make_if_clause ($1, nullptr, $3);
                   }
                 ;
 
@@ -1138,13 +1092,13 @@
 // Switch statement
 // ================
 
-switch_command  : SWITCH stash_comment expression opt_sep case_list END
+switch_command  : SWITCH expression opt_sep case_list END
                   {
-                    OCTAVE_YYUSE ($4);
-
-                    if (! ($$ = parser.finish_switch_command ($1, $3, $5, $6, $2)))
+                    OCTAVE_YYUSE ($3);
+
+                    if (! ($$ = parser.finish_switch_command ($1, $2, $4, $5)))
                       {
-                        // finish_switch_command deleted $3 adn $5.
+                        // finish_switch_command deleted $2 and $4.
                         YYABORT;
                       }
                   }
@@ -1166,19 +1120,19 @@
                   { $$ = parser.append_switch_case ($1, $2); }
                 ;
 
-switch_case     : CASE stash_comment opt_sep expression stmt_begin opt_sep opt_list
+switch_case     : CASE opt_sep expression stmt_begin opt_sep opt_list
                   {
-                    OCTAVE_YYUSE ($3, $6);
-
-                    $$ = parser.make_switch_case ($1, $4, $7, $2);
+                    OCTAVE_YYUSE ($2, $5);
+
+                    $$ = parser.make_switch_case ($1, $3, $6);
                   }
                 ;
 
-default_case    : OTHERWISE stash_comment opt_sep opt_list
+default_case    : OTHERWISE opt_sep opt_list
                   {
-                    OCTAVE_YYUSE ($3);
-
-                    $$ = parser.make_default_switch_case ($1, $2, $4);
+                    OCTAVE_YYUSE ($2);
+
+                    $$ = parser.make_default_switch_case ($1, $3);
                   }
                 ;
 
@@ -1186,65 +1140,61 @@
 // Looping
 // =======
 
-loop_command    : WHILE stash_comment expression stmt_begin opt_sep opt_list END
+loop_command    : WHILE expression stmt_begin opt_sep opt_list END
                   {
-                    OCTAVE_YYUSE ($5);
-
-                    parser.maybe_convert_to_braindead_shortcircuit ($3);
-
-                    if (! ($$ = parser.make_while_command ($1, $3, $6, $7, $2)))
+                    OCTAVE_YYUSE ($4);
+
+                    parser.maybe_convert_to_braindead_shortcircuit ($2);
+
+                    if (! ($$ = parser.make_while_command ($1, $2, $5, $6)))
                       {
-                        // make_while_command deleted $3 and $6.
+                        // make_while_command deleted $2 and $5.
                         YYABORT;
                       }
                   }
-                | DO stash_comment opt_sep opt_list UNTIL expression
+                | DO opt_sep opt_list UNTIL expression
                   {
-                    OCTAVE_YYUSE ($1, $3);
-
-                    $$ = parser.make_do_until_command ($5, $4, $6, $2);
+                    OCTAVE_YYUSE ($2);
+
+                    $$ = parser.make_do_until_command ($1, $3, $4, $5);
                   }
-                | FOR stash_comment assign_lhs '=' expression stmt_begin opt_sep opt_list END
+                | FOR assign_lhs '=' expression stmt_begin opt_sep opt_list END
                   {
-                    OCTAVE_YYUSE ($4, $7);
-
-                    if (! ($$ = parser.make_for_command (FOR, $1, $3, $5,
-                                                         nullptr, $8, $9, $2)))
+                    OCTAVE_YYUSE ($6);
+
+                    if (! ($$ = parser.make_for_command ($1, nullptr, $2, $3, $4, nullptr, nullptr, nullptr, $7, $8)))
+                      {
+                        // make_for_command deleted $2, $4, and $7.
+                        YYABORT;
+                      }
+                  }
+                | FOR '(' assign_lhs '=' expression ')' opt_sep opt_list END
+                  {
+                    OCTAVE_YYUSE ($2, $4, $6, $7);
+
+                    if (! ($$ = parser.make_for_command ($1, $2, $3, $4, $5, nullptr, nullptr, $6, $8, $9)))
                       {
                         // make_for_command deleted $3, $5, and $8.
                         YYABORT;
                       }
                   }
-                | FOR stash_comment '(' assign_lhs '=' expression ')' opt_sep opt_list END
+                | PARFOR assign_lhs '=' expression stmt_begin opt_sep opt_list END
                   {
-                    OCTAVE_YYUSE ($3, $5, $7, $8);
-
-                    if (! ($$ = parser.make_for_command (FOR, $1, $4, $6,
-                                                         nullptr, $9, $10, $2)))
+                    OCTAVE_YYUSE ($3, $6);
+
+                    if (! ($$ = parser.make_for_command ($1, nullptr, $2, $3, $4, nullptr, nullptr, nullptr, $7, $8)))
                       {
-                        // make_for_command deleted $4, $6, and $9.
+                        // make_for_command deleted $2, $4, and $7.
                         YYABORT;
                       }
                   }
-                | PARFOR stash_comment assign_lhs '=' expression stmt_begin opt_sep opt_list END
+                | PARFOR '(' assign_lhs '=' expression ',' expression ')' opt_sep opt_list END
                   {
-                    OCTAVE_YYUSE ($4, $7);
-
-                    if (! ($$ = parser.make_for_command (PARFOR, $1, $3, $5,
-                                                         nullptr, $8, $9, $2)))
+                    OCTAVE_YYUSE ($2, $4, $6, $8, $9);
+
+                    if (! ($$ = parser.make_for_command ($1, $2, $3, $4, $5, $6, $7, $8, $10, $11)))
                       {
-                        // make_for_command deleted $3, $5, and $8.
-                        YYABORT;
-                      }
-                  }
-                | PARFOR stash_comment '(' assign_lhs '=' expression ',' expression ')' opt_sep opt_list END
-                  {
-                    OCTAVE_YYUSE ($3, $5, $7, $9, $10);
-
-                    if (! ($$ = parser.make_for_command (PARFOR, $1, $4, $6,
-                                                         $8, $11, $12, $2)))
-                      {
-                        // make_for_command deleted $4, $6, $8, and $11.
+                        // make_for_command deleted $3, $5, $7, and $10.
                         YYABORT;
                       }
                   }
@@ -1272,16 +1222,13 @@
 // Parallel execution pool
 // =======================
 
-spmd_command    : SPMD stash_comment opt_sep opt_list END
+spmd_command    : SPMD opt_sep opt_list END
                   {
-                    OCTAVE_YYUSE ($3);
-
-                    octave::comment_list *lc = $2;
-                    octave::comment_list *tc = lexer.get_comment_list ();
-
-                    if (! ($$ = parser.make_spmd_command ($1, $4, $5, lc, tc)))
+                    OCTAVE_YYUSE ($2);
+
+                    if (! ($$ = parser.make_spmd_command ($1, $3, $4)))
                       {
-                        // make_spmd_command deleted $4, LC, and TC.
+                        // make_spmd_command deleted $3.
                         YYABORT;
                       }
                   }
@@ -1291,36 +1238,33 @@
 // Exceptions
 // ==========
 
-except_command  : UNWIND stash_comment opt_sep opt_list CLEANUP
-                  stash_comment opt_sep opt_list END
+except_command  : UNWIND opt_sep opt_list CLEANUP opt_sep opt_list END
                   {
-                    OCTAVE_YYUSE ($3, $5, $7);
-
-                    if (! ($$ = parser.make_unwind_command ($1, $4, $8, $9, $2, $6)))
+                    OCTAVE_YYUSE ($2, $5);
+
+                    if (! ($$ = parser.make_unwind_command ($1, $3, $4, $6, $7)))
                       {
-                        // make_unwind_command deleted $4 and $8.
+                        // make_unwind_command deleted $3 and $6.
                         YYABORT;
                       }
                   }
-                | TRY stash_comment opt_sep opt_list CATCH stash_comment
-                  opt_sep opt_list END
+                | TRY opt_sep opt_list CATCH opt_sep opt_list END
                   {
-                    OCTAVE_YYUSE ($3, $5, $7);
-
-                    if (! ($$ = parser.make_try_command ($1, $4, $7, $8, $9, $2, $6)))
+                    OCTAVE_YYUSE ($2);
+
+                    if (! ($$ = parser.make_try_command ($1, $3, $4, $5, $6, $7)))
                       {
-                        // make_try_command deleted $4 and $8.
+                        // make_try_command deleted $3 and $6.
                         YYABORT;
                       }
                   }
-                | TRY stash_comment opt_sep opt_list END
+                | TRY opt_sep opt_list END
                   {
-                    OCTAVE_YYUSE ($3);
-
-                    if (! ($$ = parser.make_try_command ($1, $4, 0, nullptr,
-                                                         $5, $2, nullptr)))
+                    OCTAVE_YYUSE ($2);
+
+                    if (! ($$ = parser.make_try_command ($1, $3, nullptr, 0, nullptr, $4)))
                       {
-                        // make_try_command deleted $4.
+                        // make_try_command deleted $3.
                         YYABORT;
                       }
                   }
@@ -1345,9 +1289,6 @@
 
 param_list_beg  : '('
                   {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = 0;
                     lexer.m_looking_at_parameter_list = true;
                     lexer.m_arguments_is_keyword = false;
 
@@ -1358,17 +1299,18 @@
                         lexer.m_looking_at_function_handle--;
                         lexer.m_looking_at_anon_fcn_args = true;
                       }
+
+                    $$ = $1;
                   }
                 ;
 
 param_list_end  : ')'
                   {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = 0;
                     lexer.m_looking_at_parameter_list = false;
                     lexer.m_arguments_is_keyword = true;
                     lexer.m_looking_for_object_index = false;
+
+                    $$ = $1;
                   }
                 ;
 
@@ -1383,11 +1325,14 @@
                     if ($2)
                       lexer.mark_as_variables ($2->variable_names ());
 
-                    $$ = $2;
+                    $$ = $2->mark_in_delims (*($1), *($3));
                   }
                 | param_list_beg error
                   {
+                    OCTAVE_YYUSE ($1);
+
                     $$ = nullptr;
+
                     parser.bison_error ("invalid parameter list");
                     YYABORT;
                   }
@@ -1416,9 +1361,7 @@
                   { $$ = parser.make_parameter_list (octave::tree_parameter_list::in, $1); }
                 | param_list2 ',' param_list_elt
                   {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.append_parameter_list ($1, $3);
+                    $$ = parser.append_parameter_list ($1, $2, $3);
                   }
                 ;
 
@@ -1434,18 +1377,17 @@
 
 return_list     : '[' ']'
                   {
-                    OCTAVE_YYUSE ($1, $2);
-
                     lexer.m_looking_at_return_list = false;
 
-                    $$ = parser.make_parameter_list (octave::tree_parameter_list::out);
+                    octave::tree_parameter_list *tmp = parser.make_parameter_list (octave::tree_parameter_list::out);
+
+                    $$ = tmp->mark_in_delims (*($1), *($2));
                   }
                 | identifier
                   {
                     lexer.m_looking_at_return_list = false;
 
-                    octave::tree_parameter_list *tmp
-                      = parser.make_parameter_list (octave::tree_parameter_list::out, $1);
+                    octave::tree_parameter_list *tmp = parser.make_parameter_list (octave::tree_parameter_list::out, $1);
 
                     // Even though this parameter list can contain only
                     // a single identifier, we still need to validate it
@@ -1461,15 +1403,13 @@
                   }
                 | '[' return_list1 ']'
                   {
-                    OCTAVE_YYUSE ($1, $3);
-
                     lexer.m_looking_at_return_list = false;
 
                     // Check for duplicate parameter names, varargin,
                     // or varargout.
 
                     if (parser.validate_param_list ($2, octave::tree_parameter_list::out))
-                      $$ = $2;
+                      $$ = $2->mark_in_delims (*($1), *($3));
                     else
                       {
                         delete $2;
@@ -1484,9 +1424,7 @@
                   }
                 | return_list1 ',' identifier
                   {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.append_parameter_list ($1, $3);
+                    $$ = parser.append_parameter_list ($1, $2, $3);
                   }
                 ;
 
@@ -1533,8 +1471,7 @@
                     else
                       {
                         octave::tree_statement *end_of_script
-                          = parser.make_end ("endscript", true,
-                                             $4->beg_pos (), $4->end_pos ());
+                          = parser.make_end ("endscript", true, $4, $4->beg_pos (), $4->end_pos ());
 
                         parser.make_script ($3, end_of_script);
                       }
@@ -1546,12 +1483,12 @@
                   }
                 | begin_file opt_nl classdef parsing_local_fcns opt_sep opt_fcn_list END_OF_INPUT
                   {
-                    OCTAVE_YYUSE ($2, $5, $7);
+                    OCTAVE_YYUSE ($2, $5);
 
                     // Unused symbol table context.
                     lexer.m_symtab_context.pop ();
 
-                    if (! parser.finish_classdef_file ($3, $6))
+                    if (! parser.finish_classdef_file ($3, $6, $7))
                       YYABORT;
 
                     $$ = nullptr;
@@ -1583,9 +1520,7 @@
                   }
                 | GET '.' identifier
                   {
-                    OCTAVE_YYUSE ($1, $2);
-
-                    $$ = $3;
+                    $$ = $3->mark_get_set (*($1), *($2));
 
                     lexer.m_parsed_function_name.top () = true;
                     lexer.m_maybe_classdef_get_set_method = false;
@@ -1594,9 +1529,7 @@
                   }
                 | SET '.' identifier
                   {
-                    OCTAVE_YYUSE ($1, $2);
-
-                    $$ = $3;
+                    $$ = $3->mark_get_set (*($1), *($2));
 
                     lexer.m_parsed_function_name.top () = true;
                     lexer.m_maybe_classdef_get_set_method = false;
@@ -1610,8 +1543,7 @@
                     parser.endfunction_found (true);
 
                     if (parser.end_token_ok ($1, octave::token::function_end))
-                      $$ = parser.make_end ("endfunction", false,
-                                            $1->beg_pos (), $1->end_pos ());
+                      $$ = parser.make_end ("endfunction", false, $1, $1->beg_pos (), $1->end_pos ());
                     else
                       {
                         parser.end_token_error ($1, octave::token::function_end);
@@ -1648,29 +1580,26 @@
                         YYABORT;
                       }
 
-                    $$ = parser.make_end ("endfunction", true,
-                                          $1->beg_pos (), $1->end_pos ());
+                    $$ = parser.make_end ("endfunction", true, $1, $1->beg_pos (), $1->end_pos ());
                   }
                 ;
 
-function        : function_beg stash_comment fcn_name opt_param_list opt_sep stash_comment function_body function_end
+function        : function_beg fcn_name opt_param_list opt_sep function_body function_end
                   {
-                    OCTAVE_YYUSE ($5);
-
-                    $$ = parser.make_function ($1, nullptr, $3, $4, $7, $8, $2, $6);
+                    OCTAVE_YYUSE ($4);
+
+                    $$ = parser.make_function ($1, nullptr, nullptr, $2, $3, $5, $6);
                   }
-                | function_beg stash_comment return_list '=' fcn_name opt_param_list opt_sep stash_comment function_body function_end
+                | function_beg return_list '=' fcn_name opt_param_list opt_sep function_body function_end
                   {
-                    OCTAVE_YYUSE ($4, $7);
-
-                    $$ = parser.make_function ($1, $3, $5, $6, $9, $10, $2, $8);
+                    OCTAVE_YYUSE ($6);
+
+                    $$ = parser.make_function ($1, $2, $3, $4, $5, $7, $8);
                   }
                 ;
 
 function_body   : at_first_executable_stmt opt_list
                   {
-                    OCTAVE_YYUSE ($1);
-
                     $$ = $2;
                   }
                 | function_body1 opt_sep at_first_executable_stmt opt_list
@@ -1684,7 +1613,6 @@
 at_first_executable_stmt
                 : // empty
                   {
-                    $$ = 0;
                     lexer.m_arguments_is_keyword = false;
                   }
                 ;
@@ -1703,17 +1631,13 @@
                   }
                 ;
 
-arguments_block : arguments_beg stash_comment opt_sep args_attr_list
-                  args_validation_list opt_sep END
+arguments_block : arguments_beg opt_sep args_attr_list args_validation_list opt_sep END
                   {
-                    OCTAVE_YYUSE ($3, $6);
-
-                    octave::comment_list *lc = $2;
-                    octave::comment_list *tc = lexer.get_comment_list ();
-
-                    if (! ($$ = parser.make_arguments_block ($1, $4, $5, $7, lc, tc)))
+                    OCTAVE_YYUSE ($2, $5);
+
+                    if (! ($$ = parser.make_arguments_block ($1, $3, $4, $6)))
                       {
-                        // make_arguments_block deleted $4, $5, LC, and TC.
+                        // make_arguments_block deleted $3, and $4.
                         YYABORT;
                       }
 
@@ -1732,7 +1656,7 @@
                   { $$ = nullptr; }
                 | '(' identifier ')'
                   {
-                    OCTAVE_YYUSE ($1, $3);
+                    $2->mark_in_delims (*($1), *($3));
 
                     // Error if $$ is nullptr.
                     if  (! ($$ = parser.make_args_attribute_list ($2)))
@@ -1766,21 +1690,29 @@
                     { $$ = $1; }
                   ;
 
-arg_validation    : size_spec class_name validation_fcns default_value
-                  {
-                    if (! ($$ = parser.make_arg_validation ($1, $2, $3, $4)))
-                      {
-                        // make_arg_validation deleted ...
-                        YYABORT;
-                      }
-                  }
+arg_validation    : size_spec class_name validation_fcns
+                    {
+                      if (! ($$ = parser.make_arg_validation ($1, $2, $3)))
+                        {
+                          // make_arg_validation deleted ...
+                          YYABORT;
+                        }
+                    }
+                  | size_spec class_name validation_fcns '=' expression
+                    {
+                      if (! ($$ = parser.make_arg_validation ($1, $2, $3, $4, $5)))
+                        {
+                          // make_arg_validation deleted ...
+                          YYABORT;
+                        }
+                    }
                 ;
 
 size_spec       : // empty
                   { $$ = nullptr; }
                 | '(' arg_list ')'
                   {
-                    OCTAVE_YYUSE ($1, $3);
+                    $2->mark_in_delims (*($1), *($3));
 
                     if (! ($$ = parser.make_arg_size_spec ($2)))
                       {
@@ -1801,7 +1733,7 @@
                   { $$ = nullptr; }
                 | '{' arg_list '}'
                   {
-                    OCTAVE_YYUSE ($1, $3);
+                    $2->mark_in_delims (*($1), *($3));
 
                     if (! ($$ = parser.make_arg_validation_fcns ($2)))
                       {
@@ -1811,16 +1743,6 @@
                   }
                 ;
 
-default_value   : // empty
-                  { $$ = nullptr; }
-                | '=' expression
-                  {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = $2;
-                  }
-                ;
-
 // ========
 // Classdef
 // ========
@@ -1843,15 +1765,15 @@
                   }
                 ;
 
-classdef        : classdef_beg stash_comment attr_list identifier opt_sep superclass_list stash_comment class_body stash_comment END
+classdef        : classdef_beg attr_list identifier opt_sep superclass_list class_body END
                   {
-                    OCTAVE_YYUSE ($5);
+                    OCTAVE_YYUSE ($4);
 
                     lexer.m_parsing_classdef = false;
 
-                    if (! ($$ = parser.make_classdef ($1, $3, $4, $6, $8, $10, $2, $7, $9)))
+                    if (! ($$ = parser.make_classdef ($1, $2, $3, $5, $6, $7)))
                       {
-                        // make_classdef deleted $2, $3, $4, $6, $7, $8, $9
+                        // make_classdef deleted $2, $3, $5, $6
                         YYABORT;
                       }
                   }
@@ -1861,9 +1783,9 @@
                   { $$ = nullptr; }
                 | '(' attr_list1 ')' opt_sep
                   {
-                    OCTAVE_YYUSE ($1, $3, $4);
-
-                    $$ = $2;
+                    OCTAVE_YYUSE ($4);
+
+                    $$ = $2->mark_in_delims (*($1), *($3));
                   }
                 ;
 
@@ -1871,9 +1793,7 @@
                   { $$ = parser.make_classdef_attribute_list ($1); }
                 | attr_list1 ',' attr
                   {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.append_classdef_attribute ($1, $3);
+                    $$ = parser.append_classdef_attribute ($1, $2, $3);
                   }
                 ;
 
@@ -1881,21 +1801,15 @@
                   { $$ = parser.make_classdef_attribute ($1); }
                 | identifier '=' expression
                   {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.make_classdef_attribute ($1, $3);
+                    $$ = parser.make_classdef_attribute ($1, $2, $3);
                   }
                 | '~' identifier
                   {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = parser.make_not_classdef_attribute ($2);
+                    $$ = parser.make_not_classdef_attribute ($1, $2);
                   }
                 | '!' identifier
                   {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = parser.make_not_classdef_attribute ($2);
+                    $$ = parser.make_not_classdef_attribute ($1, $2);
                   }
                 ;
 
@@ -1920,15 +1834,11 @@
 superclass_list1
                 : EXPR_LT superclass
                   {
-                    OCTAVE_YYUSE ($1);
-
-                    $$ = parser.make_classdef_superclass_list ($2);
+                    $$ = parser.make_classdef_superclass_list ($1, $2);
                   }
                 | superclass_list1 EXPR_AND superclass
                   {
-                    OCTAVE_YYUSE ($2);
-
-                    $$ = parser.append_classdef_superclass ($1, $3);
+                    $$ = parser.append_classdef_superclass ($1, $2, $3);
                   }
                 ;
 
@@ -1985,18 +1895,13 @@
                 ;
 
 properties_block
-                : properties_beg stash_comment opt_sep attr_list property_list END
+                : properties_beg opt_sep attr_list property_list END
                   {
-                    OCTAVE_YYUSE ($3);
-
-                    octave::comment_list *lc = $2;
-                    octave::comment_list *tc = lexer.get_comment_list ();
-
-                    if (! ($$ = parser.make_classdef_properties_block
-                           ($1, $4, $5, $6, lc, tc)))
+                    OCTAVE_YYUSE ($2);
+
+                    if (! ($$ = parser.make_classdef_properties_block ($1, $3, $4, $5)))
                       {
-                        // make_classdef_properties_block deleted $4,
-                        // $5, LC, and TC.
+                        // make_classdef_properties_block deleted $3 and $4.
                         YYABORT;
                       }
                   }
@@ -2035,18 +1940,18 @@
                     // identifier that becomes the next element in the
                     // list.  If the element at the end of the list
                     // doesn't have a doc string, see whether the
-                    // element we are adding is stroing an end-of-line
+                    // element we are adding is storing an end-of-line
                     // comment for us to use.
 
                     octave::tree_classdef_property *last_elt = $1->back ();
 
                     if (! last_elt->have_doc_string ())
                       {
-                        octave::comment_list *cl = $3->comments ();
-
-                        if (cl)
+                        octave::comment_list comments = $3->leading_comments ();
+
+                        if (! comments.empty ())
                           {
-                            octave::comment_elt elt = cl->front ();
+                            octave::comment_elt elt = comments.front ();
 
                             if (elt.is_end_of_line ())
                               last_elt->doc_string (elt.text ());
@@ -2057,22 +1962,17 @@
                   }
                 ;
 
-class_property  : stash_comment identifier arg_validation
-                  { $$ = parser.make_classdef_property ($1, $2, $3); }
+class_property  : identifier arg_validation
+                  { $$ = parser.make_classdef_property ($1, $2); }
                 ;
 
-methods_block   : methods_beg stash_comment opt_sep attr_list methods_list END
+methods_block   : methods_beg opt_sep attr_list method_list END
                   {
-                    OCTAVE_YYUSE ($3);
-
-                    octave::comment_list *lc = $2;
-                    octave::comment_list *tc = lexer.get_comment_list ();
-
-                    if (! ($$ = parser.make_classdef_methods_block
-                           ($1, $4, $5, $6, lc, tc)))
+                    OCTAVE_YYUSE ($2);
+
+                    if (! ($$ = parser.make_classdef_methods_block ($1, $3, $4, $5)))
                       {
-                        // make_classdef_methods_block deleted $4, $5,
-                        // LC, and TC.
+                        // make_classdef_methods_block deleted $3 and $4.
                         YYABORT;
                       }
                   }
@@ -2087,7 +1987,7 @@
 
 method_decl1    : identifier
                   {
-                    if (! ($$ = parser.start_classdef_external_method ($1, nullptr)))
+                    if (! ($$ = parser.start_classdef_external_method ($1)))
                       YYABORT;
                   }
                 | identifier param_list
@@ -2097,12 +1997,12 @@
                   }
                 ;
 
-method_decl     : stash_comment method_decl1
-                  { $$ = parser.finish_classdef_external_method ($2, nullptr, $1); }
-                | stash_comment return_list '='
+method_decl     : method_decl1
                   {
-                    OCTAVE_YYUSE ($3);
-
+                    $$ = parser.finish_classdef_external_method ($1);
+                  }
+                | return_list '='
+                  {
                     lexer.m_defining_fcn++;
                     lexer.m_parsed_function_name.push (false);
                   }
@@ -2111,7 +2011,7 @@
                     lexer.m_defining_fcn--;
                     lexer.m_parsed_function_name.pop ();
 
-                    $$ = parser.finish_classdef_external_method ($5, $2, $1);
+                    $$ = parser.finish_classdef_external_method ($4, $1, $2);
                   }
                 ;
 
@@ -2121,12 +2021,12 @@
                   { $$ = $1; }
                 ;
 
-methods_list    : // empty
+method_list     : // empty
                   {
                     lexer.m_classdef_element_names_are_keywords = true;
                     $$ = nullptr;
                   }
-                | methods_list1 opt_sep
+                | method_list1 opt_sep
                   {
                     OCTAVE_YYUSE ($2);
 
@@ -2135,9 +2035,9 @@
                   }
                 ;
 
-methods_list1   : method
-                  { $$ = parser.make_classdef_methods_list ($1); }
-                | methods_list1 opt_sep method
+method_list1    : method
+                  { $$ = parser.make_classdef_method_list ($1); }
+                | method_list1 opt_sep method
                   {
                     OCTAVE_YYUSE ($2);
 
@@ -2145,18 +2045,13 @@
                   }
                 ;
 
-events_block    : events_beg stash_comment opt_sep attr_list events_list END
+events_block    : events_beg opt_sep attr_list event_list END
                   {
-                    OCTAVE_YYUSE ($3);
-
-                    octave::comment_list *lc = $2;
-                    octave::comment_list *tc = lexer.get_comment_list ();
-
-                    if (! ($$ = parser.make_classdef_events_block
-                           ($1, $4, $5, $6, lc, tc)))
+                    OCTAVE_YYUSE ($2);
+
+                    if (! ($$ = parser.make_classdef_events_block ($1, $3, $4, $5)))
                       {
-                        // make_classdef_events_block deleted $4, $5,
-                        // LC, and TC.
+                        // make_classdef_events_block deleted $4 and $5.
                         YYABORT;
                       }
                   }
@@ -2169,12 +2064,12 @@
                   }
                 ;
 
-events_list     : // empty
+event_list      : // empty
                   {
                     lexer.m_classdef_element_names_are_keywords = true;
                     $$ = nullptr;
                   }
-                | events_list1 opt_sep
+                | event_list1 opt_sep
                   {
                     OCTAVE_YYUSE ($2);
 
@@ -2183,9 +2078,9 @@
                   }
                 ;
 
-events_list1    : class_event
-                  { $$ = parser.make_classdef_events_list ($1); }
-                | events_list1 opt_sep class_event
+event_list1     : class_event
+                  { $$ = parser.make_classdef_event_list ($1); }
+                | event_list1 opt_sep class_event
                   {
                     OCTAVE_YYUSE ($2);
 
@@ -2193,22 +2088,17 @@
                   }
                 ;
 
-class_event     : stash_comment identifier
-                  { $$ = parser.make_classdef_event ($1, $2); }
+class_event     : identifier
+                  { $$ = parser.make_classdef_event ($1); }
                 ;
 
-enum_block      : enumeration_beg stash_comment opt_sep attr_list enum_list END
+enum_block      : enumeration_beg opt_sep attr_list enum_list END
                   {
-                    OCTAVE_YYUSE ($3);
-
-                    octave::comment_list *lc = $2;
-                    octave::comment_list *tc = lexer.get_comment_list ();
-
-                    if (! ($$ = parser.make_classdef_enum_block
-                           ($1, $4, $5, $6, lc, tc)))
+                    OCTAVE_YYUSE ($2);
+
+                    if (! ($$ = parser.make_classdef_enum_block ($1, $3, $4, $5)))
                       {
-                        // make_classdef_enum_block deleted $4, $5, LC,
-                        // and TC.
+                        // make_classdef_enum_block deleted $3 and $4.
                         YYABORT;
                       }
                   }
@@ -2245,11 +2135,9 @@
                   }
                 ;
 
-class_enum      : stash_comment identifier '(' expression ')'
+class_enum      : identifier '(' expression ')'
                   {
-                    OCTAVE_YYUSE ($3, $5);
-
-                    $$ = parser.make_classdef_enum ($2, $4, $1);
+                    $$ = parser.make_classdef_enum ($1, $2, $3, $4);
                   }
                 ;
 
@@ -2272,12 +2160,6 @@
                   }
                 ;
 
-stash_comment   : // empty
-                  {
-                    $$ = lexer.get_comment_list ();
-                  }
-                ;
-
 parse_error     : LEXICAL_ERROR
                   {
                     $$ = 0;
@@ -2533,6 +2415,15 @@
     return list;
   }
 
+  template <typename LIST_T, typename ELT_T>
+  static LIST_T *
+  list_append (LIST_T *list, const token& /*sep_tok*/, ELT_T elt)
+  {
+    // FIXME XXX! need to capture SEP_TOK here
+    list->push_back (elt);
+    return list;
+  }
+
   std::size_t
   base_parser::parent_scope_info::size () const
   {
@@ -2886,9 +2777,9 @@
   }
 
   tree_black_hole *
-  base_parser::make_black_hole ()
-  {
-    return new tree_black_hole ();
+  base_parser::make_black_hole (token *tilde)
+  {
+    return new tree_black_hole (*tilde);
   }
 
   // Make a function handle.
@@ -2973,9 +2864,13 @@
   // Build a colon expression.
 
   tree_expression *
-  base_parser::make_colon_expression (tree_expression *base,
-                                      tree_expression *limit,
-                                      tree_expression *incr)
+  base_parser::make_colon_expression (tree_expression *base, token *colon_tok, tree_expression *limit)
+  {
+    return make_colon_expression (base, colon_tok, nullptr, nullptr, limit);
+  }
+
+  tree_expression *
+  base_parser::make_colon_expression (tree_expression *base, token *colon_1_tok, tree_expression *incr, token *colon_2_tok, tree_expression *limit)
   {
     tree_expression *retval = nullptr;
 
@@ -2991,8 +2886,10 @@
     int l = base->line ();
     int c = base->column ();
 
+    token tmp_colon_2_tok = colon_2_tok ? *colon_2_tok : token ();
+
     tree_colon_expression *expr
-      = new tree_colon_expression (base, limit, incr, l, c);
+      = new tree_colon_expression (base, *colon_1_tok, incr, tmp_colon_2_tok, limit, l, c);
 
     retval = expr;
 
@@ -3053,12 +2950,11 @@
   // Build a binary expression.
 
   tree_expression *
-  base_parser::make_binary_op (int op, tree_expression *op1,
-                               token *tok, tree_expression *op2)
+  base_parser::make_binary_op (tree_expression *op1, token *op_tok, tree_expression *op2)
   {
     octave_value::binary_op t = octave_value::unknown_binary_op;
 
-    switch (op)
+    switch (op_tok->token_id ())
       {
       case POW:
         t = octave_value::op_pow;
@@ -3137,8 +3033,8 @@
         break;
       }
 
-    int l = tok->line ();
-    int c = tok->column ();
+    int l = op_tok->line ();
+    int c = op_tok->column ();
 
     return maybe_compound_binary_expression (op1, op2, l, c, t);
   }
@@ -3181,12 +3077,11 @@
   // Build a boolean expression.
 
   tree_expression *
-  base_parser::make_boolean_op (int op, tree_expression *op1,
-                                token *tok, tree_expression *op2)
+  base_parser::make_boolean_op (tree_expression *op1, token *op_tok, tree_expression *op2)
   {
     tree_boolean_expression::type t;
 
-    switch (op)
+    switch (op_tok->token_id ())
       {
       case EXPR_AND_AND:
         t = tree_boolean_expression::bool_and;
@@ -3201,8 +3096,8 @@
         break;
       }
 
-    int l = tok->line ();
-    int c = tok->column ();
+    int l = op_tok->line ();
+    int c = op_tok->column ();
 
     return new tree_boolean_expression (op1, op2, l, c, t);
   }
@@ -3210,11 +3105,11 @@
   // Build a prefix expression.
 
   tree_expression *
-  base_parser::make_prefix_op (int op, tree_expression *op1, token *tok)
+  base_parser::make_prefix_op (token *op_tok, tree_expression *op1)
   {
     octave_value::unary_op t = octave_value::unknown_unary_op;
 
-    switch (op)
+    switch (op_tok->token_id ())
       {
       case '~':
       case '!':
@@ -3242,8 +3137,8 @@
         break;
       }
 
-    int l = tok->line ();
-    int c = tok->column ();
+    int l = op_tok->line ();
+    int c = op_tok->column ();
 
     return new tree_prefix_expression (op1, l, c, t);
   }
@@ -3251,11 +3146,11 @@
   // Build a postfix expression.
 
   tree_expression *
-  base_parser::make_postfix_op (int op, tree_expression *op1, token *tok)
+  base_parser::make_postfix_op (tree_expression *op1, token *op_tok)
   {
     octave_value::unary_op t = octave_value::unknown_unary_op;
 
-    switch (op)
+    switch (op_tok->token_id ())
       {
       case HERMITIAN:
         t = octave_value::op_hermitian;
@@ -3278,8 +3173,8 @@
         break;
       }
 
-    int l = tok->line ();
-    int c = tok->column ();
+    int l = op_tok->line ();
+    int c = op_tok->column ();
 
     return new tree_postfix_expression (op1, l, c, t);
   }
@@ -3287,24 +3182,16 @@
   // Build an unwind-protect command.
 
   tree_command *
-  base_parser::make_unwind_command (token *unwind_tok,
-                                    tree_statement_list *body,
-                                    tree_statement_list *cleanup_stmts,
-                                    token *end_tok,
-                                    comment_list *lc,
-                                    comment_list *mc)
+  base_parser::make_unwind_command (token *unwind_tok, tree_statement_list *body, token *cleanup_tok, tree_statement_list *cleanup_stmts, token *end_tok)
   {
     tree_command *retval = nullptr;
 
     if (end_token_ok (end_tok, token::unwind_protect_end))
       {
-        comment_list *tc = m_lexer.m_comment_buf.get_comment_list ();
-
         int l = unwind_tok->line ();
         int c = unwind_tok->column ();
 
-        retval = new tree_unwind_protect_command (body, cleanup_stmts,
-                                                  lc, mc, tc, l, c);
+        retval = new tree_unwind_protect_command (*unwind_tok, body, *cleanup_tok, cleanup_stmts, *end_tok, l, c);
       }
     else
       {
@@ -3320,25 +3207,20 @@
   // Build a try-catch command.
 
   tree_command *
-  base_parser::make_try_command (token *try_tok,
-                                 tree_statement_list *body,
-                                 char catch_sep,
-                                 tree_statement_list *cleanup_stmts,
-                                 token *end_tok,
-                                 comment_list *lc,
-                                 comment_list *mc)
+  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)
   {
     tree_command *retval = nullptr;
 
     if (end_token_ok (end_tok, token::try_catch_end))
       {
-        comment_list *tc = m_lexer.m_comment_buf.get_comment_list ();
-
         int l = try_tok->line ();
         int c = try_tok->column ();
 
         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 ())
           {
             tree_statement *stmt = cleanup_stmts->front ();
@@ -3359,8 +3241,9 @@
               }
           }
 
-        retval = new tree_try_catch_command (body, cleanup_stmts, id,
-                                             lc, mc, tc, l, c);
+        token tmp_catch_tok = catch_tok ? *catch_tok : token ();
+
+        retval = new tree_try_catch_command (*try_tok, body, tmp_catch_tok, id, cleanup_stmts, *end_tok, l, c);
       }
     else
       {
@@ -3376,11 +3259,7 @@
   // Build a while command.
 
   tree_command *
-  base_parser::make_while_command (token *while_tok,
-                                   tree_expression *expr,
-                                   tree_statement_list *body,
-                                   token *end_tok,
-                                   comment_list *lc)
+  base_parser::make_while_command (token *while_tok, tree_expression *expr, tree_statement_list *body, token *end_tok)
   {
     tree_command *retval = nullptr;
 
@@ -3388,14 +3267,12 @@
 
     if (end_token_ok (end_tok, token::while_end))
       {
-        comment_list *tc = m_lexer.m_comment_buf.get_comment_list ();
-
         m_lexer.m_looping--;
 
         int l = while_tok->line ();
         int c = while_tok->column ();
 
-        retval = new tree_while_command (expr, body, lc, tc, l, c);
+        retval = new tree_while_command (*while_tok, expr, body, *end_tok, l, c);
       }
     else
       {
@@ -3411,44 +3288,35 @@
   // Build a do-until command.
 
   tree_command *
-  base_parser::make_do_until_command (token *until_tok,
-                                      tree_statement_list *body,
-                                      tree_expression *expr,
-                                      comment_list *lc)
+  base_parser::make_do_until_command (token *do_tok, tree_statement_list *body, token *until_tok, tree_expression *expr)
   {
     maybe_warn_assign_as_truth_value (expr);
 
-    comment_list *tc = m_lexer.m_comment_buf.get_comment_list ();
-
     m_lexer.m_looping--;
 
     int l = until_tok->line ();
     int c = until_tok->column ();
 
-    return new tree_do_until_command (expr, body, lc, tc, l, c);
+    return new tree_do_until_command (*do_tok, body, *until_tok, expr, l, c);
   }
 
   // Build a for command.
 
   tree_command *
-  base_parser::make_for_command (int tok_id, token *for_tok,
-                                 tree_argument_list *lhs,
-                                 tree_expression *expr,
-                                 tree_expression *maxproc,
-                                 tree_statement_list *body,
-                                 token *end_tok,
-                                 comment_list *lc)
+  base_parser::make_for_command (token *for_tok, token *open_paren, tree_argument_list *lhs, token *eq_tok, tree_expression *expr, token *sep_tok, tree_expression *maxproc, token *close_paren, tree_statement_list *body, token *end_tok)
   {
     tree_command *retval = nullptr;
 
-    bool parfor = tok_id == PARFOR;
+    bool parfor = for_tok->token_id () == PARFOR;
+
+    token tmp_open_paren = open_paren ? *open_paren : token ();
+    token tmp_close_paren = close_paren ? *close_paren : token ();
+    token tmp_sep_tok = sep_tok ? *sep_tok : token ();
 
     if (end_token_ok (end_tok, parfor ? token::parfor_end : token::for_end))
       {
         expr->mark_as_for_cmd_expr ();
 
-        comment_list *tc = m_lexer.m_comment_buf.get_comment_list ();
-
         m_lexer.m_looping--;
 
         int l = for_tok->line ();
@@ -3460,8 +3328,7 @@
 
             m_lexer.mark_as_variable (tmp->name ());
 
-            retval = new tree_simple_for_command (parfor, tmp, expr, maxproc,
-                                                  body, lc, tc, l, c);
+            retval = new tree_simple_for_command (parfor, *for_tok, tmp_open_paren, tmp, *eq_tok, expr, tmp_sep_tok, maxproc, tmp_close_paren, body, *end_tok, l, c);
 
             delete lhs;
           }
@@ -3478,8 +3345,7 @@
           {
             m_lexer.mark_as_variables (lhs->variable_names ());
 
-            retval = new tree_complex_for_command (lhs, expr, body,
-                                                   lc, tc, l, c);
+            retval = new tree_complex_for_command (*for_tok, lhs, *eq_tok, expr, body, *end_tok, l, c);
           }
       }
     else
@@ -3543,9 +3409,7 @@
   // Build an spmd command.
 
   tree_spmd_command *
-  base_parser::make_spmd_command (token *spmd_tok, tree_statement_list *body,
-                                  token *end_tok, comment_list *lc,
-                                  comment_list *tc)
+  base_parser::make_spmd_command (token *spmd_tok, tree_statement_list *body, token *end_tok)
   {
     tree_spmd_command *retval = nullptr;
 
@@ -3554,13 +3418,11 @@
         int l = spmd_tok->line ();
         int c = spmd_tok->column ();
 
-        retval = new tree_spmd_command (body, lc, tc, l, c);
+        retval = new tree_spmd_command (body, l, c);
       }
     else
       {
         delete body;
-        delete lc;
-        delete tc;
 
         end_token_error (end_tok, token::spmd_end);
       }
@@ -3571,51 +3433,42 @@
   // Start an if command.
 
   tree_if_command_list *
-  base_parser::start_if_command (tree_expression *expr,
-                                 tree_statement_list *list)
-  {
-    maybe_warn_assign_as_truth_value (expr);
-
-    // Line and column will be set in finish_if_command.
-
-    tree_if_clause *t = new tree_if_clause (expr, list);
-
-    return new tree_if_command_list (t);
+  base_parser::start_if_command (tree_if_clause *clause)
+  {
+    return new tree_if_command_list (clause);
   }
 
   // Finish an if command.
 
   tree_if_command *
-  base_parser::finish_if_command (token *if_tok,
-                                  tree_if_command_list *list,
-                                  token *end_tok,
-                                  comment_list *lc)
+  base_parser::finish_if_command (tree_if_command_list *list, tree_if_clause *else_clause, token *end_tok)
   {
     tree_if_command *retval = nullptr;
 
     if (end_token_ok (end_tok, token::if_end))
       {
-        comment_list *tc = m_lexer.m_comment_buf.get_comment_list ();
-
-        int l = if_tok->line ();
-        int c = if_tok->column ();
-
-        if (list && ! list->empty ())
+        if (else_clause)
+          list_append (list, else_clause);
+
+        token if_tok = list->if_token ();
+
+        int l = if_tok.line ();
+        int c = if_tok.column ();
+
+        tree_if_clause *elt = list->front ();
+
+        if (elt)
           {
-            tree_if_clause *elt = list->front ();
-
-            if (elt)
-              {
-                elt->line (l);
-                elt->column (c);
-              }
+            elt->line (l);
+            elt->column (c);
           }
 
-        retval = new tree_if_command (list, lc, tc, l, c);
+        retval = new tree_if_command (if_tok, list, *end_tok, l, c);
       }
     else
       {
         delete list;
+        delete else_clause;
 
         end_token_error (end_tok, token::if_end);
       }
@@ -3623,35 +3476,26 @@
     return retval;
   }
 
-  // Build an elseif clause.
+  // Build an if, elseif, or else clause.
 
   tree_if_clause *
-  base_parser::make_elseif_clause (token *elseif_tok,
-                                   tree_expression *expr,
-                                   tree_statement_list *list,
-                                   comment_list *lc)
-  {
-    maybe_warn_assign_as_truth_value (expr);
-
-    int l = elseif_tok->line ();
-    int c = elseif_tok->column ();
-
-    return new tree_if_clause (expr, list, lc, l, c);
-  }
-
-  tree_if_clause *
-  base_parser::make_else_clause (token *else_tok, comment_list *lc,
-                                 tree_statement_list *list)
-  {
-    int l = else_tok->line ();
-    int c = else_tok->column ();
-
-    return new tree_if_clause (list, lc, l, c);
+  base_parser::make_if_clause (token *tok, tree_expression *expr, tree_statement_list *list)
+  {
+    if (expr)
+      {
+        maybe_warn_assign_as_truth_value (expr);
+
+        maybe_convert_to_braindead_shortcircuit (expr);
+      }
+
+    int l = tok->line ();
+    int c = tok->column ();
+
+    return new tree_if_clause (*tok, expr, list, l, c);
   }
 
   tree_if_command_list *
-  base_parser::append_if_clause (tree_if_command_list *list,
-                                 tree_if_clause *clause)
+  base_parser::append_if_clause (tree_if_command_list *list, tree_if_clause *clause)
   {
     return list_append (list, clause);
   }
@@ -3659,18 +3503,12 @@
   // Finish a switch command.
 
   tree_switch_command *
-  base_parser::finish_switch_command (token *switch_tok,
-                                      tree_expression *expr,
-                                      tree_switch_case_list *list,
-                                      token *end_tok,
-                                      comment_list *lc)
+  base_parser::finish_switch_command (token *switch_tok, tree_expression *expr, tree_switch_case_list *list, token *end_tok)
   {
     tree_switch_command *retval = nullptr;
 
     if (end_token_ok (end_tok, token::switch_end))
       {
-        comment_list *tc = m_lexer.m_comment_buf.get_comment_list ();
-
         int l = switch_tok->line ();
         int c = switch_tok->column ();
 
@@ -3685,7 +3523,7 @@
               }
           }
 
-        retval = new tree_switch_command (expr, list, lc, tc, l, c);
+        retval = new tree_switch_command (*switch_tok, expr, list, *end_tok, l, c);
       }
     else
       {
@@ -3707,27 +3545,23 @@
   // Build a switch case.
 
   tree_switch_case *
-  base_parser::make_switch_case (token *case_tok,
-                                 tree_expression *expr,
-                                 tree_statement_list *list,
-                                 comment_list *lc)
+  base_parser::make_switch_case (token *case_tok, tree_expression *expr, tree_statement_list *list)
   {
     maybe_warn_variable_switch_label (expr);
 
     int l = case_tok->line ();
     int c = case_tok->column ();
 
-    return new tree_switch_case (expr, list, lc, l, c);
+    return new tree_switch_case (*case_tok, expr, list, l, c);
   }
 
   tree_switch_case *
-  base_parser::make_default_switch_case (token *default_tok, comment_list *lc,
-                                         tree_statement_list *list)
+  base_parser::make_default_switch_case (token *default_tok, tree_statement_list *list)
   {
     int l = default_tok->line ();
     int c = default_tok->column ();
 
-    return new tree_switch_case (list, lc, l, c);
+    return new tree_switch_case (*default_tok, list, l, c);
   }
 
   tree_switch_case_list *
@@ -3740,12 +3574,11 @@
   // Build an assignment to a variable.
 
   tree_expression *
-  base_parser::make_assign_op (int op, tree_argument_list *lhs,
-                               token *eq_tok, tree_expression *rhs)
+  base_parser::make_assign_op (tree_argument_list *lhs, token *eq_tok, tree_expression *rhs)
   {
     octave_value::assign_op t = octave_value::unknown_assign_op;
 
-    switch (op)
+    switch (eq_tok->token_id ())
       {
       case '=':
         t = octave_value::op_asn_eq;
@@ -3894,12 +3727,9 @@
     // First non-copyright comment in classdef body, before first
     // properties, methods, etc. block.
 
-    tree_statement *first_stmt = cmds->front ();
-    comment_list *leading_comments = first_stmt->comment_text ();
-
-    std::string doc_string;
-    if (leading_comments)
-      doc_string = leading_comments->find_doc_string ();
+    comment_list leading_comments = cmds->leading_comments ();
+
+    std::string doc_string = leading_comments.find_doc_string ();
 
     octave_user_script *script
       = new octave_user_script (m_lexer.m_fcn_file_full_name,
@@ -3951,23 +3781,31 @@
   // for a large mess.  Maybe this could be a bit better organized?
 
   tree_function_def *
-  base_parser::make_function (token *fcn_tok,
-                              tree_parameter_list *ret_list,
-                              tree_identifier *id,
-                              tree_parameter_list *param_list,
-                              tree_statement_list *body,
-                              tree_statement *end_fcn_stmt,
-                              comment_list *lc, comment_list *bc)
+  base_parser::make_function (token *fcn_tok, tree_parameter_list *ret_list, token *eq_tok, tree_identifier *id, tree_parameter_list *param_list, tree_statement_list *body, tree_statement *end_fcn_stmt)
   {
     // First non-copyright comments found above and below function keyword.
     comment_elt leading_doc_comment;
     comment_elt body_doc_comment;
 
-    if (lc)
-      leading_doc_comment = lc->find_doc_comment ();
-
-    if (bc)
-      body_doc_comment = bc->find_doc_comment ();
+    comment_list lc = fcn_tok->leading_comments ();
+
+    if (! lc.empty ())
+      leading_doc_comment = lc.find_doc_comment ();
+
+    if (body)
+      {
+        comment_list bc = body->leading_comments ();
+
+        if (! bc.empty ())
+          body_doc_comment = bc.find_doc_comment ();
+      }
+    else if (end_fcn_stmt)
+      {
+        comment_list ec = end_fcn_stmt->leading_comments ();
+
+        if (! ec.empty ())
+          body_doc_comment = ec.find_doc_comment ();
+      }
 
     // Choose which comment to use for doc string.
 
@@ -4004,7 +3842,7 @@
     octave_user_function *tmp_fcn
       = start_function (id, param_list, body, end_fcn_stmt, doc_string);
 
-    tree_function_def *retval = finish_function (ret_list, tmp_fcn, lc, l, c);
+    tree_function_def *retval = finish_function (fcn_tok, ret_list, eq_tok, tmp_fcn, l, c);
 
     recover_from_parsing_function ();
 
@@ -4043,9 +3881,6 @@
       = new octave_user_function (m_lexer.m_symtab_context.curr_scope (),
                                   param_list, nullptr, body);
 
-    comment_list *tc = m_lexer.m_comment_buf.get_comment_list ();
-
-    fcn->stash_trailing_comment (tc);
     fcn->stash_fcn_end_location (end_fcn_stmt->line (),
                                  end_fcn_stmt->column ());
 
@@ -4146,20 +3981,17 @@
   }
 
   tree_statement *
-  base_parser::make_end (const std::string& type, bool eof,
+  base_parser::make_end (const std::string& type, bool eof, token *end_tok,
                          const filepos& beg_pos, const filepos& /*end_pos*/)
   {
     int l = beg_pos.line ();
     int c = beg_pos.column ();
 
-    return make_statement (new tree_no_op_command (type, eof, l, c));
+    return make_statement (new tree_no_op_command (type, eof, *end_tok, l, c));
   }
 
   tree_function_def *
-  base_parser::finish_function (tree_parameter_list *ret_list,
-                                octave_user_function *fcn,
-                                comment_list *lc,
-                                int l, int c)
+  base_parser::finish_function (token *fcn_tok, tree_parameter_list *ret_list, token *eq_tok, octave_user_function *fcn, int l, int c)
   {
     tree_function_def *retval = nullptr;
 
@@ -4170,6 +4002,11 @@
 
     if (fcn)
       {
+        fcn->set_fcn_tok (*fcn_tok);
+
+        if (eq_tok)
+          fcn->set_eq_tok (*eq_tok);
+
         std::string fcn_nm = fcn->name ();
         std::string file = fcn->fcn_file_name ();
 
@@ -4183,9 +4020,6 @@
         fcn_scope.cache_fcn_file_name (file);
         fcn_scope.cache_dir_name (m_lexer.m_dir_name);
 
-        if (lc)
-          fcn->stash_leading_comment (lc);
-
         fcn->define_ret_list (ret_list);
 
         if (m_curr_fcn_depth > 0 || m_parsing_subfunctions)
@@ -4267,11 +4101,7 @@
   }
 
   tree_arguments_block *
-  base_parser::make_arguments_block (token *arguments_tok,
-                                     tree_args_block_attribute_list *attr_list,
-                                     tree_args_block_validation_list *validation_list,
-                                     token *end_tok,
-                                     comment_list *lc, comment_list *tc)
+  base_parser::make_arguments_block (token *arguments_tok, tree_args_block_attribute_list *attr_list, tree_args_block_validation_list *validation_list, token *end_tok)
   {
     tree_arguments_block *retval = nullptr;
 
@@ -4288,9 +4118,6 @@
       {
         delete attr_list;
         delete validation_list;
-
-        delete lc;
-        delete tc;
       }
 
     return retval;
@@ -4300,13 +4127,15 @@
   base_parser::make_arg_validation (tree_arg_size_spec *size_spec,
                                     tree_identifier *class_name,
                                     tree_arg_validation_fcns *validation_fcns,
+                                    token *eq_tok,
                                     tree_expression *default_value)
   {
     // FIXME: Validate arguments and convert to more specific types
     // (std::string for arg_name and class_name, etc).
 
-    return new tree_arg_validation (size_spec, class_name,
-                                    validation_fcns, default_value);
+    token tmp_eq_tok = eq_tok ? *eq_tok : token ();
+
+    return new tree_arg_validation (size_spec, class_name, validation_fcns, tmp_eq_tok, default_value);
   }
 
   tree_args_block_attribute_list *
@@ -4371,17 +4200,8 @@
   // and methods, and adding to the list of known objects) and creates
   // a parse tree containing meta information about the class.
 
-  // LC contains comments appearing before the classdef keyword.
-  // TC contains comments appearing between the classdef elements
-  // and the final end token for the classdef block.
-
   tree_classdef *
-  base_parser::make_classdef (token *tok,
-                              tree_classdef_attribute_list *a,
-                              tree_identifier *id,
-                              tree_classdef_superclass_list *sc,
-                              tree_classdef_body *body, token *end_tok,
-                              comment_list *lc, comment_list *bc, comment_list *tc)
+  base_parser::make_classdef (token *cdef_tok, tree_classdef_attribute_list *a, tree_identifier *id, tree_classdef_superclass_list *sc, tree_classdef_body *body, token *end_tok)
   {
     tree_classdef *retval = nullptr;
 
@@ -4407,9 +4227,6 @@
         delete id;
         delete sc;
         delete body;
-        delete lc;
-        delete bc;
-        delete tc;
 
         bison_error ("invalid classdef definition, the class name must match the filename", l, c);
 
@@ -4418,29 +4235,13 @@
       {
         if (end_token_ok (end_tok, token::classdef_end))
           {
-            int l = tok->line ();
-            int c = tok->column ();
-
-            // First non-copyright comments found above and below
-            // function keyword are candidates for the documentation
-            // string.  Use the first one that is not empty.
-
-            std::string doc_string;
-
-            if (lc)
-              doc_string = lc->find_doc_string ();
-
-            if (doc_string.empty () && bc)
-              doc_string = bc->find_doc_string ();
+            int l = cdef_tok->line ();
+            int c = cdef_tok->column ();
 
             if (! body)
               body = new tree_classdef_body ();
 
-            // FIXME - pass body comment to tree_classdef constructor.
-
-            retval = new tree_classdef (m_lexer.m_symtab_context.curr_scope (),
-                                        doc_string, a, id, sc, body, lc, tc,
-                                        m_curr_package_name, full_name, l, c);
+            retval = new tree_classdef (m_lexer.m_symtab_context.curr_scope (), *cdef_tok, a, id, sc, body, *end_tok, m_curr_package_name, full_name, l, c);
           }
         else
           {
@@ -4448,9 +4249,6 @@
             delete id;
             delete sc;
             delete body;
-            delete lc;
-            delete bc;
-            delete tc;
 
             end_token_error (end_tok, token::switch_end);
           }
@@ -4459,22 +4257,8 @@
     return retval;
   }
 
-  // LC contains comments appearing before the properties keyword.
-  // If this properties block appears first in the list of classdef
-  // elements, this comment list will be used for the help text for the
-  // classdef block.
-
-  // TC contains comments appearing between the list of properties
-  // and the final end token for the properties block and may be used to
-  // find the doc string for the final property in the list.
-
   tree_classdef_properties_block *
-  base_parser::make_classdef_properties_block (token *tok,
-                                               tree_classdef_attribute_list *a,
-                                               tree_classdef_property_list *plist,
-                                               token *end_tok,
-                                               comment_list *lc,
-                                               comment_list *tc)
+  base_parser::make_classdef_properties_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_property_list *plist, token *end_tok)
   {
     tree_classdef_properties_block *retval = nullptr;
 
@@ -4486,37 +4270,34 @@
         if (plist)
           {
             // If the element at the end of the list doesn't have a doc
-            // string, see whether the first element of TC is an
-            // end-of-line comment for us to use.
-
-            if (tc)
+            // string, see whether the first element of the comments
+            // attached to the end token is an end-of-line comment for
+            // us to use.
+
+            tree_classdef_property *last_elt = plist->back ();
+
+            if (last_elt && ! last_elt->have_doc_string ())
               {
-                tree_classdef_property *last_elt = plist->back ();
-
-                if (! last_elt->have_doc_string ())
+                comment_list comments = end_tok->leading_comments ();
+
+                if (! comments.empty ())
                   {
-                    comment_elt first_comment_elt = tc->front ();
-
-                    if (first_comment_elt.is_end_of_line ())
-                      {
-                        std::string eol_comment = first_comment_elt.text ();
-
-                        last_elt->doc_string (eol_comment);
-                      }
+                    comment_elt elt = comments.front ();
+
+                    if (elt.is_end_of_line ())
+                      last_elt->doc_string (elt.text ());
                   }
               }
           }
         else
           plist = new tree_classdef_property_list ();
 
-        retval = new tree_classdef_properties_block (a, plist, lc, tc, l, c);
+        retval = new tree_classdef_properties_block (*tok, a, plist, *end_tok, l, c);
       }
     else
       {
         delete a;
         delete plist;
-        delete lc;
-        delete tc;
 
         end_token_error (end_tok, token::properties_end);
       }
@@ -4531,28 +4312,18 @@
   }
 
   tree_classdef_property *
-  base_parser::make_classdef_property (comment_list *lc, tree_identifier *id,
-                                       tree_arg_validation *av)
+  base_parser::make_classdef_property (tree_identifier *id, tree_arg_validation *av)
   {
     av->arg_name (id);
 
     if (av->size_spec () || av->class_name () || av->validation_fcns ())
       warning ("size, class, and validation function specifications are not yet supported for classdef properties; INCORRECT RESULTS ARE POSSIBLE!");
 
-    return new tree_classdef_property (av, lc);
-  }
-
-  // LC contains comments appearing before the methods keyword.
-  // If this methods block appears first in the list of classdef
-  // elements, this comment list will be used for the help text for the
-  // classdef block.
+    return new tree_classdef_property (av);
+  }
 
   tree_classdef_methods_block *
-  base_parser::make_classdef_methods_block (token *tok,
-                                            tree_classdef_attribute_list *a,
-                                            tree_classdef_methods_list *mlist,
-                                            token *end_tok, comment_list *lc,
-                                            comment_list *tc)
+  base_parser::make_classdef_methods_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_method_list *mlist, token *end_tok)
   {
     tree_classdef_methods_block *retval = nullptr;
 
@@ -4562,16 +4333,14 @@
         int c = tok->column ();
 
         if (! mlist)
-          mlist = new tree_classdef_methods_list ();
-
-        retval = new tree_classdef_methods_block (a, mlist, lc, tc, l, c);
+          mlist = new tree_classdef_method_list ();
+
+        retval = new tree_classdef_methods_block (*tok, a, mlist, *end_tok, l, c);
       }
     else
       {
         delete a;
         delete mlist;
-        delete lc;
-        delete tc;
 
         end_token_error (end_tok, token::methods_end);
       }
@@ -4579,22 +4348,8 @@
     return retval;
   }
 
-  // LC contains comments appearing before the events keyword.
-  // If this events block appears first in the list of classdef
-  // elements, this comment list will be used for the help text for the
-  // classdef block.
-
-  // TC contains comments appearing between the list of events and
-  // the final end token for the events block and may be used to find
-  // the doc string for the final event in the list.
-
   tree_classdef_events_block *
-  base_parser::make_classdef_events_block (token *tok,
-                                           tree_classdef_attribute_list *a,
-                                           tree_classdef_events_list *elist,
-                                           token *end_tok,
-                                           comment_list *lc,
-                                           comment_list *tc)
+  base_parser::make_classdef_events_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_event_list *elist, token *end_tok)
   {
     tree_classdef_events_block *retval = nullptr;
 
@@ -4604,16 +4359,14 @@
         int c = tok->column ();
 
         if (! elist)
-          elist = new tree_classdef_events_list ();
-
-        retval = new tree_classdef_events_block (a, elist, lc, tc, l, c);
+          elist = new tree_classdef_event_list ();
+
+        retval = new tree_classdef_events_block (*tok, a, elist, *end_tok, l, c);
       }
     else
       {
         delete a;
         delete elist;
-        delete lc;
-        delete tc;
 
         end_token_error (end_tok, token::events_end);
       }
@@ -4621,35 +4374,20 @@
     return retval;
   }
 
-  tree_classdef_events_list *
-  base_parser::make_classdef_events_list (tree_classdef_event *e)
-  {
-    return new tree_classdef_events_list (e);
+  tree_classdef_event_list *
+  base_parser::make_classdef_event_list (tree_classdef_event *e)
+  {
+    return new tree_classdef_event_list (e);
   }
 
   tree_classdef_event *
-  base_parser::make_classdef_event (comment_list *lc, tree_identifier *id)
-  {
-    return new tree_classdef_event (id, lc);
-  }
-
-  // LC contains comments appearing before the enumeration keyword.
-  // If this enumeration block appears first in the list of classdef
-  // elements, this comment list will be used for the help text for the
-  // classdef block.
-
-  // TC contains comments appearing between the list of
-  // enumerations and the final end token for the enumeration block and
-  // may be used to find the doc string for the final enumeration in the
-  // list.
+  base_parser::make_classdef_event (tree_identifier *id)
+  {
+    return new tree_classdef_event (id);
+  }
 
   tree_classdef_enum_block *
-  base_parser::make_classdef_enum_block (token *tok,
-                                         tree_classdef_attribute_list *a,
-                                         tree_classdef_enum_list *elist,
-                                         token *end_tok,
-                                         comment_list *lc,
-                                         comment_list *tc)
+  base_parser::make_classdef_enum_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_enum_list *elist, token *end_tok)
   {
     tree_classdef_enum_block *retval = nullptr;
 
@@ -4661,14 +4399,12 @@
         if (! elist)
           elist = new tree_classdef_enum_list ();
 
-        retval = new tree_classdef_enum_block (a, elist, lc, tc, l, c);
+        retval = new tree_classdef_enum_block (*tok, a, elist, *end_tok, l, c);
       }
     else
       {
         delete a;
         delete elist;
-        delete lc;
-        delete tc;
 
         end_token_error (end_tok, token::enumeration_end);
       }
@@ -4683,10 +4419,9 @@
   }
 
   tree_classdef_enum *
-  base_parser::make_classdef_enum (tree_identifier *id, tree_expression *expr,
-                                   comment_list *lc)
-  {
-    return new tree_classdef_enum (id, expr, lc);
+  base_parser::make_classdef_enum (tree_identifier *id, token *open_paren, tree_expression *expr, token *close_paren)
+  {
+    return new tree_classdef_enum (id, *open_paren, expr, *close_paren);
   }
 
   tree_classdef_property_list *
@@ -4696,8 +4431,8 @@
     return list_append (list, elt);
   }
 
-  tree_classdef_events_list *
-  base_parser::append_classdef_event (tree_classdef_events_list *list,
+  tree_classdef_event_list *
+  base_parser::append_classdef_event (tree_classdef_event_list *list,
                                       tree_classdef_event *elt)
   {
     return list_append (list, elt);
@@ -4711,21 +4446,24 @@
   }
 
   tree_classdef_superclass_list *
-  base_parser::make_classdef_superclass_list (tree_classdef_superclass *sc)
-  {
+  base_parser::make_classdef_superclass_list (token *lt_tok, tree_classdef_superclass *sc)
+  {
+    sc->set_separator (*lt_tok);
+
     return new tree_classdef_superclass_list (sc);
   }
 
   tree_classdef_superclass *
   base_parser::make_classdef_superclass (token *fqident)
   {
-    return new tree_classdef_superclass (fqident->text ());
+    return new tree_classdef_superclass (*fqident);
   }
 
   tree_classdef_superclass_list *
-  base_parser::append_classdef_superclass (tree_classdef_superclass_list *list,
-                                           tree_classdef_superclass *elt)
-  {
+  base_parser::append_classdef_superclass (tree_classdef_superclass_list *list, token *and_tok, tree_classdef_superclass *elt)
+  {
+    elt->set_separator (*and_tok);
+
     return list_append (list, elt);
   }
 
@@ -4736,25 +4474,29 @@
   }
 
   tree_classdef_attribute *
-  base_parser::make_classdef_attribute (tree_identifier *id,
-                                        tree_expression *expr)
+  base_parser::make_classdef_attribute (tree_identifier *id)
+  {
+    return make_classdef_attribute (id, nullptr, nullptr);
+  }
+
+  tree_classdef_attribute *
+  base_parser::make_classdef_attribute (tree_identifier *id, token *eq_tok, tree_expression *expr)
   {
     return (expr
-            ? new tree_classdef_attribute (id, expr)
+            ? new tree_classdef_attribute (id, *eq_tok, expr)
             : new tree_classdef_attribute (id));
   }
 
   tree_classdef_attribute *
-  base_parser::make_not_classdef_attribute (tree_identifier *id)
-  {
-    return new tree_classdef_attribute (id, false);
+  base_parser::make_not_classdef_attribute (token *not_tok, tree_identifier *id)
+  {
+    return new tree_classdef_attribute (*not_tok, id, false);
   }
 
   tree_classdef_attribute_list *
-  base_parser::append_classdef_attribute (tree_classdef_attribute_list *list,
-                                          tree_classdef_attribute *elt)
-  {
-    return list_append (list, elt);
+  base_parser::append_classdef_attribute (tree_classdef_attribute_list *list, token *sep_tok, tree_classdef_attribute *elt)
+  {
+    return list_append (list, *sep_tok, elt);
   }
 
   tree_classdef_body *
@@ -4859,17 +4601,15 @@
   }
 
   tree_function_def *
-  base_parser::finish_classdef_external_method (octave_user_function *fcn,
-                                                tree_parameter_list *ret_list,
-                                                comment_list *cl)
+  base_parser::finish_classdef_external_method (octave_user_function *fcn, tree_parameter_list *ret_list, token *eq_tok)
   {
     if (! ret_list)
       ret_list = new tree_parameter_list (tree_parameter_list::out);
 
     fcn->define_ret_list (ret_list);
 
-    if (cl)
-      fcn->stash_leading_comment (cl);
+    if (eq_tok)
+      fcn->set_eq_tok (*eq_tok);
 
     int l = fcn->beginning_line ();
     int c = fcn->beginning_column ();
@@ -4877,8 +4617,8 @@
     return new tree_function_def (fcn, l, c);
   }
 
-  tree_classdef_methods_list *
-  base_parser::make_classdef_methods_list (tree_function_def *fcn_def)
+  tree_classdef_method_list *
+  base_parser::make_classdef_method_list (tree_function_def *fcn_def)
   {
     octave_value fcn;
 
@@ -4887,11 +4627,11 @@
 
     delete fcn_def;
 
-    return new tree_classdef_methods_list (fcn);
-  }
-
-  tree_classdef_methods_list *
-  base_parser::append_classdef_method (tree_classdef_methods_list *list,
+    return new tree_classdef_method_list (fcn);
+  }
+
+  tree_classdef_method_list *
+  base_parser::append_classdef_method (tree_classdef_method_list *list,
                                        tree_function_def *fcn_def)
   {
     octave_value fcn;
@@ -4907,8 +4647,7 @@
   }
 
   bool
-  base_parser::finish_classdef_file (tree_classdef *cls,
-                                     tree_statement_list *local_fcns)
+  base_parser::finish_classdef_file (tree_classdef *cls, tree_statement_list *local_fcns, token *eof_tok)
   {
     parse_tree_validator validator;
 
@@ -4959,6 +4698,8 @@
             std::string nm = fcn->name ();
             std::string file = fcn->fcn_file_name ();
 
+            fcn->attach_trailing_comments (eof_tok->leading_comments ());
+
             symtab.install_local_function (nm, ov_fcn, file);
           }
 
@@ -4975,9 +4716,7 @@
   // Make an index expression.
 
   tree_index_expression *
-  base_parser::make_index_expression (tree_expression *expr,
-                                      tree_argument_list *args,
-                                      char type)
+  base_parser::make_index_expression (tree_expression *expr, token *open_delim, tree_argument_list *args, token *close_delim, char type)
   {
     tree_index_expression *retval = nullptr;
 
@@ -4996,14 +4735,17 @@
         if (! expr->is_postfix_indexed ())
           expr->set_postfix_index (type);
 
+        token tmp_open_delim = open_delim ? *open_delim : token ();
+        token tmp_close_delim = close_delim ? *close_delim : token ();
+
         if (expr->is_index_expression ())
           {
             retval = dynamic_cast<tree_index_expression *> (expr);
 
-            retval->append (args, type);
+            retval->append (tmp_open_delim, args, tmp_close_delim, type);
           }
         else
-          retval = new tree_index_expression (expr, args, l, c, type);
+          retval = new tree_index_expression (expr, tmp_open_delim, args, tmp_close_delim, l, c, type);
       }
 
     return retval;
@@ -5012,8 +4754,7 @@
   // Make an indirect reference expression.
 
   tree_index_expression *
-  base_parser::make_indirect_ref (tree_expression *expr,
-                                  const std::string& elt)
+  base_parser::make_indirect_ref (tree_expression *expr, token *dot_tok, token *struct_elt_tok)
   {
     tree_index_expression *retval = nullptr;
 
@@ -5027,10 +4768,10 @@
       {
         retval = dynamic_cast<tree_index_expression *> (expr);
 
-        retval->append (elt);
+        retval->append (*dot_tok, *struct_elt_tok);
       }
     else
-      retval = new tree_index_expression (expr, elt, l, c);
+      retval = new tree_index_expression (expr, *dot_tok, *struct_elt_tok, l, c);
 
     m_lexer.m_looking_at_indirect_ref = false;
 
@@ -5040,8 +4781,7 @@
   // Make an indirect reference expression with dynamic field name.
 
   tree_index_expression *
-  base_parser::make_indirect_ref (tree_expression *expr,
-                                  tree_expression *elt)
+  base_parser::make_indirect_ref (tree_expression *expr, token *dot_tok, token *open_paren, tree_expression *elt, token *close_paren)
   {
     tree_index_expression *retval = nullptr;
 
@@ -5055,10 +4795,10 @@
       {
         retval = dynamic_cast<tree_index_expression *> (expr);
 
-        retval->append (elt);
+        retval->append (*dot_tok, *open_paren, elt, *close_paren);
       }
     else
-      retval = new tree_index_expression (expr, elt, l, c);
+      retval = new tree_index_expression (expr, *dot_tok, *open_paren, elt, *close_paren, l, c);
 
     m_lexer.m_looking_at_indirect_ref = false;
 
@@ -5068,8 +4808,7 @@
   // Make a declaration command.
 
   tree_decl_command *
-  base_parser::make_decl_command (int tok_id, token *tok,
-                                  tree_decl_init_list *lst)
+  base_parser::make_decl_command (token *tok, tree_decl_init_list *lst)
   {
     tree_decl_command *retval = nullptr;
 
@@ -5079,7 +4818,7 @@
     if (lst)
       m_lexer.mark_as_variables (lst->variable_names ());
 
-    switch (tok_id)
+    switch (tok->token_id ())
       {
       case GLOBAL:
         {
@@ -5287,11 +5026,12 @@
   // Finish building an array_list.
 
   tree_expression *
-  base_parser::finish_array_list (tree_array_list *array_list,
-                                  token */*open_delim*/, token *close_delim)
+  base_parser::finish_array_list (token *open_delim, tree_array_list *array_list, token *close_delim)
   {
     tree_expression *retval = array_list;
 
+    array_list->mark_in_delims (*open_delim, *close_delim);
+
     array_list->set_location (close_delim->line (), close_delim->column ());
 
     if (array_list->all_elements_are_constant ())
@@ -5351,11 +5091,10 @@
   // Finish building a matrix list.
 
   tree_expression *
-  base_parser::finish_matrix (tree_matrix *m, token *open_delim,
-                              token *close_delim)
+  base_parser::finish_matrix (token *open_delim, tree_matrix *m, token *close_delim)
   {
     return (m
-            ? finish_array_list (m, open_delim, close_delim)
+            ? finish_array_list (open_delim, m, close_delim)
             : new tree_constant (octave_null_matrix::instance,
                                  close_delim->line (), close_delim->column ()));
   }
@@ -5367,22 +5106,21 @@
   }
 
   tree_matrix *
-  base_parser::append_matrix_row (tree_matrix *matrix, tree_argument_list *row)
+  base_parser::append_matrix_row (tree_matrix *matrix, token *sep_tok, tree_argument_list *row)
   {
     if (! matrix)
       return make_matrix (row);
 
-    return row ? list_append (matrix, row) : matrix;
+    return row ? list_append (matrix, *sep_tok, row) : matrix;
   }
 
   // Finish building a cell list.
 
   tree_expression *
-  base_parser::finish_cell (tree_cell *c, token *open_delim,
-                            token *close_delim)
+  base_parser::finish_cell (token *open_delim, tree_cell *c, token *close_delim)
   {
     return (c
-            ? finish_array_list (c, open_delim, close_delim)
+            ? finish_array_list (open_delim, c, close_delim)
             : new tree_constant (octave_value (Cell ()),
                                  close_delim->line (), close_delim->column ()));
   }
@@ -5394,29 +5132,20 @@
   }
 
   tree_cell *
-  base_parser::append_cell_row (tree_cell *cell, tree_argument_list *row)
+  base_parser::append_cell_row (tree_cell *cell, token *sep_tok, tree_argument_list *row)
   {
     if (! cell)
       return make_cell (row);
 
-    return row ? list_append (cell, row) : cell;
+    return row ? list_append (cell, *sep_tok, row) : cell;
   }
 
   tree_identifier *
   base_parser::make_identifier (token *ident)
   {
-    // Find the token in the symbol table.
     symbol_scope scope = m_lexer.m_symtab_context.curr_scope ();
 
-    std::string nm = ident->text ();
-
-    symbol_record sr = (scope ? scope.insert (nm) : symbol_record (nm));
-
-
-    int l = ident->line ();
-    int c = ident->column ();
-
-    return new tree_identifier (sr, l, c);
+    return new tree_identifier (scope, *ident);
   }
 
   tree_superclass_ref *
@@ -5484,9 +5213,7 @@
   tree_statement *
   base_parser::make_statement (T *arg)
   {
-    comment_list *comment = m_lexer.get_comment_list ();
-
-    return new tree_statement (arg, comment);
+    return new tree_statement (arg);
   }
 
   tree_statement_list *
@@ -5512,10 +5239,9 @@
   }
 
   tree_argument_list *
-  base_parser::append_argument_list (tree_argument_list *list,
-                                     tree_expression *expr)
-  {
-    return list_append (list, expr);
+  base_parser::append_argument_list (tree_argument_list *list, token *sep_tok, tree_expression *expr)
+  {
+    return list_append (list, *sep_tok, expr);
   }
 
   tree_parameter_list *
@@ -5525,31 +5251,27 @@
   }
 
   tree_parameter_list *
-  base_parser::make_parameter_list (tree_parameter_list::in_or_out io,
-                                    tree_decl_elt *t)
+  base_parser::make_parameter_list (tree_parameter_list::in_or_out io, tree_decl_elt *t)
   {
     return new tree_parameter_list (io, t);
   }
 
   tree_parameter_list *
-  base_parser::make_parameter_list (tree_parameter_list::in_or_out io,
-                                    tree_identifier *id)
+  base_parser::make_parameter_list (tree_parameter_list::in_or_out io, tree_identifier *id)
   {
     return new tree_parameter_list (io, id);
   }
 
   tree_parameter_list *
-  base_parser::append_parameter_list (tree_parameter_list *list,
-                                      tree_decl_elt *t)
-  {
-    return list_append (list, t);
+  base_parser::append_parameter_list (tree_parameter_list *list, token *sep_tok, tree_decl_elt *t)
+  {
+    return list_append (list, *sep_tok, t);
   }
 
   tree_parameter_list *
-  base_parser::append_parameter_list (tree_parameter_list *list,
-                                      tree_identifier *id)
-  {
-    return list_append (list, new tree_decl_elt (id));
+  base_parser::append_parameter_list (tree_parameter_list *list, token *sep_tok, tree_identifier *id)
+  {
+    return list_append (list, *sep_tok, new tree_decl_elt (id));
   }
 
   void
@@ -5949,8 +5671,7 @@
   void
   base_parser::maybe_warn_assign_as_truth_value (tree_expression *expr)
   {
-    if (expr->is_assignment_expression ()
-        && expr->paren_count () < 2)
+    if (expr->is_assignment_expression () && expr->delim_count () < 2)
       {
         if (m_lexer.m_fcn_file_full_name.empty ())
           warning_with_id
--- a/libinterp/parse-tree/parse.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/parse.h	Mon Apr 01 23:27:43 2024 -0400
@@ -48,7 +48,6 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
-class comment_list;
 class parse_exception;
 class tree;
 class tree_anon_fcn_handle;
@@ -67,9 +66,9 @@
 class tree_classdef_enum_block;
 class tree_classdef_enum_list;
 class tree_classdef_events_block;
-class tree_classdef_events_list;
+class tree_classdef_event_list;
 class tree_classdef_methods_block;
-class tree_classdef_methods_list;
+class tree_classdef_method_list;
 class tree_classdef_properties_block;
 class tree_classdef_property_list;
 class tree_classdef_superclass_list;
@@ -236,17 +235,17 @@
   // Build a constant.
   OCTINTERP_API tree_constant * make_constant (token *tok);
 
-  OCTINTERP_API tree_black_hole * make_black_hole ();
+  OCTINTERP_API tree_black_hole * make_black_hole (token *tilde);
 
   OCTINTERP_API tree_matrix * make_matrix (tree_argument_list *row);
 
   OCTINTERP_API tree_matrix *
-  append_matrix_row (tree_matrix *matrix, tree_argument_list *row);
+  append_matrix_row (tree_matrix *matrix, token *sep_tok, tree_argument_list *row);
 
   OCTINTERP_API tree_cell * make_cell (tree_argument_list *row);
 
   OCTINTERP_API tree_cell *
-  append_cell_row (tree_cell *cell, tree_argument_list *row);
+  append_cell_row (tree_cell *cell, token *sep_tok, tree_argument_list *row);
 
   // Build a function handle.
   OCTINTERP_API tree_fcn_handle * make_fcn_handle (token *tok);
@@ -258,13 +257,15 @@
 
   // Build a colon expression.
   OCTINTERP_API tree_expression *
-  make_colon_expression (tree_expression *base, tree_expression *limit,
-                         tree_expression *incr = nullptr);
+  make_colon_expression (tree_expression *base, token *colon_tok, tree_expression *limit);
+
+  // Build a colon expression.
+  OCTINTERP_API tree_expression *
+  make_colon_expression (tree_expression *base, token *colon_1_tok, tree_expression *incr, token *colon_2_tok, tree_expression *limit);
 
   // Build a binary expression.
   OCTINTERP_API tree_expression *
-  make_binary_op (int op, tree_expression *op1, token *tok,
-                  tree_expression *op2);
+  make_binary_op (tree_expression *op1, token *op_tok, tree_expression *op2);
 
   // Maybe convert EXPR to a braindead_shortcircuit expression.
   OCTINTERP_API void
@@ -272,47 +273,35 @@
 
   // Build a boolean expression.
   OCTINTERP_API tree_expression *
-  make_boolean_op (int op, tree_expression *op1, token *tok,
-                   tree_expression *op2);
+  make_boolean_op (tree_expression *op1, token *op_tok, tree_expression *op2);
 
   // Build a prefix expression.
   OCTINTERP_API tree_expression *
-  make_prefix_op (int op, tree_expression *op1, token *tok);
+  make_prefix_op (token *op_tok, tree_expression *op1);
 
   // Build a postfix expression.
   OCTINTERP_API tree_expression *
-  make_postfix_op (int op, tree_expression *op1, token *tok);
+  make_postfix_op (tree_expression *op1, token *op_tok);
 
   // Build an unwind-protect command.
   OCTINTERP_API tree_command *
-  make_unwind_command (token *unwind_tok, tree_statement_list *body,
-                       tree_statement_list *cleanup, token *end_tok,
-                       comment_list *lc, comment_list *mc);
+  make_unwind_command (token *unwind_tok, tree_statement_list *body, token *cleanup_tok, tree_statement_list *cleanup, token *end_tok);
 
   // Build a try-catch command.
   OCTINTERP_API tree_command *
-  make_try_command (token *try_tok, tree_statement_list *body,
-                    char catch_sep, tree_statement_list *cleanup,
-                    token *end_tok, comment_list *lc,
-                    comment_list *mc);
+  make_try_command (token *try_tok, tree_statement_list *body, token *catch_tok, char catch_sep, tree_statement_list *cleanup, token *end_tok);
 
   // Build a while command.
   OCTINTERP_API tree_command *
-  make_while_command (token *while_tok, tree_expression *expr,
-                      tree_statement_list *body, token *end_tok,
-                      comment_list *lc);
+  make_while_command (token *while_tok, tree_expression *expr, tree_statement_list *body, token *end_tok);
 
   // Build a do-until command.
   OCTINTERP_API tree_command *
-  make_do_until_command (token *until_tok, tree_statement_list *body,
-                         tree_expression *expr, comment_list *lc);
+  make_do_until_command (token *do_tok, tree_statement_list *body, token *until_tok, tree_expression *expr);
 
   // Build a for command.
   OCTINTERP_API tree_command *
-  make_for_command (int tok_id, token *for_tok, tree_argument_list *lhs,
-                    tree_expression *expr, tree_expression *maxproc,
-                    tree_statement_list *body, token *end_tok,
-                    comment_list *lc);
+  make_for_command (token *for_tok, token *open_paren, tree_argument_list *lhs, token *eq_tok, tree_expression *expr, token *sep_tok, tree_expression *maxproc, token *close_paren, tree_statement_list *body, token *end_tok);
 
   // Build a break command.
   OCTINTERP_API tree_command * make_break_command (token *break_tok);
@@ -326,55 +315,43 @@
   // Build an spmd command.
 
   OCTINTERP_API tree_spmd_command *
-  make_spmd_command (token *spmd_tok, tree_statement_list *body,
-                     token *end_tok, comment_list *lc, comment_list *tc);
+  make_spmd_command (token *spmd_tok, tree_statement_list *body, token *end_tok);
 
   // Start an if command.
   OCTINTERP_API tree_if_command_list *
-  start_if_command (tree_expression *expr, tree_statement_list *list);
+  start_if_command (tree_if_clause *clause);
 
   // Finish an if command.
   OCTINTERP_API tree_if_command *
-  finish_if_command (token *if_tok, tree_if_command_list *list,
-                     token *end_tok, comment_list *lc);
+  finish_if_command (tree_if_command_list *list, tree_if_clause *else_clause, token *end_tok);
 
   // Build an elseif clause.
   OCTINTERP_API tree_if_clause *
-  make_elseif_clause (token *elseif_tok, tree_expression *expr,
-                      tree_statement_list *list, comment_list *lc);
-
-  OCTINTERP_API tree_if_clause *
-  make_else_clause (token *else_tok, comment_list *lc,
-                    tree_statement_list *list);
+  make_if_clause (token *if_tok, 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);
 
   // Finish a switch command.
   OCTINTERP_API tree_switch_command *
-  finish_switch_command (token *switch_tok, tree_expression *expr,
-                         tree_switch_case_list *list, token *end_tok,
-                         comment_list *lc);
+  finish_switch_command (token *switch_tok, tree_expression *expr, tree_switch_case_list *list, token *end_tok);
 
   OCTINTERP_API tree_switch_case_list *
   make_switch_case_list (tree_switch_case *switch_case);
 
   // Build a switch case.
   OCTINTERP_API tree_switch_case *
-  make_switch_case (token *case_tok, tree_expression *expr,
-                    tree_statement_list *list, comment_list *lc);
+  make_switch_case (token *case_tok, tree_expression *expr, tree_statement_list *list);
 
   OCTINTERP_API tree_switch_case *
-  make_default_switch_case (token *default_tok, comment_list *lc,
-                            tree_statement_list *list);
+  make_default_switch_case (token *default_tok, tree_statement_list *list);
 
   OCTINTERP_API tree_switch_case_list *
   append_switch_case (tree_switch_case_list *list, tree_switch_case *elt);
 
   // Build an assignment to a variable.
   OCTINTERP_API tree_expression *
-  make_assign_op (int op, tree_argument_list *lhs, token *eq_tok,
-                  tree_expression *rhs);
+  make_assign_op (tree_argument_list *lhs, token *eq_tok, tree_expression *rhs);
 
   // Define a script.
   OCTINTERP_API void
@@ -386,10 +363,7 @@
 
   // Define a function.
   OCTINTERP_API tree_function_def *
-  make_function (token *fcn_tok, tree_parameter_list *ret_list,
-                 tree_identifier *id, tree_parameter_list *param_list,
-                 tree_statement_list *body, tree_statement *end_fcn_stmt,
-                 comment_list *lc, comment_list *bc);
+  make_function (token *fcn_tok, tree_parameter_list *ret_list, token *eq_tok, tree_identifier *id, tree_parameter_list *param_list, tree_statement_list *body, tree_statement *end_fcn_stmt);
 
   // Begin defining a function.
   OCTINTERP_API octave_user_function *
@@ -399,8 +373,7 @@
 
   // Create a no-op statement for end_function.
   OCTINTERP_API tree_statement *
-  make_end (const std::string& type, bool eof,
-            const filepos& beg_pos, const filepos& end_pos);
+  make_end (const std::string& type, bool eof, token *tok, const filepos& beg_pos, const filepos& end_pos);
 
   // Do most of the work for defining a function.
   OCTINTERP_API octave_user_function *
@@ -408,19 +381,14 @@
 
   // Finish defining a function.
   OCTINTERP_API tree_function_def *
-  finish_function (tree_parameter_list *ret_list,
-                   octave_user_function *fcn, comment_list *lc,
-                   int l, int c);
+  finish_function (token *fcn_tok, tree_parameter_list *ret_list, token *eq_tok, octave_user_function *fcn, int l, int c);
 
   OCTINTERP_API tree_statement_list *
   append_function_body (tree_statement_list *body, tree_statement_list *list);
 
   // Make an arguments validation block.
   OCTINTERP_API tree_arguments_block *
-  make_arguments_block (token *arguments_tok,
-                        tree_args_block_attribute_list *attr_list,
-                        tree_args_block_validation_list *validation_list,
-                        token *end_tok, comment_list *lc, comment_list *tc);
+  make_arguments_block (token *arguments_tok, tree_args_block_attribute_list *attr_list, tree_args_block_validation_list *validation_list, token *end_tok);
 
   OCTINTERP_API tree_args_block_attribute_list *
   make_args_attribute_list (tree_identifier *attribute_name);
@@ -430,7 +398,8 @@
   make_arg_validation (tree_arg_size_spec *size_spec,
                        tree_identifier *class_name,
                        tree_arg_validation_fcns *validation_fcns,
-                       tree_expression *default_value);
+                       token *eq_tok = nullptr,
+                       tree_expression *default_value = nullptr);
 
   // Make an argument validation list.
   OCTINTERP_API tree_args_block_validation_list *
@@ -454,94 +423,73 @@
   recover_from_parsing_function ();
 
   OCTINTERP_API tree_classdef *
-  make_classdef (token *tok, tree_classdef_attribute_list *a,
-                 tree_identifier *id, tree_classdef_superclass_list *sc,
-                 tree_classdef_body *body, token *end_tok,
-                 comment_list *lc, comment_list *bc, comment_list *tc);
+  make_classdef (token *tok, tree_classdef_attribute_list *a, tree_identifier *id, tree_classdef_superclass_list *sc, tree_classdef_body *body, token *end_tok);
 
   OCTINTERP_API tree_classdef_properties_block *
-  make_classdef_properties_block (token *tok,
-                                  tree_classdef_attribute_list *a,
-                                  tree_classdef_property_list *plist,
-                                  token *end_tok, comment_list *lc,
-                                  comment_list *tc);
+  make_classdef_properties_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_property_list *plist, token *end_tok);
 
   OCTINTERP_API tree_classdef_property_list *
   make_classdef_property_list (tree_classdef_property *prop);
 
   OCTINTERP_API tree_classdef_property *
-  make_classdef_property (comment_list *lc, tree_identifier *id,
-                          tree_arg_validation *av);
+  make_classdef_property (tree_identifier *id, tree_arg_validation *av);
 
   OCTINTERP_API tree_classdef_property_list *
   append_classdef_property (tree_classdef_property_list *list,
                             tree_classdef_property *elt);
 
   OCTINTERP_API tree_classdef_methods_block *
-  make_classdef_methods_block (token *tok,
-                               tree_classdef_attribute_list *a,
-                               tree_classdef_methods_list *mlist,
-                               token *end_tok, comment_list *lc,
-                               comment_list *tc);
+  make_classdef_methods_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_method_list *mlist, token *end_tok);
 
   OCTINTERP_API tree_classdef_events_block *
-  make_classdef_events_block (token *tok,
-                              tree_classdef_attribute_list *a,
-                              tree_classdef_events_list *elist,
-                              token *end_tok, comment_list *lc,
-                              comment_list *tc);
+  make_classdef_events_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_event_list *elist, token *end_tok);
 
-  OCTINTERP_API tree_classdef_events_list *
-  make_classdef_events_list (tree_classdef_event *e);
+  OCTINTERP_API tree_classdef_event_list *
+  make_classdef_event_list (tree_classdef_event *e);
 
   OCTINTERP_API tree_classdef_event *
-  make_classdef_event (comment_list *lc, tree_identifier *id);
+  make_classdef_event (tree_identifier *id);
 
-  OCTINTERP_API tree_classdef_events_list *
-  append_classdef_event (tree_classdef_events_list *list,
+  OCTINTERP_API tree_classdef_event_list *
+  append_classdef_event (tree_classdef_event_list *list,
                          tree_classdef_event *elt);
 
   OCTINTERP_API tree_classdef_enum_block *
-  make_classdef_enum_block (token *tok,
-                            tree_classdef_attribute_list *a,
-                            tree_classdef_enum_list *elist,
-                            token *end_tok, comment_list *lc,
-                            comment_list *tc);
+  make_classdef_enum_block (token *tok, tree_classdef_attribute_list *a, tree_classdef_enum_list *elist, token *end_tok);
 
   OCTINTERP_API tree_classdef_enum_list *
   make_classdef_enum_list (tree_classdef_enum *e);
 
   OCTINTERP_API tree_classdef_enum *
-  make_classdef_enum (tree_identifier *id, tree_expression *expr,
-                      comment_list *lc);
+  make_classdef_enum (tree_identifier *id, token *open_paren, tree_expression *expr, token *close_paren);
 
   OCTINTERP_API tree_classdef_enum_list *
   append_classdef_enum (tree_classdef_enum_list *list,
                         tree_classdef_enum *elt);
 
   OCTINTERP_API tree_classdef_superclass_list *
-  make_classdef_superclass_list (tree_classdef_superclass *sc);
+  make_classdef_superclass_list (token *lt_tok, tree_classdef_superclass *sc);
 
   OCTINTERP_API tree_classdef_superclass *
   make_classdef_superclass (token *fqident);
 
   OCTINTERP_API tree_classdef_superclass_list *
-  append_classdef_superclass (tree_classdef_superclass_list *list,
-                              tree_classdef_superclass *elt);
+  append_classdef_superclass (tree_classdef_superclass_list *list, token *and_tok, tree_classdef_superclass *elt);
 
   OCTINTERP_API tree_classdef_attribute_list *
   make_classdef_attribute_list (tree_classdef_attribute *attr);
 
   OCTINTERP_API tree_classdef_attribute *
-  make_classdef_attribute (tree_identifier *id,
-                           tree_expression *expr = nullptr);
+  make_classdef_attribute (tree_identifier *id);
 
   OCTINTERP_API tree_classdef_attribute *
-  make_not_classdef_attribute (tree_identifier *id);
+  make_classdef_attribute (tree_identifier *id, token *eq_tok, tree_expression *expr);
+
+  OCTINTERP_API tree_classdef_attribute *
+  make_not_classdef_attribute (token *not_tok, tree_identifier *id);
 
   OCTINTERP_API tree_classdef_attribute_list *
-  append_classdef_attribute (tree_classdef_attribute_list *list,
-                             tree_classdef_attribute *elt);
+  append_classdef_attribute (tree_classdef_attribute_list *list, token *sep_tok, tree_classdef_attribute *elt);
 
   OCTINTERP_API tree_classdef_body *
   make_classdef_body (tree_classdef_properties_block *pb);
@@ -572,41 +520,36 @@
                               tree_classdef_enum_block *block);
 
   OCTINTERP_API octave_user_function *
-  start_classdef_external_method (tree_identifier *id,
-                                  tree_parameter_list *pl);
+  start_classdef_external_method (tree_identifier *id, tree_parameter_list *pl = nullptr);
 
   OCTINTERP_API tree_function_def *
-  finish_classdef_external_method (octave_user_function *fcn,
-                                   tree_parameter_list *ret_list,
-                                   comment_list *cl);
+  finish_classdef_external_method (octave_user_function *fcn, tree_parameter_list *ret_list = nullptr, token *eq_tok = nullptr);
 
-  OCTINTERP_API tree_classdef_methods_list *
-  make_classdef_methods_list (tree_function_def *fcn_def);
+  OCTINTERP_API tree_classdef_method_list *
+  make_classdef_method_list (tree_function_def *fcn_def);
 
-  OCTINTERP_API tree_classdef_methods_list *
-  append_classdef_method (tree_classdef_methods_list *list,
+  OCTINTERP_API tree_classdef_method_list *
+  append_classdef_method (tree_classdef_method_list *list,
                           tree_function_def *fcn_def);
 
   OCTINTERP_API bool
-  finish_classdef_file (tree_classdef *cls,
-                        tree_statement_list *local_fcns);
+  finish_classdef_file (tree_classdef *cls, tree_statement_list *local_fcns, token *eof_tok);
 
   // Make an index expression.
   OCTINTERP_API tree_index_expression *
-  make_index_expression (tree_expression *expr,
-                         tree_argument_list *args, char type);
+  make_index_expression (tree_expression *expr, token *open_paren, tree_argument_list *args, token *close_paren, char type);
 
   // Make an indirect reference expression.
   OCTINTERP_API tree_index_expression *
-  make_indirect_ref (tree_expression *expr, const std::string&);
+  make_indirect_ref (tree_expression *expr, token *dot_tok, token *struct_elt_tok);
 
   // Make an indirect reference expression with dynamic field name.
   OCTINTERP_API tree_index_expression *
-  make_indirect_ref (tree_expression *expr, tree_expression *field);
+  make_indirect_ref (tree_expression *expr, token *dot_tok, token *open_paren, tree_expression *field, token *close_paren);
 
   // Make a declaration command.
   OCTINTERP_API tree_decl_command *
-  make_decl_command (int tok_id, token *tok, tree_decl_init_list *lst);
+  make_decl_command (token *tok, tree_decl_init_list *lst);
 
   OCTINTERP_API tree_decl_init_list *
   make_decl_init_list (tree_decl_elt *elt);
@@ -632,16 +575,15 @@
   // Finish building an array_list (common action for finish_matrix
   // and finish_cell).
   OCTINTERP_API tree_expression *
-  finish_array_list (tree_array_list *a, token *open_delim,
-                     token *close_delim);
+  finish_array_list (token *open_delim, tree_array_list *a, token *close_delim);
 
   // Finish building a matrix list.
   OCTINTERP_API tree_expression *
-  finish_matrix (tree_matrix *m, token *open_delim, token *close_delim);
+  finish_matrix (token *open_delim, tree_matrix *m, token *close_delim);
 
   // Finish building a cell list.
   OCTINTERP_API tree_expression *
-  finish_cell (tree_cell *c, token *open_delim, token *close_delim);
+  finish_cell (token *open_delim, tree_cell *c, token *close_delim);
 
   OCTINTERP_API tree_identifier *
   make_identifier (token *ident);
@@ -673,7 +615,7 @@
   make_argument_list (tree_expression *expr);
 
   OCTINTERP_API tree_argument_list *
-  append_argument_list (tree_argument_list *list, tree_expression *expr);
+  append_argument_list (tree_argument_list *list, token *sep_tok, tree_expression *expr);
 
   OCTINTERP_API tree_parameter_list *
   make_parameter_list (tree_parameter_list::in_or_out io);
@@ -682,14 +624,13 @@
   make_parameter_list (tree_parameter_list::in_or_out io, tree_decl_elt *t);
 
   OCTINTERP_API tree_parameter_list *
-  make_parameter_list (tree_parameter_list::in_or_out io,
-                       tree_identifier *id);
+  make_parameter_list (tree_parameter_list::in_or_out io, tree_identifier *id);
 
   OCTINTERP_API tree_parameter_list *
-  append_parameter_list (tree_parameter_list *list, tree_decl_elt *t);
+  append_parameter_list (tree_parameter_list *list, token *sep_tok, tree_decl_elt *t);
 
   OCTINTERP_API tree_parameter_list *
-  append_parameter_list (tree_parameter_list *list, tree_identifier *id);
+  append_parameter_list (tree_parameter_list *list, token *sep_tok, tree_identifier *id);
 
   // Don't allow parsing command syntax.  If the parser/lexer is
   // reset, this setting is also reset to the default (allow command
--- a/libinterp/parse-tree/pt-arg-list.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-arg-list.h	Mon Apr 01 23:27:43 2024 -0400
@@ -35,7 +35,9 @@
 
 #include "str-vec.h"
 
+#include "pt-delimiter-list.h"
 #include "pt-walk.h"
+#include "token.h"
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
@@ -64,6 +66,12 @@
 
   ~tree_argument_list ();
 
+  tree_argument_list * mark_in_delims (const token& open_delim, const token& close_delim)
+  {
+    m_delims.push (open_delim, close_delim);
+    return this;
+  }
+
   bool has_magic_tilde () const
   {
     return m_list_includes_magic_tilde;
@@ -108,6 +116,8 @@
   bool m_list_includes_magic_tilde;
 
   bool m_simple_assign_lhs;
+
+  tree_delimiter_list m_delims;
 };
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-args-block.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-args-block.h	Mon Apr 01 23:27:43 2024 -0400
@@ -41,6 +41,8 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
+class comment_list;
+
 class tree_arg_size_spec
 {
 public:
@@ -102,10 +104,11 @@
   tree_arg_validation (tree_arg_size_spec *size_spec,
                        tree_identifier *class_name,
                        tree_arg_validation_fcns *validation_fcns,
+                       const token& eq_tok,
                        tree_expression *default_value)
     : m_arg_name (nullptr), m_size_spec (size_spec),
       m_class_name (class_name), m_validation_fcns (validation_fcns),
-      m_default_value (default_value)
+      m_eq_tok (eq_tok), m_default_value (default_value)
   { }
 
   OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_arg_validation)
@@ -149,6 +152,7 @@
   tree_arg_size_spec *m_size_spec;
   tree_identifier *m_class_name;
   tree_arg_validation_fcns *m_validation_fcns;
+  token m_eq_tok;
   tree_expression *m_default_value;
 };
 
@@ -211,12 +215,8 @@
 {
 public:
 
-  tree_arguments_block (tree_args_block_attribute_list *attr_list,
-                        tree_args_block_validation_list *validation_list,
-                        int l = -1, int c = -1)
-    : tree_command (l, c), m_attr_list (attr_list),
-      m_validation_list (validation_list),
-      m_lead_comm (nullptr), m_trail_comm (nullptr)
+  tree_arguments_block (tree_args_block_attribute_list *attr_list, tree_args_block_validation_list *validation_list, int l = -1, int c = -1)
+    : tree_command (l, c), m_attr_list (attr_list), m_validation_list (validation_list)
   { }
 
   OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_arguments_block)
@@ -225,9 +225,6 @@
   {
     delete m_attr_list;
     delete m_validation_list;
-
-    delete m_lead_comm;
-    delete m_trail_comm;
   }
 
   tree_args_block_attribute_list * attribute_list ()
@@ -240,10 +237,6 @@
     return m_validation_list;
   }
 
-  comment_list * leading_comment () { return m_lead_comm; }
-
-  comment_list * trailing_comment () { return m_trail_comm; }
-
   void accept (tree_walker& tw)
   {
     tw.visit_arguments_block (*this);
@@ -254,12 +247,6 @@
   tree_args_block_attribute_list *m_attr_list;
 
   tree_args_block_validation_list *m_validation_list;
-
-  // Comment preceding ARGUMENTS token.
-  comment_list *m_lead_comm;
-
-  // Comment preceding ENDARGUMENTS token.
-  comment_list *m_trail_comm;
 };
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-assign.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-assign.h	Mon Apr 01 23:27:43 2024 -0400
@@ -34,9 +34,11 @@
 class octave_value;
 class octave_value_list;
 
+#include "comment-list.h"
 #include "ov.h"
 #include "pt-exp.h"
 #include "pt-walk.h"
+#include "token.h"
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
@@ -64,6 +66,8 @@
 
   ~tree_simple_assignment ();
 
+  comment_list leading_comments () const { return m_lhs->leading_comments (); }
+
   bool rvalue_ok () const { return true; }
 
   bool is_assignment_expression () const { return true; }
--- a/libinterp/parse-tree/pt-classdef.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-classdef.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -29,6 +29,7 @@
 
 #include <iostream>
 
+#include "comment-list.h"
 #include "ov.h"
 #include "ov-classdef.h"
 #include "pt-args-block.h"
@@ -121,33 +122,39 @@
 
 // Classdef property
 
-std::string
-check_for_doc_string (comment_list *comments)
+static std::string
+check_for_doc_string (const comment_list& comments)
 {
-  // If the comment list ends in a block comment or full-line comment,
-  // then it is the doc string for this property.
+  if (! comments.empty ())
+    {
+      // If the comment list ends in a block comment or full-line
+      // comment, then it is the doc string for this property.
 
-  if (comments)
-    {
-      comment_elt last_elt = comments->back ();
+      comment_elt last_elt = comments.back ();
 
-      if (last_elt.is_block () || last_elt.is_full_line ())
+      if (! last_elt.is_copyright ()
+          && (last_elt.is_block () || last_elt.is_full_line ()))
         return last_elt.text ();
     }
 
   return "";
 }
 
-tree_classdef_property::tree_classdef_property (tree_arg_validation *av,
-    comment_list *comments)
-  : m_av (av), m_comments (comments),
-    m_doc_string (check_for_doc_string (m_comments))
+tree_classdef_property::tree_classdef_property (tree_arg_validation *av)
+  : m_av (av), m_doc_string (check_for_doc_string (leading_comments ()))
 { }
 
 tree_classdef_property::~tree_classdef_property ()
 {
   delete m_av;
-  delete m_comments;
+}
+
+comment_list
+tree_classdef_property::leading_comments ()
+{
+  tree_identifier *id = ident ();
+
+  return id->leading_comments ();
 }
 
 tree_identifier *
@@ -178,21 +185,19 @@
 
 // Classdef properties_block
 
-// Classdef methods_list
+// Classdef method_list
 
 // Classdef methods_block
 
 // Classdef event
 
-tree_classdef_event::tree_classdef_event (tree_identifier *i,
-    comment_list *comments)
-  : m_id (i), m_comments (comments),
-    m_doc_string (check_for_doc_string (m_comments))
+tree_classdef_event::tree_classdef_event (tree_identifier *i)
+  : m_id (i)
 { }
 
-// Classdef events_list
+// Classdef event_list
 
-tree_classdef_events_list::~tree_classdef_events_list ()
+tree_classdef_event_list::~tree_classdef_event_list ()
 {
   while (! empty ())
     {
@@ -206,11 +211,8 @@
 
 // Classdef enum
 
-tree_classdef_enum::tree_classdef_enum (tree_identifier *i,
-                                        tree_expression *e,
-                                        comment_list *comments)
-  : m_id (i), m_expr (e), m_comments (comments),
-    m_doc_string (check_for_doc_string (m_comments))
+tree_classdef_enum::tree_classdef_enum (tree_identifier *i, const token& open_paren, tree_expression *e, const token& close_paren)
+  : m_id (i), m_open_paren (open_paren), m_expr (e), m_close_paren (close_paren)
 { }
 
 // Classdef enum_list
@@ -230,82 +232,55 @@
 // Classdef body
 
 tree_classdef_body::tree_classdef_body ()
-  : m_properties_lst (), m_methods_lst (), m_events_lst (), m_enum_lst ()
+  : m_property_lst (), m_method_lst (), m_event_lst (), m_enum_lst ()
 { }
 
 tree_classdef_body::tree_classdef_body (tree_classdef_properties_block *pb)
-  : m_properties_lst (), m_methods_lst (), m_events_lst (), m_enum_lst (),
-    m_doc_string (pb ? get_doc_string (pb->leading_comment ()) : "")
+  : m_property_lst (), m_method_lst (), m_event_lst (), m_enum_lst ()
 {
   append (pb);
 }
 
 tree_classdef_body::tree_classdef_body (tree_classdef_methods_block *mb)
-  : m_properties_lst (), m_methods_lst (), m_events_lst (), m_enum_lst (),
-    m_doc_string (mb ? get_doc_string (mb->leading_comment ()) : "")
+  : m_property_lst (), m_method_lst (), m_event_lst (), m_enum_lst ()
 {
   append (mb);
 }
 
 tree_classdef_body::tree_classdef_body (tree_classdef_events_block *evb)
-  : m_properties_lst (), m_methods_lst (), m_events_lst (), m_enum_lst (),
-    m_doc_string (evb ? get_doc_string (evb->leading_comment ()) : "")
+  : m_property_lst (), m_method_lst (), m_event_lst (), m_enum_lst ()
 {
   append (evb);
 }
 
 tree_classdef_body::tree_classdef_body (tree_classdef_enum_block *enb)
-  : m_properties_lst (), m_methods_lst (), m_events_lst (), m_enum_lst (),
-    m_doc_string (enb ? get_doc_string (enb->leading_comment ()) : "")
+  : m_property_lst (), m_method_lst (), m_event_lst (), m_enum_lst ()
 {
   append (enb);
 }
 
+comment_list
+tree_classdef_body::leading_comments () const
+{
+  if (! m_all_elements.empty ())
+    {
+      tree_base_classdef_block *element = m_all_elements.front ();
+
+      if (element)
+        return element->leading_comments ();
+    }
+
+  return comment_list ();
+}
+
 tree_classdef_body::~tree_classdef_body ()
 {
-  while (! m_properties_lst.empty ())
-    {
-      auto p = m_properties_lst.begin ();
-      delete *p;
-      m_properties_lst.erase (p);
-    }
-
-  while (! m_methods_lst.empty ())
+  while (! m_all_elements.empty ())
     {
-      auto p = m_methods_lst.begin ();
+      auto p = m_all_elements.begin ();
       delete *p;
-      m_methods_lst.erase (p);
-    }
-
-  while (! m_events_lst.empty ())
-    {
-      auto p = m_events_lst.begin ();
-      delete *p;
-      m_events_lst.erase (p);
+      m_all_elements.erase (p);
     }
-
-  while (! m_enum_lst.empty ())
-    {
-      auto p = m_enum_lst.begin ();
-      delete *p;
-      m_enum_lst.erase (p);
-    }
-}
-
-std::string
-tree_classdef_body::get_doc_string (comment_list *comments) const
-{
-  // Grab the first comment from the list and use it as the doc string
-  // for this classdef body.
-
-  if (comments)
-    {
-      comment_elt first_elt = comments->front ();
-
-      return first_elt.text ();
-    }
-
-  return "";
 }
 
 // Classdef
--- a/libinterp/parse-tree/pt-classdef.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-classdef.h	Mon Apr 01 23:27:43 2024 -0400
@@ -30,18 +30,19 @@
 
 class octave_value;
 
-#include "comment-list.h"
 #include "pt-cmd.h"
+#include "pt-delimiter-list.h"
 #include "pt-exp.h"
 #include "pt-walk.h"
 #include "pt-id.h"
+#include "token.h"
 
 #include <list>
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
+class coment_list;
 class interpreter;
-
 class tree_arg_validation;
 
 class tree_superclass_ref : public tree_expression
@@ -128,13 +129,16 @@
 {
 public:
 
-  tree_classdef_attribute (tree_identifier *i = nullptr,
-                           tree_expression *e = nullptr)
-    : m_id (i), m_expr (e), m_neg (false)
+  tree_classdef_attribute (tree_identifier *i)
+    : m_id (i)
   { }
 
-  tree_classdef_attribute (tree_identifier *i, bool b)
-    : m_id (i), m_expr (nullptr), m_neg (b)
+  tree_classdef_attribute (tree_identifier *i, const token eq_tok, tree_expression *e)
+    : m_id (i), m_eq_tok (eq_tok), m_expr (e)
+  { }
+
+  tree_classdef_attribute (const token& not_tok, tree_identifier *i, bool b)
+    : m_not_tok (not_tok), m_id (i), m_neg (b)
   { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_classdef_attribute)
@@ -158,9 +162,11 @@
 
 private:
 
+  token m_not_tok;
   tree_identifier *m_id;
-  tree_expression *m_expr;
-  bool m_neg;
+  token m_eq_tok;
+  tree_expression *m_expr {nullptr};
+  bool m_neg {false};
 };
 
 class tree_classdef_attribute_list : public std::list<tree_classdef_attribute *>
@@ -179,25 +185,37 @@
 
   ~tree_classdef_attribute_list ();
 
+  tree_classdef_attribute_list * mark_in_delims (const token& open_delim, token& close_delim)
+  {
+    m_delims.push (open_delim, close_delim);
+    return this;
+  }
+
   void accept (tree_walker& tw)
   {
     tw.visit_classdef_attribute_list (*this);
   }
+
+private:
+
+  tree_delimiter_list m_delims;
 };
 
 class tree_classdef_superclass
 {
 public:
 
-  tree_classdef_superclass (const std::string& cname)
-    : m_cls_name (cname)
+  tree_classdef_superclass (const token& fqident)
+    : m_fqident (fqident)
   { }
 
   OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_classdef_superclass)
 
   ~tree_classdef_superclass () = default;
 
-  std::string class_name () { return m_cls_name; }
+  void set_separator (const token& sep_tok) { m_sep_tok = sep_tok; }
+
+  std::string class_name () { return m_fqident.text (); }
 
   void accept (tree_walker& tw)
   {
@@ -206,7 +224,13 @@
 
 private:
 
-  std::string m_cls_name;
+  // The '<' or '&&' token introducing an element of a superclass list
+  // element.  Is there a better name for it?
+
+  token m_sep_tok;
+
+  // The fully-qualified identifier token for this superclass element.
+  token m_fqident;
 };
 
 class tree_classdef_superclass_list
@@ -235,76 +259,84 @@
   }
 };
 
-template <typename T>
-class tree_classdef_element : public tree
+class tree_base_classdef_block : public tree
 {
 public:
 
-  tree_classdef_element (tree_classdef_attribute_list *a, T *elt_list,
-                         comment_list *lc, comment_list *tc,
-                         int l = -1, int c = -1)
-    : tree (l, c), m_attr_list (a), m_elt_list (elt_list),
-      m_lead_comm (lc), m_trail_comm (tc)
+  tree_base_classdef_block (const token& block_tok, tree_classdef_attribute_list *a, const token& end_tok, int l = -1, int c = -1)
+    : tree (l, c), m_block_tok (block_tok), m_attr_list (a), m_end_tok (end_tok)
   { }
 
-  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_classdef_element)
+  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_base_classdef_block)
 
-  ~tree_classdef_element ()
+  ~tree_base_classdef_block ()
   {
     delete m_attr_list;
-    delete m_elt_list;
-    delete m_lead_comm;
-    delete m_trail_comm;
   }
 
+  comment_list leading_comments () const { return m_block_tok.leading_comments (); }
+
   tree_classdef_attribute_list * attribute_list () { return m_attr_list; }
 
-  T * element_list () { return m_elt_list; }
-
-  comment_list * leading_comment () { return m_lead_comm; }
-
-  comment_list * trailing_comment () { return m_trail_comm; }
-
   void accept (tree_walker&) { }
 
 private:
 
+  token m_block_tok;
+
   // List of attributes that apply to this class.
   tree_classdef_attribute_list *m_attr_list;
 
-  // The list of objects contained in this block.
-  T *m_elt_list;
+  token m_end_tok;
+};
+
+template <typename T>
+class tree_classdef_block : public tree_base_classdef_block
+{
+public:
+
+  tree_classdef_block (const token& block_tok, tree_classdef_attribute_list *a, T *elt_list, const token& end_tok, int l = -1, int c = -1)
+    : tree_base_classdef_block (block_tok, a, end_tok, l, c), m_elt_list (elt_list)
+  { }
+
+  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_classdef_block)
 
-  // Comments preceding the token marking the beginning of the block.
-  comment_list *m_lead_comm;
+  ~tree_classdef_block ()
+  {
+    delete m_elt_list;
+  }
+
+  T * element_list () { return m_elt_list; }
 
-  // Comments preceding the END token marking the end of the block.
-  comment_list *m_trail_comm;
+private:
+
+  T *m_elt_list;
 };
 
+// FIXME: should this class be derived from tree?
+
 class tree_classdef_property
 {
 public:
 
-  tree_classdef_property (tree_arg_validation *av,
-                          comment_list *comments = nullptr);
+  tree_classdef_property (tree_arg_validation *av);
 
   OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_classdef_property)
 
   ~tree_classdef_property ();
 
-  tree_identifier * ident ();
-
-  tree_expression * expression ();
+  comment_list leading_comments ();
 
-  comment_list * comments () const { return m_comments; }
-
-  void doc_string (const std::string& txt) { m_doc_string = txt; }
+  void doc_string (const std::string& s) { m_doc_string = s; }
 
   std::string doc_string () const { return m_doc_string; }
 
   bool have_doc_string () const { return ! m_doc_string.empty (); }
 
+  tree_identifier * ident ();
+
+  tree_expression * expression ();
+
   void accept (tree_walker& tw)
   {
     tw.visit_classdef_property (*this);
@@ -313,7 +345,7 @@
 private:
 
   tree_arg_validation *m_av;
-  comment_list *m_comments;
+
   std::string m_doc_string;
 };
 
@@ -338,65 +370,61 @@
   }
 };
 
-class tree_classdef_properties_block
-  : public tree_classdef_element<tree_classdef_property_list>
+class tree_classdef_properties_block : public tree_classdef_block<tree_classdef_property_list>
 {
 public:
 
-  tree_classdef_properties_block (tree_classdef_attribute_list *a,
-                                  tree_classdef_property_list *plist,
-                                  comment_list *lc, comment_list *tc,
-                                  int l = -1, int c = -1)
-    : tree_classdef_element<tree_classdef_property_list> (a, plist, lc, tc, l, c)
+  tree_classdef_properties_block (const token& block_tok, tree_classdef_attribute_list *a, tree_classdef_property_list *plist, const token& end_tok, int l = -1, int c = -1)
+    : tree_classdef_block<tree_classdef_property_list> (block_tok, a, plist, end_tok, l, c)
   { }
 
   OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_classdef_properties_block)
 
   ~tree_classdef_properties_block () = default;
 
+  tree_classdef_property_list * property_list () { return element_list (); }
+
   void accept (tree_walker& tw)
   {
     tw.visit_classdef_properties_block (*this);
   }
 };
 
-class tree_classdef_methods_list : public std::list<octave_value>
+class tree_classdef_method_list : public std::list<octave_value>
 {
 public:
 
-  tree_classdef_methods_list () { }
+  tree_classdef_method_list () { }
 
-  tree_classdef_methods_list (const octave_value& f) { push_back (f); }
+  tree_classdef_method_list (const octave_value& f) { push_back (f); }
 
-  tree_classdef_methods_list (const std::list<octave_value>& a)
+  tree_classdef_method_list (const std::list<octave_value>& a)
     : std::list<octave_value> (a) { }
 
-  OCTAVE_DISABLE_COPY_MOVE (tree_classdef_methods_list)
+  OCTAVE_DISABLE_COPY_MOVE (tree_classdef_method_list)
 
-  ~tree_classdef_methods_list () = default;
+  ~tree_classdef_method_list () = default;
 
   void accept (tree_walker& tw)
   {
-    tw.visit_classdef_methods_list (*this);
+    tw.visit_classdef_method_list (*this);
   }
 };
 
-class tree_classdef_methods_block
-  : public tree_classdef_element<tree_classdef_methods_list>
+class tree_classdef_methods_block : public tree_classdef_block<tree_classdef_method_list>
 {
 public:
 
-  tree_classdef_methods_block (tree_classdef_attribute_list *a,
-                               tree_classdef_methods_list *mlist,
-                               comment_list *lc, comment_list *tc,
-                               int l = -1, int c = -1)
-    : tree_classdef_element<tree_classdef_methods_list> (a, mlist, lc, tc, l, c)
+  tree_classdef_methods_block (const token& block_tok, tree_classdef_attribute_list *a, tree_classdef_method_list *mlist, const token& end_tok, int l = -1, int c = -1)
+    : tree_classdef_block<tree_classdef_method_list> (block_tok, a, mlist, end_tok, l, c)
   { }
 
   OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_classdef_methods_block)
 
   ~tree_classdef_methods_block () = default;
 
+  tree_classdef_method_list * method_list () { return element_list (); }
+
   void accept (tree_walker& tw)
   {
     tw.visit_classdef_methods_block (*this);
@@ -407,27 +435,17 @@
 {
 public:
 
-  tree_classdef_event (tree_identifier *i = nullptr,
-                       comment_list *comments = nullptr);
+  tree_classdef_event (tree_identifier *i = nullptr);
 
   OCTAVE_DISABLE_COPY_MOVE (tree_classdef_event)
 
   ~tree_classdef_event ()
   {
     delete m_id;
-    delete m_comments;
   }
 
   tree_identifier * ident () { return m_id; }
 
-  comment_list * comments () const { return m_comments; }
-
-  void doc_string (const std::string& txt) { m_doc_string = txt; }
-
-  std::string doc_string () const { return m_doc_string; }
-
-  bool have_doc_string () const { return ! m_doc_string.empty (); }
-
   void accept (tree_walker& tw)
   {
     tw.visit_classdef_event (*this);
@@ -436,48 +454,44 @@
 private:
 
   tree_identifier *m_id;
-  comment_list *m_comments;
-  std::string m_doc_string;
 };
 
-class tree_classdef_events_list : public std::list<tree_classdef_event *>
+class tree_classdef_event_list : public std::list<tree_classdef_event *>
 {
 public:
 
-  tree_classdef_events_list () { }
+  tree_classdef_event_list () { }
 
-  tree_classdef_events_list (tree_classdef_event *e) { push_back (e); }
+  tree_classdef_event_list (tree_classdef_event *e) { push_back (e); }
 
-  tree_classdef_events_list (const std::list<tree_classdef_event *>& a)
+  tree_classdef_event_list (const std::list<tree_classdef_event *>& a)
     : std::list<tree_classdef_event *> (a)
   { }
 
-  OCTAVE_DISABLE_COPY_MOVE (tree_classdef_events_list)
+  OCTAVE_DISABLE_COPY_MOVE (tree_classdef_event_list)
 
-  ~tree_classdef_events_list ();
+  ~tree_classdef_event_list ();
 
   void accept (tree_walker& tw)
   {
-    tw.visit_classdef_events_list (*this);
+    tw.visit_classdef_event_list (*this);
   }
 };
 
-class tree_classdef_events_block
-  : public tree_classdef_element<tree_classdef_events_list>
+class tree_classdef_events_block : public tree_classdef_block<tree_classdef_event_list>
 {
 public:
 
-  tree_classdef_events_block (tree_classdef_attribute_list *a,
-                              tree_classdef_events_list *elist,
-                              comment_list *lc, comment_list *tc,
-                              int l = -1, int c = -1)
-    : tree_classdef_element<tree_classdef_events_list> (a, elist, lc, tc, l, c)
+  tree_classdef_events_block (const token& block_tok, tree_classdef_attribute_list *a, tree_classdef_event_list *elist, const token& end_tok, int l = -1, int c = -1)
+    : tree_classdef_block<tree_classdef_event_list> (block_tok, a, elist, end_tok, l, c)
   { }
 
   OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_classdef_events_block)
 
   ~tree_classdef_events_block () = default;
 
+  tree_classdef_event_list * event_list () { return element_list (); }
+
   void accept (tree_walker& tw)
   {
     tw.visit_classdef_events_block (*this);
@@ -488,8 +502,7 @@
 {
 public:
 
-  tree_classdef_enum (tree_identifier *i, tree_expression *e,
-                      comment_list *comments);
+  tree_classdef_enum (tree_identifier *i, const token& open_paren, tree_expression *e, const token& close_paren);
 
   OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_classdef_enum)
 
@@ -497,20 +510,15 @@
   {
     delete m_id;
     delete m_expr;
-    delete m_comments;
   }
 
   tree_identifier * ident () { return m_id; }
 
+  token open_paren () const { return m_open_paren; }
+
   tree_expression * expression () { return m_expr; }
 
-  comment_list * comments () const { return m_comments; }
-
-  void doc_string (const std::string& txt) { m_doc_string = txt; }
-
-  std::string doc_string () const { return m_doc_string; }
-
-  bool have_doc_string () const { return ! m_doc_string.empty (); }
+  token close_paren () const { return m_close_paren; }
 
   void accept (tree_walker& tw)
   {
@@ -520,9 +528,9 @@
 private:
 
   tree_identifier *m_id;
+  token m_open_paren;
   tree_expression *m_expr;
-  comment_list *m_comments;
-  std::string m_doc_string;
+  token m_close_paren;
 };
 
 class tree_classdef_enum_list : public std::list<tree_classdef_enum *>
@@ -547,49 +555,43 @@
   }
 };
 
-class tree_classdef_enum_block
-  : public tree_classdef_element<tree_classdef_enum_list>
+class tree_classdef_enum_block : public tree_classdef_block<tree_classdef_enum_list>
 {
 public:
 
-  tree_classdef_enum_block (tree_classdef_attribute_list *a,
-                            tree_classdef_enum_list *elist,
-                            comment_list *lc, comment_list *tc,
-                            int l = -1, int c = -1)
-    : tree_classdef_element<tree_classdef_enum_list> (a, elist, lc, tc, l, c)
+  tree_classdef_enum_block (const token& block_tok, tree_classdef_attribute_list *a, tree_classdef_enum_list *elist, const token& end_tok, int l = -1, int c = -1)
+    : tree_classdef_block<tree_classdef_enum_list> (block_tok, a, elist, end_tok, l, c)
   { }
 
   OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_classdef_enum_block)
 
   ~tree_classdef_enum_block () = default;
 
+  tree_classdef_enum_list * enum_list () { return element_list (); }
+
   void accept (tree_walker& tw)
   {
     tw.visit_classdef_enum_block (*this);
   }
 };
 
+// FIXME: should this class be derived from tree?
+
 class tree_classdef_body
 {
 public:
 
-  typedef std::list<tree_classdef_properties_block *>::iterator
-    properties_list_iterator;
-  typedef std::list<tree_classdef_properties_block *>::const_iterator
-    properties_list_const_iterator;
+  typedef std::list<tree_classdef_properties_block *>::iterator property_list_iterator;
+  typedef std::list<tree_classdef_properties_block *>::const_iterator property_list_const_iterator;
 
-  typedef std::list<tree_classdef_methods_block *>::iterator
-    methods_list_iterator;
-  typedef std::list<tree_classdef_methods_block *>::const_iterator
-    methods_list_const_iterator;
+  typedef std::list<tree_classdef_methods_block *>::iterator method_list_iterator;
+  typedef std::list<tree_classdef_methods_block *>::const_iterator method_list_const_iterator;
 
-  typedef std::list<tree_classdef_events_block *>::iterator events_list_iterator;
-  typedef std::list<tree_classdef_events_block *>::const_iterator
-    events_list_const_iterator;
+  typedef std::list<tree_classdef_events_block *>::iterator event_list_iterator;
+  typedef std::list<tree_classdef_events_block *>::const_iterator event_list_const_iterator;
 
   typedef std::list<tree_classdef_enum_block *>::iterator enum_list_iterator;
-  typedef std::list<tree_classdef_enum_block *>::const_iterator
-    enum_list_const_iterator;
+  typedef std::list<tree_classdef_enum_block *>::const_iterator enum_list_const_iterator;
 
   tree_classdef_body ();
 
@@ -605,43 +607,49 @@
 
   ~tree_classdef_body ();
 
+  comment_list leading_comments () const;
+
   tree_classdef_body * append (tree_classdef_properties_block *pb)
   {
-    m_properties_lst.push_back (pb);
+    m_property_lst.push_back (pb);
+    m_all_elements.push_back (pb);
     return this;
   }
 
   tree_classdef_body * append (tree_classdef_methods_block *mb)
   {
-    m_methods_lst.push_back (mb);
+    m_method_lst.push_back (mb);
+    m_all_elements.push_back (mb);
     return this;
   }
 
   tree_classdef_body * append (tree_classdef_events_block *evb)
   {
-    m_events_lst.push_back (evb);
+    m_event_lst.push_back (evb);
+    m_all_elements.push_back (evb);
     return this;
   }
 
   tree_classdef_body * append (tree_classdef_enum_block *enb)
   {
     m_enum_lst.push_back (enb);
+    m_all_elements.push_back (enb);
     return this;
   }
 
-  std::list<tree_classdef_properties_block *> properties_list ()
+  std::list<tree_classdef_properties_block *> property_list ()
   {
-    return m_properties_lst;
+    return m_property_lst;
   }
 
-  std::list<tree_classdef_methods_block *> methods_list ()
+  std::list<tree_classdef_methods_block *> method_list ()
   {
-    return m_methods_lst;
+    return m_method_lst;
   }
 
-  std::list<tree_classdef_events_block *> events_list ()
+  std::list<tree_classdef_events_block *> event_list ()
   {
-    return m_events_lst;
+    return m_event_lst;
   }
 
   std::list<tree_classdef_enum_block *> enum_list ()
@@ -649,12 +657,6 @@
     return m_enum_lst;
   }
 
-  void doc_string (const std::string& txt) { m_doc_string = txt; }
-
-  std::string doc_string () const { return m_doc_string; }
-
-  bool have_doc_string () const { return ! m_doc_string.empty (); }
-
   void accept (tree_walker& tw)
   {
     tw.visit_classdef_body (*this);
@@ -662,17 +664,15 @@
 
 private:
 
-  std::string get_doc_string (comment_list *comment) const;
-
-  std::list<tree_classdef_properties_block *> m_properties_lst;
+  std::list<tree_classdef_properties_block *> m_property_lst;
 
-  std::list<tree_classdef_methods_block *> m_methods_lst;
+  std::list<tree_classdef_methods_block *> m_method_lst;
 
-  std::list<tree_classdef_events_block *> m_events_lst;
+  std::list<tree_classdef_events_block *> m_event_lst;
 
   std::list<tree_classdef_enum_block *> m_enum_lst;
 
-  std::string m_doc_string;
+  std::list<tree_base_classdef_block *> m_all_elements;
 };
 
 // Classdef definition.
@@ -681,17 +681,11 @@
 {
 public:
 
-  tree_classdef (const symbol_scope& scope, const std::string& help_text,
-                 tree_classdef_attribute_list *a, tree_identifier *i,
-                 tree_classdef_superclass_list *sc,
-                 tree_classdef_body *b, comment_list *lc,
-                 comment_list *tc, const std::string& pn = "",
-                 const std::string& fn = "", int l = -1, int c = -1)
-    : tree_command (l, c), m_scope (scope), m_help_text (help_text),
-      m_attr_list (a), m_id (i),
-      m_supclass_list (sc), m_element_list (b), m_lead_comm (lc),
-      m_trail_comm (tc), m_pack_name (pn), m_file_name (fn)
-  { }
+  tree_classdef (const symbol_scope& scope, const token& cdef_tok, tree_classdef_attribute_list *a, tree_identifier *i, tree_classdef_superclass_list *sc, tree_classdef_body *b, const token& end_tok, const std::string& pn = "", const std::string& fn = "", int l = -1, int c = -1)
+    : tree_command (l, c), m_scope (scope), m_cdef_tok (cdef_tok), m_attr_list (a), m_id (i), m_supclass_list (sc), m_body (b), m_end_tok (end_tok), m_pack_name (pn), m_file_name (fn)
+  {
+    cache_doc_string ();
+  }
 
   OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_classdef)
 
@@ -700,9 +694,7 @@
     delete m_attr_list;
     delete m_id;
     delete m_supclass_list;
-    delete m_element_list;
-    delete m_lead_comm;
-    delete m_trail_comm;
+    delete m_body;
   }
 
   symbol_scope scope () { return m_scope; }
@@ -715,10 +707,9 @@
   tree_classdef_superclass_list *
   superclass_list () { return m_supclass_list; }
 
-  tree_classdef_body * body () { return m_element_list; }
+  tree_classdef_body * body () { return m_body; }
 
-  comment_list * leading_comment () { return m_lead_comm; }
-  comment_list * trailing_comment () { return m_trail_comm; }
+  comment_list leading_comments () const { return m_cdef_tok.leading_comments (); }
 
   std::string package_name () const { return m_pack_name; }
 
@@ -727,10 +718,7 @@
   octave_value make_meta_class (interpreter& interp,
                                 bool is_at_folder = false);
 
-  std::string doc_string () const
-  {
-    return m_help_text;
-  }
+  std::string doc_string () const { return m_doc_string; }
 
   void accept (tree_walker& tw)
   {
@@ -739,13 +727,31 @@
 
 private:
 
+  void cache_doc_string ()
+  {
+    // First non-copyright comments found above and below classdef
+    // keyword are candidates for the documentation string.  Use the
+    // first one that is not empty.
+
+    comment_list comments = m_cdef_tok.leading_comments ();
+
+    m_doc_string = comments.find_doc_string ();
+
+    if (m_doc_string.empty ())
+      {
+        comments = m_body->leading_comments ();
+
+        m_doc_string = comments.find_doc_string ();
+      }
+  }
+
   // The scope that was used when parsing the classdef object and that
   // corresponds to any identifiers that were found in attribute lists
   // (for example).  Used again when computing the meta class object.
 
   symbol_scope m_scope;
 
-  std::string m_help_text;
+  token m_cdef_tok;
 
   tree_classdef_attribute_list *m_attr_list;
 
@@ -753,10 +759,11 @@
 
   tree_classdef_superclass_list *m_supclass_list;
 
-  tree_classdef_body *m_element_list;
+  tree_classdef_body *m_body;
 
-  comment_list *m_lead_comm;
-  comment_list *m_trail_comm;
+  token m_end_tok;
+
+  std::string m_doc_string;
 
   std::string m_pack_name;
   std::string m_file_name;
--- a/libinterp/parse-tree/pt-cmd.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-cmd.h	Mon Apr 01 23:27:43 2024 -0400
@@ -30,10 +30,12 @@
 
 #include <string>
 
+#include "comment-list.h"
 #include "ov-fcn.h"
 #include "pt.h"
 #include "pt-bp.h"
 #include "pt-walk.h"
+#include "token.h"
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
@@ -57,14 +59,25 @@
 {
 public:
 
-  tree_no_op_command (const std::string& cmd = "no_op", bool e = false,
-                      int l = -1, int c = -1)
-    : tree_command (l, c), m_eof (e), m_orig_cmd (cmd) { }
+  tree_no_op_command (const std::string& cmd, bool eof, const token& tok, int l = -1, int c = -1)
+    : tree_command (l, c), m_eof (eof), m_tok (tok), m_orig_cmd (cmd) { }
 
-  OCTAVE_DISABLE_COPY_MOVE (tree_no_op_command)
+  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_no_op_command)
 
   ~tree_no_op_command () = default;
 
+  comment_list leading_comments () const { return m_tok.leading_comments (); }
+
+  void attach_trailing_comments (const comment_list& lst)
+  {
+    m_tok.trailing_comments (lst);
+  }
+
+  comment_list trailing_comments () const
+  {
+    return m_tok.trailing_comments ();
+  }
+
   void accept (tree_walker& tw)
   {
     tw.visit_no_op_command (*this);
@@ -83,6 +96,9 @@
 
   bool m_eof;
 
+  // If defined, may be END token or EOF.
+  token m_tok;
+
   std::string m_orig_cmd;
 };
 
--- a/libinterp/parse-tree/pt-colon.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-colon.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -41,9 +41,10 @@
 {
   tree_colon_expression *new_ce
     = new tree_colon_expression (m_base ? m_base->dup (scope) : nullptr,
+                                 m_colon_1_tok,
+                                 m_increment ? m_increment->dup (scope) : nullptr,
+                                 m_colon_2_tok,
                                  m_limit ? m_limit->dup (scope) : nullptr,
-                                 m_increment ? m_increment->dup (scope)
-                                 : nullptr,
                                  line (), column ());
 
   new_ce->copy_base (*this);
--- a/libinterp/parse-tree/pt-colon.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-colon.h	Mon Apr 01 23:27:43 2024 -0400
@@ -46,19 +46,13 @@
 {
 public:
 
-  tree_colon_expression (int l = -1, int c = -1)
-    : tree_expression (l, c), m_base (nullptr), m_limit (nullptr),
-      m_increment (nullptr), m_save_base (false) { }
+  tree_colon_expression (tree_expression *base, const token& colon_1_tok, tree_expression *limit, int l = -1, int c = -1)
+    : tree_expression (l, c), m_base (base), m_colon_1_tok (colon_1_tok), m_limit (limit)
+  { }
 
-  tree_colon_expression (tree_expression *bas, tree_expression *lim,
-                         int l = -1, int c = -1)
-    : tree_expression (l, c), m_base (bas), m_limit (lim),
-      m_increment (nullptr), m_save_base (false) { }
-
-  tree_colon_expression (tree_expression *bas, tree_expression *lim,
-                         tree_expression *inc, int l = -1, int c = -1)
-    : tree_expression (l, c), m_base (bas), m_limit (lim),
-      m_increment (inc), m_save_base (false) { }
+  tree_colon_expression (tree_expression *base, const token& colon_1_tok, tree_expression *increment, const token& colon_2_tok, tree_expression *limit, int l = -1, int c = -1)
+    : tree_expression (l, c), m_base (base), m_colon_1_tok (colon_1_tok), m_increment (increment), m_colon_2_tok (colon_2_tok), m_limit (limit)
+  { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_colon_expression)
 
@@ -103,10 +97,12 @@
 
   // The components of the expression.
   tree_expression *m_base;
+  token m_colon_1_tok;
+  tree_expression *m_increment {nullptr};
+  token m_colon_2_tok;
   tree_expression *m_limit;
-  tree_expression *m_increment;
 
-  bool m_save_base;
+  bool m_save_base {false};
 };
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-eval.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-eval.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -3413,7 +3413,7 @@
   if (expr)
     {
       tree_expression *expr_dup = expr->dup (new_scope);
-      tree_statement *stmt = new tree_statement (expr_dup, nullptr);
+      tree_statement *stmt = new tree_statement (expr_dup);
       stmt_list = new tree_statement_list (stmt);
     }
 
--- a/libinterp/parse-tree/pt-except.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-except.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -52,9 +52,6 @@
   delete m_expr_id;
   delete m_try_code;
   delete m_catch_code;
-  delete m_lead_comm;
-  delete m_mid_comm;
-  delete m_trail_comm;
 }
 
 // Simple exception handling.
@@ -63,9 +60,6 @@
 {
   delete m_unwind_protect_code;
   delete m_cleanup_code;
-  delete m_lead_comm;
-  delete m_mid_comm;
-  delete m_trail_comm;
 }
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-except.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-except.h	Mon Apr 01 23:27:43 2024 -0400
@@ -43,20 +43,8 @@
 {
 public:
 
-  tree_try_catch_command (int l = -1, int c = -1)
-    : tree_command (l, c), m_try_code (nullptr), m_catch_code (nullptr),
-      m_expr_id (nullptr), m_lead_comm (nullptr), m_mid_comm (nullptr),
-      m_trail_comm (nullptr)
-  { }
-
-  tree_try_catch_command (tree_statement_list *tc, tree_statement_list *cc,
-                          tree_identifier *id,
-                          comment_list *cl = nullptr,
-                          comment_list *cm = nullptr,
-                          comment_list *ct = nullptr,
-                          int l = -1, int c = -1)
-    : tree_command (l, c), m_try_code (tc), m_catch_code (cc),
-      m_expr_id (id), m_lead_comm (cl), m_mid_comm (cm), m_trail_comm (ct)
+  tree_try_catch_command (const token try_tok, tree_statement_list *tc, const token catch_tok, tree_identifier *id, tree_statement_list *cc, const token& end_tok, int l = -1, int c = -1)
+    : tree_command (l, c), m_try_tok (try_tok), m_try_code (tc), m_catch_tok (catch_tok), m_expr_id (id), m_catch_code (cc), m_end_tok (end_tok)
   { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_try_catch_command)
@@ -69,12 +57,6 @@
 
   tree_statement_list * cleanup () { return m_catch_code; }
 
-  comment_list * leading_comment () { return m_lead_comm; }
-
-  comment_list * middle_comment () { return m_mid_comm; }
-
-  comment_list * trailing_comment () { return m_trail_comm; }
-
   void accept (tree_walker& tw)
   {
     tw.visit_try_catch_command (*this);
@@ -82,23 +64,20 @@
 
 private:
 
+  token m_try_tok;
+
   // The first block of code to attempt to execute.
   tree_statement_list *m_try_code;
 
-  // The code to execute if an error occurs in the first block.
-  tree_statement_list *m_catch_code;
+  token m_catch_tok;
 
   // Identifier to modify.
   tree_identifier *m_expr_id;
 
-  // Comment preceding TRY token.
-  comment_list *m_lead_comm;
+  // The code to execute if an error occurs in the first block.
+  tree_statement_list *m_catch_code;
 
-  // Comment preceding CATCH token.
-  comment_list *m_mid_comm;
-
-  // Comment preceding END_TRY_CATCH token.
-  comment_list *m_trail_comm;
+  token m_end_tok;
 };
 
 // Simple exception handling.
@@ -107,20 +86,8 @@
 {
 public:
 
-  tree_unwind_protect_command (int l = -1, int c = -1)
-    : tree_command (l, c),
-      m_unwind_protect_code (nullptr), m_cleanup_code (nullptr),
-      m_lead_comm (nullptr), m_mid_comm (nullptr), m_trail_comm (nullptr)
-  { }
-
-  tree_unwind_protect_command (tree_statement_list *tc,
-                               tree_statement_list *cc,
-                               comment_list *cl = nullptr,
-                               comment_list *cm = nullptr,
-                               comment_list *ct = nullptr,
-                               int l = -1, int c = -1)
-    : tree_command (l, c), m_unwind_protect_code (tc), m_cleanup_code (cc),
-      m_lead_comm (cl), m_mid_comm (cm), m_trail_comm (ct)
+  tree_unwind_protect_command (const token& unwind_tok, tree_statement_list *tc, const token& cleanup_tok, tree_statement_list *cc, const token& end_tok, int l = -1, int c = -1)
+    : tree_command (l, c), m_unwind_tok (unwind_tok), m_unwind_protect_code (tc), m_cleanup_tok (cleanup_tok), m_cleanup_code (cc), m_end_tok (end_tok)
   { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_unwind_protect_command)
@@ -131,12 +98,6 @@
 
   tree_statement_list * cleanup () { return m_cleanup_code; }
 
-  comment_list * leading_comment () { return m_lead_comm; }
-
-  comment_list * middle_comment () { return m_mid_comm; }
-
-  comment_list * trailing_comment () { return m_trail_comm; }
-
   void accept (tree_walker& tw)
   {
     tw.visit_unwind_protect_command (*this);
@@ -144,21 +105,18 @@
 
 private:
 
+  token m_unwind_tok;
+
   // The first body of code to attempt to execute.
   tree_statement_list *m_unwind_protect_code;
 
+  token m_cleanup_tok;
+
   // The body of code to execute no matter what happens in the first
   // body of code.
   tree_statement_list *m_cleanup_code;
 
-  // Comment preceding UNWIND_PROTECT token.
-  comment_list *m_lead_comm;
-
-  // Comment preceding UNWIND_PROTECT_CLEANUP token.
-  comment_list *m_mid_comm;
-
-  // Comment preceding END_UNWIND_PROTECT token.
-  comment_list *m_trail_comm;
+  token m_end_tok;
 };
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-exp.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-exp.h	Mon Apr 01 23:27:43 2024 -0400
@@ -34,7 +34,9 @@
 class octave_value;
 
 #include "pt.h"
+#include "pt-delimiter-list.h"
 #include "pt-eval.h"
+#include "token.h"
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
@@ -48,8 +50,7 @@
 public:
 
   tree_expression (int l = -1, int c = -1)
-    : tree (l, c), m_num_parens (0), m_postfix_index_type ('\0'),
-      m_for_cmd_expr (false), m_print_flag (false) { }
+    : tree (l, c), m_postfix_index_type ('\0'), m_for_cmd_expr (false), m_print_flag (false) { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_expression)
 
@@ -85,7 +86,14 @@
 
   virtual octave_lvalue lvalue (tree_evaluator&);
 
-  int paren_count () const { return m_num_parens; }
+  // The number of times this expression appears directly inside a set
+  // of delimiters.
+  //
+  //   (([e1]) + e2)  ==> 2 for expression e1
+  //                  ==> 1 for expression ([e1]) + e2
+  //                  ==> 0 for expression e2
+
+  size_t delim_count () const { return m_delims.count (); }
 
   bool is_postfix_indexed () const
   { return (m_postfix_index_type != '\0'); }
@@ -107,9 +115,9 @@
 
   bool is_for_cmd_expr () const { return m_for_cmd_expr; }
 
-  tree_expression * mark_in_parens ()
+  tree_expression * mark_in_delims (const token& open_delim, const token& close_delim)
   {
-    m_num_parens++;
+    m_delims.push (open_delim, close_delim);
     return this;
   }
 
@@ -127,9 +135,9 @@
 
   virtual void copy_base (const tree_expression& e)
   {
-    m_num_parens = e.m_num_parens;
     m_postfix_index_type = e.m_postfix_index_type;
     m_print_flag = e.m_print_flag;
+    m_delims = e.m_delims;
   }
 
   virtual octave_value evaluate (tree_evaluator& tw, int nargout = 1) = 0;
@@ -139,14 +147,6 @@
 
 protected:
 
-  // A count of the number of times this expression appears directly
-  // inside a set of parentheses.
-  //
-  //   (((e1)) + e2)  ==> 2 for expression e1
-  //                  ==> 1 for expression ((e1)) + e2
-  //                  ==> 0 for expression e2
-  int m_num_parens;
-
   // The first index type associated with this expression.  This field
   // is 0 (character '\0') if the expression has no associated index.
   // See the code in tree_identifier::rvalue for the rationale.
@@ -158,6 +158,9 @@
 
   // Print result of rvalue for this expression?
   bool m_print_flag;
+
+  // Tokens for any delimiters surrounding this expression.
+  tree_delimiter_list m_delims;
 };
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-id.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-id.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -81,8 +81,7 @@
 
   symbol_record new_sym = scope.find_symbol (name ());
 
-  tree_identifier *new_id
-    = new tree_identifier (new_sym, line (), column ());
+  tree_identifier *new_id = new tree_identifier (new_sym, m_token);
 
   new_id->copy_base (*this);
 
--- a/libinterp/parse-tree/pt-id.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-id.h	Mon Apr 01 23:27:43 2024 -0400
@@ -35,11 +35,13 @@
 class octave_value_list;
 class octave_function;
 
+#include "comment-list.h"
 #include "oct-lvalue.h"
 #include "pt-bp.h"
 #include "pt-exp.h"
 #include "pt-walk.h"
 #include "symscope.h"
+#include "token.h"
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
@@ -53,12 +55,15 @@
 
 public:
 
-  tree_identifier (int l = -1, int c = -1)
-    : tree_expression (l, c), m_sym () { }
+  tree_identifier (const token& tok)
+    : tree_expression (tok.line (), tok.column ()), m_sym (), m_token (tok)
+  { }
 
-  tree_identifier (const symbol_record& s,
-                   int l = -1, int c = -1)
-    : tree_expression (l, c), m_sym (s) { }
+  tree_identifier (symbol_scope& scope, const token& tok)
+    : tree_expression (tok.line (), tok.column ()),
+      m_sym (scope ? scope.insert (tok.text ()) : symbol_record (tok.text ())),
+      m_token (tok)
+  { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_identifier)
 
@@ -68,6 +73,8 @@
 
   std::string name () const { return m_sym.name (); }
 
+  comment_list leading_comments () const { return m_token.leading_comments (); }
+
   virtual bool is_black_hole () const { return false; }
 
   void mark_as_formal_parameter () { m_sym.mark_formal (); }
@@ -105,18 +112,38 @@
 
   symbol_record symbol () const { return m_sym; }
 
+  tree_identifier * mark_get_set (const token& get_set_tok, const token& dot_tok)
+  {
+    m_get_set_tok = get_set_tok;
+    m_dot_tok = dot_tok;
+
+    return this;
+  }
+
 protected:
 
+  tree_identifier (symbol_record& sym, const token& tok)
+    : tree_expression (tok.line (), tok.column ()), m_sym (sym), m_token (tok)
+  { }
+
   // The symbol record that this identifier references.
   symbol_record m_sym;
+
+  // These will be defined for get.ID or set.ID function names.
+  token m_get_set_tok;
+  token m_dot_tok;
+
+  // The IDENT token from the lexer.
+  token m_token;
 };
 
 class tree_black_hole : public tree_identifier
 {
 public:
 
-  tree_black_hole (int l = -1, int c = -1)
-    : tree_identifier (l, c) { }
+  tree_black_hole (const token& token)
+    : tree_identifier (token)
+  { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_black_hole)
 
@@ -128,7 +155,7 @@
 
   tree_black_hole * dup (symbol_scope&) const
   {
-    return new tree_black_hole;
+    return new tree_black_hole (m_token);
   }
 
   octave_lvalue lvalue (tree_evaluator& tw);
--- a/libinterp/parse-tree/pt-idx.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-idx.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -47,38 +47,31 @@
 // Index expressions.
 
 tree_index_expression::tree_index_expression (int l, int c)
-  : tree_expression (l, c), m_expr (nullptr), m_args (0), m_type (),
-    m_arg_nm (), m_dyn_field (), m_word_list_cmd (false) { }
+  : tree_expression (l, c)
+{ }
 
-tree_index_expression::tree_index_expression (tree_expression *e,
-    tree_argument_list *lst,
-    int l, int c, char t)
-  : tree_expression (l, c), m_expr (e), m_args (0), m_type (),
-    m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
+tree_index_expression::tree_index_expression (tree_expression *e, const token& open_delim, tree_argument_list *lst, const token& close_delim, int l, int c, char t)
+  : tree_expression (l, c), m_expr (e), m_args (0), m_type (), m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
 {
-  append (lst, t);
+  append (open_delim, lst, close_delim, t);
 }
 
-tree_index_expression::tree_index_expression (tree_expression *e,
-    const std::string& n,
-    int l, int c)
-  : tree_expression (l, c), m_expr (e), m_args (0), m_type (),
-    m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
+tree_index_expression::tree_index_expression (tree_expression *e, const token& dot_tok, const token& struct_elt_tok, int l, int c)
+  : tree_expression (l, c), m_expr (e), m_args (0), m_type (), m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
 {
-  append (n);
+  append (dot_tok, struct_elt_tok);
 }
 
-tree_index_expression::tree_index_expression (tree_expression *e,
-    tree_expression *df,
-    int l, int c)
-  : tree_expression (l, c), m_expr (e), m_args (0), m_type (),
-    m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
+tree_index_expression::tree_index_expression (tree_expression *e, const token& dot_tok, const token& open_paren, tree_expression *df, const token& close_paren, int l, int c)
+  : tree_expression (l, c), m_expr (e), m_args (0), m_type (), m_arg_nm (), m_dyn_field (), m_word_list_cmd (false)
 {
-  append (df);
+  append (dot_tok, open_paren, df, close_paren);
 }
 
+// FIXME: Need to handle open_delim and close_delim.
+
 tree_index_expression *
-tree_index_expression::append (tree_argument_list *lst, char t)
+tree_index_expression::append (const token& /*open_delim*/, tree_argument_list *lst, const token& /*close_delim*/, char t)
 {
   m_args.push_back (lst);
   m_type.append (1, t);
@@ -91,19 +84,23 @@
   return this;
 }
 
+// FIXME: Need to handle dot_tok.
+
 tree_index_expression *
-tree_index_expression::append (const std::string& n)
+tree_index_expression::append (const token& /*dot_tok*/, const token& struct_elt_tok)
 {
   m_args.push_back (static_cast<tree_argument_list *> (nullptr));
   m_type += '.';
-  m_arg_nm.push_back (n);
+  m_arg_nm.push_back (struct_elt_tok.text ());
   m_dyn_field.push_back (static_cast<tree_expression *> (nullptr));
 
   return this;
 }
 
+// FIXME: Need to handle dot_tok, open_paren, and close_paren.
+
 tree_index_expression *
-tree_index_expression::append (tree_expression *df)
+tree_index_expression::append (const token& /*dot_tok*/, const token& /*open_paren*/, tree_expression *df, const token& /*close_paren*/)
 {
   m_args.push_back (static_cast<tree_argument_list *> (nullptr));
   m_type += '.';
--- a/libinterp/parse-tree/pt-idx.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-idx.h	Mon Apr 01 23:27:43 2024 -0400
@@ -52,26 +52,22 @@
 {
 public:
 
-  tree_index_expression (tree_expression *e = nullptr,
-                         tree_argument_list *lst = nullptr,
-                         int l = -1, int c = -1, char t = '(');
+  tree_index_expression (tree_expression *e, const token& open_delim, tree_argument_list *lst, const token& close_delim, int l, int c, char t);
 
-  tree_index_expression (tree_expression *e, const std::string& n,
-                         int l = -1, int c = -1);
+  tree_index_expression (tree_expression *e, const token& dot_tok, const token& struct_elt_tok, int l = -1, int c = -1);
 
-  tree_index_expression (tree_expression *e, tree_expression *df,
-                         int l = -1, int c = -1);
+  tree_index_expression (tree_expression *e, const token& dot_tok, const token& open_paren, tree_expression *df, const token& close_paren, int l = -1, int c = -1);
 
   OCTAVE_DISABLE_COPY_MOVE (tree_index_expression)
 
   ~tree_index_expression ();
 
   tree_index_expression *
-  append (tree_argument_list *lst = nullptr, char t = '(');
+  append (const token& open_delim, tree_argument_list *lst, const token& close_delim, char t = '(');
 
-  tree_index_expression * append (const std::string& n);
+  tree_index_expression * append (const token& dot_tok, const token& struct_elt_tok);
 
-  tree_index_expression * append (tree_expression *df);
+  tree_index_expression * append (const token& dot_tok, const token& open_paren, tree_expression *df, const token& close_paren);
 
   bool is_index_expression () const { return true; }
 
@@ -121,7 +117,7 @@
 private:
 
   // The LHS of this index expression.
-  tree_expression *m_expr;
+  tree_expression *m_expr {nullptr};
 
   // The indices (only valid if type == paren || type == brace).
   std::list<tree_argument_list *> m_args;
@@ -137,7 +133,7 @@
   std::list<tree_expression *> m_dyn_field;
 
   // TRUE if this expression was parsed as a word list command.
-  bool m_word_list_cmd;
+  bool m_word_list_cmd {false};
 
   tree_index_expression (int l, int c);
 
--- a/libinterp/parse-tree/pt-loop.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-loop.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -39,9 +39,15 @@
 tree_while_command::~tree_while_command ()
 {
   delete m_expr;
-  delete m_list;
-  delete m_lead_comm;
-  delete m_trail_comm;
+  delete m_body;
+}
+
+// Do-until.
+
+tree_do_until_command::~tree_do_until_command ()
+{
+  delete m_body;
+  delete m_expr;
 }
 
 // For.
@@ -51,18 +57,14 @@
   delete m_lhs;
   delete m_expr;
   delete m_maxproc;
-  delete m_list;
-  delete m_lead_comm;
-  delete m_trail_comm;
+  delete m_body;
 }
 
 tree_complex_for_command::~tree_complex_for_command ()
 {
   delete m_lhs;
   delete m_expr;
-  delete m_list;
-  delete m_lead_comm;
-  delete m_trail_comm;
+  delete m_body;
 }
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-loop.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-loop.h	Mon Apr 01 23:27:43 2024 -0400
@@ -35,6 +35,7 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
+class comment_list;
 class tree_argument_list;
 class tree_expression;
 class tree_statement_list;
@@ -45,25 +46,8 @@
 {
 public:
 
-  tree_while_command (int l = -1, int c = -1)
-    : tree_command (l, c), m_expr (nullptr), m_list (nullptr),
-      m_lead_comm (nullptr), m_trail_comm (nullptr)
-  { }
-
-  tree_while_command (tree_expression *e,
-                      comment_list *lc = nullptr,
-                      comment_list *tc = nullptr,
-                      int l = -1, int c = -1)
-    : tree_command (l, c), m_expr (e), m_list (nullptr),
-      m_lead_comm (lc), m_trail_comm (tc)
-  { }
-
-  tree_while_command (tree_expression *e, tree_statement_list *lst,
-                      comment_list *lc = nullptr,
-                      comment_list *tc = nullptr,
-                      int l = -1, int c = -1)
-    : tree_command (l, c), m_expr (e), m_list (lst), m_lead_comm (lc),
-      m_trail_comm (tc)
+  tree_while_command (const token& while_tok, tree_expression *expr, tree_statement_list *body, const token& end_tok, int l = -1, int c = -1)
+    : tree_command (l, c), m_while_tok (while_tok), m_expr (expr), m_body (body), m_end_tok (end_tok)
   { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_while_command)
@@ -72,64 +56,60 @@
 
   tree_expression * condition () { return m_expr; }
 
-  tree_statement_list * body () { return m_list; }
-
-  comment_list * leading_comment () { return m_lead_comm; }
-
-  comment_list * trailing_comment () { return m_trail_comm; }
+  tree_statement_list * body () { return m_body; }
 
   void accept (tree_walker& tw)
   {
     tw.visit_while_command (*this);
   }
 
-protected:
+private:
+
+  token m_while_tok;
 
   // Expression to test.
   tree_expression *m_expr;
 
   // List of commands to execute.
-  tree_statement_list *m_list;
+  tree_statement_list *m_body {nullptr};
 
-  // Comment preceding WHILE token.
-  comment_list *m_lead_comm;
-
-  // Comment preceding ENDWHILE token.
-  comment_list *m_trail_comm;
+  token m_end_tok;
 };
 
 // Do-Until.
 
-class tree_do_until_command : public tree_while_command
+class tree_do_until_command : public tree_command
 {
 public:
 
-  tree_do_until_command (int l = -1, int c = -1)
-    : tree_while_command (l, c)
-  { }
-
-  tree_do_until_command (tree_expression *e,
-                         comment_list *lc = nullptr,
-                         comment_list *tc = nullptr,
-                         int l = -1, int c = -1)
-    : tree_while_command (e, lc, tc, l, c)
-  { }
-
-  tree_do_until_command (tree_expression *e, tree_statement_list *lst,
-                         comment_list *lc = nullptr,
-                         comment_list *tc = nullptr,
-                         int l = -1, int c = -1)
-    : tree_while_command (e, lst, lc, tc, l, c)
+  tree_do_until_command (const token& do_tok, tree_statement_list *body, const token& until_tok, tree_expression *expr, int l = -1, int c = -1)
+    : tree_command (l, c), m_do_tok (do_tok), m_body (body), m_until_tok (until_tok), m_expr (expr)
   { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_do_until_command)
 
-  ~tree_do_until_command () = default;
+  ~tree_do_until_command ();
+
+  tree_statement_list * body () { return m_body; }
+
+  tree_expression * condition () { return m_expr; }
 
   void accept (tree_walker& tw)
   {
     tw.visit_do_until_command (*this);
   }
+
+private:
+
+  token m_do_tok;
+
+  // List of commands to execute.
+  tree_statement_list *m_body {nullptr};
+
+  token m_until_tok;
+
+  // Expression to test.
+  tree_expression *m_expr;
 };
 
 // For.
@@ -138,29 +118,19 @@
 {
 public:
 
-  tree_simple_for_command (int l = -1, int c = -1)
-    : tree_command (l, c), m_parallel (false), m_lhs (nullptr),
-      m_expr (nullptr), m_maxproc (nullptr), m_list (nullptr),
-      m_lead_comm (nullptr), m_trail_comm (nullptr)
-  { }
-
-  tree_simple_for_command (bool parallel_arg, tree_expression *le,
-                           tree_expression *re,
-                           tree_expression *maxproc_arg,
-                           tree_statement_list *lst,
-                           comment_list *lc = nullptr,
-                           comment_list *tc = nullptr,
-                           int l = -1, int c = -1)
-    : tree_command (l, c), m_parallel (parallel_arg), m_lhs (le),
-      m_expr (re), m_maxproc (maxproc_arg), m_list (lst),
-      m_lead_comm (lc), m_trail_comm (tc)
+  tree_simple_for_command (bool parfor, const token& for_tok, const token& open_paren, tree_expression *le, const token& eq_tok,
+                           tree_expression *re, const token& sep_tok, tree_expression *maxproc_arg, const token& close_paren,
+                           tree_statement_list *body, const token& end_tok, int l = -1, int c = -1)
+    : tree_command (l, c), m_parfor (parfor), m_for_tok (for_tok), m_open_paren (open_paren), m_lhs (le), m_eq_tok (eq_tok),
+      m_expr (re), m_sep_tok (sep_tok), m_maxproc (maxproc_arg), m_close_paren (close_paren),
+      m_body (body), m_end_tok (end_tok)
   { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_simple_for_command)
 
   ~tree_simple_for_command ();
 
-  bool in_parallel () { return m_parallel; }
+  bool in_parallel () { return m_parfor; }
 
   tree_expression * left_hand_side () { return m_lhs; }
 
@@ -168,11 +138,7 @@
 
   tree_expression * maxproc_expr () { return m_maxproc; }
 
-  tree_statement_list * body () { return m_list; }
-
-  comment_list * leading_comment () { return m_lead_comm; }
-
-  comment_list * trailing_comment () { return m_trail_comm; }
+  tree_statement_list * body () { return m_body; }
 
   void accept (tree_walker& tw)
   {
@@ -180,46 +146,44 @@
   }
 
 private:
-  // TRUE means operate in parallel (subject to the value of the
-  // maxproc expression).
-  bool m_parallel;
+
+  // FIXME: it would be better to get this info from FOR_TOK.
+  bool m_parfor {false};
+
+  token m_for_tok;
+
+  token m_open_paren;
 
   // Expression to modify.
   tree_expression *m_lhs;
 
+  token m_eq_tok;
+
   // Expression to evaluate.
   tree_expression *m_expr;
 
+  token m_sep_tok;
+
   // Expression to tell how many processors should be used (only valid
   // if parallel is TRUE).
-  tree_expression *m_maxproc;
+  tree_expression *m_maxproc {nullptr};
+
+  token m_close_paren;
 
   // List of commands to execute.
-  tree_statement_list *m_list;
+  tree_statement_list *m_body;
 
-  // Comment preceding FOR token.
-  comment_list *m_lead_comm;
-
-  // Comment preceding ENDFOR token.
-  comment_list *m_trail_comm;
+  token m_end_tok;
 };
 
 class tree_complex_for_command : public tree_command
 {
 public:
 
-  tree_complex_for_command (int l = -1, int c = -1)
-    : tree_command (l, c), m_lhs (nullptr), m_expr (nullptr),
-      m_list (nullptr), m_lead_comm (nullptr), m_trail_comm (nullptr)
-  { }
-
-  tree_complex_for_command (tree_argument_list *le, tree_expression *re,
-                            tree_statement_list *lst,
-                            comment_list *lc = nullptr,
-                            comment_list *tc = nullptr,
-                            int l = -1, int c = -1)
-    : tree_command (l, c), m_lhs (le), m_expr (re), m_list (lst),
-      m_lead_comm (lc), m_trail_comm (tc)
+  tree_complex_for_command (const token& for_tok, tree_argument_list *le, const token& eq_tok, tree_expression *re,
+                            tree_statement_list *body, const token& end_tok, int l = -1, int c = -1)
+    : tree_command (l, c), m_for_tok (for_tok), m_lhs (le), m_eq_tok (eq_tok), m_expr (re),
+      m_body (body), m_end_tok (end_tok)
   { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_complex_for_command)
@@ -230,11 +194,7 @@
 
   tree_expression * control_expr () { return m_expr; }
 
-  tree_statement_list * body () { return m_list; }
-
-  comment_list * leading_comment () { return m_lead_comm; }
-
-  comment_list * trailing_comment () { return m_trail_comm; }
+  tree_statement_list * body () { return m_body; }
 
   void accept (tree_walker& tw)
   {
@@ -243,20 +203,20 @@
 
 private:
 
+  token m_for_tok;
+
   // Expression to modify.
   tree_argument_list *m_lhs;
 
+  token m_eq_tok;
+
   // Expression to evaluate.
   tree_expression *m_expr;
 
   // List of commands to execute.
-  tree_statement_list *m_list;
+  tree_statement_list *m_body;
 
-  // Comment preceding FOR token.
-  comment_list *m_lead_comm;
-
-  // Comment preceding ENDFOR token.
-  comment_list *m_trail_comm;
+  token m_end_tok;
 };
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-misc.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-misc.h	Mon Apr 01 23:27:43 2024 -0400
@@ -73,6 +73,14 @@
 
   ~tree_parameter_list ();
 
+  tree_parameter_list * mark_in_delims (const token& open_delim, const token& close_delim)
+  {
+    m_open_delim = open_delim;
+    m_close_delim = close_delim;
+
+    return this;
+  }
+
   void mark_as_formal_parameters ();
 
   void mark_varargs () { m_marked_for_varargs = 1; }
@@ -109,6 +117,9 @@
   // -1: takes varargs only
   // 0: does not take varargs.
   int m_marked_for_varargs;
+
+  token m_open_delim;
+  token m_close_delim;
 };
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-pr-code.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-pr-code.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -238,7 +238,7 @@
 void
 tree_print_code::visit_simple_for_command (tree_simple_for_command& cmd)
 {
-  print_comment_list (cmd.leading_comment ());
+  print_comment_list (cmd.leading_comments ());
 
   indent ();
 
@@ -281,8 +281,6 @@
       decrement_indent_level ();
     }
 
-  print_indented_comment (cmd.trailing_comment ());
-
   indent ();
 
   m_os << (cmd.in_parallel () ? "endparfor" : "endfor");
@@ -291,7 +289,7 @@
 void
 tree_print_code::visit_complex_for_command (tree_complex_for_command& cmd)
 {
-  print_comment_list (cmd.leading_comment ());
+  print_comment_list (cmd.leading_comments ());
 
   indent ();
 
@@ -324,8 +322,6 @@
       decrement_indent_level ();
     }
 
-  print_indented_comment (cmd.trailing_comment ());
-
   indent ();
 
   m_os << "endfor";
@@ -334,7 +330,7 @@
 void
 tree_print_code::visit_spmd_command (tree_spmd_command& cmd)
 {
-  print_comment_list (cmd.leading_comment ());
+  print_comment_list (cmd.leading_comments ());
 
   indent ();
 
@@ -353,8 +349,6 @@
       decrement_indent_level ();
     }
 
-  print_indented_comment (cmd.trailing_comment ());
-
   indent ();
 
   m_os << "endspmd";
@@ -395,11 +389,11 @@
 void
 tree_print_code::visit_octave_user_function_header (octave_user_function& fcn)
 {
-  comment_list *leading_comment = fcn.leading_comment ();
+  comment_list leading_comments = fcn.leading_comments ();
 
-  if (leading_comment)
+  if (! leading_comments.empty ())
     {
-      print_comment_list (leading_comment);
+      print_comment_list (leading_comments);
       newline ();
     }
 
@@ -430,7 +424,7 @@
 void
 tree_print_code::visit_octave_user_function_trailer (octave_user_function& fcn)
 {
-  print_indented_comment (fcn.trailing_comment ());
+  print_indented_comment (fcn.trailing_comments ());
 
   newline ();
 }
@@ -486,7 +480,7 @@
 void
 tree_print_code::visit_if_command (tree_if_command& cmd)
 {
-  print_comment_list (cmd.leading_comment ());
+  print_comment_list (cmd.leading_comments ());
 
   indent ();
 
@@ -497,8 +491,6 @@
   if (list)
     list->accept (*this);
 
-  print_indented_comment (cmd.trailing_comment ());
-
   indent ();
 
   m_os << "endif";
@@ -519,7 +511,7 @@
         {
           if (! first_elt)
             {
-              print_indented_comment (elt->leading_comment ());
+              print_indented_comment (elt->leading_comments ());
 
               indent ();
 
@@ -566,7 +558,7 @@
         case '(':
           {
             char nc = m_nesting.top ();
-            if ((nc == '[' || nc == '{') && expr.paren_count () == 0)
+            if ((nc == '[' || nc == '{') && expr.delim_count () == 0)
               m_os << '(';
             else
               m_os << " (";
@@ -584,7 +576,7 @@
         case '{':
           {
             char nc = m_nesting.top ();
-            if ((nc == '[' || nc == '{') && expr.paren_count () == 0)
+            if ((nc == '[' || nc == '{') && expr.delim_count () == 0)
               m_os << '{';
             else
               m_os << " {";
@@ -896,7 +888,7 @@
 void
 tree_print_code::visit_statement (tree_statement& stmt)
 {
-  print_comment_list (stmt.comment_text ());
+  print_comment_list (stmt.leading_comments ());
 
   tree_command *cmd = stmt.command ();
 
@@ -938,7 +930,7 @@
 void
 tree_print_code::visit_switch_case (tree_switch_case& cs)
 {
-  print_comment_list (cs.leading_comment ());
+  print_comment_list (cs.leading_comments ());
 
   indent ();
 
@@ -971,7 +963,7 @@
 void
 tree_print_code::visit_switch_command (tree_switch_command& cmd)
 {
-  print_comment_list (cmd.leading_comment ());
+  print_comment_list (cmd.leading_comments ());
 
   indent ();
 
@@ -995,7 +987,7 @@
       decrement_indent_level ();
     }
 
-  print_indented_comment (cmd.leading_comment ());
+  print_indented_comment (cmd.leading_comments ());
 
   indent ();
 
@@ -1005,7 +997,7 @@
 void
 tree_print_code::visit_try_catch_command (tree_try_catch_command& cmd)
 {
-  print_comment_list (cmd.leading_comment ());
+  print_comment_list (cmd.leading_comments ());
 
   indent ();
 
@@ -1025,8 +1017,6 @@
       decrement_indent_level ();
     }
 
-  print_indented_comment (cmd.middle_comment ());
-
   indent ();
 
   m_os << "catch";
@@ -1050,8 +1040,6 @@
       decrement_indent_level ();
     }
 
-  print_indented_comment (cmd.trailing_comment ());
-
   indent ();
 
   m_os << "end_try_catch";
@@ -1060,7 +1048,7 @@
 void
 tree_print_code::visit_unwind_protect_command (tree_unwind_protect_command& cmd)
 {
-  print_comment_list (cmd.leading_comment ());
+  print_comment_list (cmd.leading_comments ());
 
   indent ();
 
@@ -1079,8 +1067,6 @@
       decrement_indent_level ();
     }
 
-  print_indented_comment (cmd.middle_comment ());
-
   indent ();
 
   m_os << "unwind_protect_cleanup";
@@ -1098,8 +1084,6 @@
       decrement_indent_level ();
     }
 
-  print_indented_comment (cmd.trailing_comment ());
-
   indent ();
 
   m_os << "end_unwind_protect";
@@ -1108,7 +1092,7 @@
 void
 tree_print_code::visit_while_command (tree_while_command& cmd)
 {
-  print_comment_list (cmd.leading_comment ());
+  print_comment_list (cmd.leading_comments ());
 
   indent ();
 
@@ -1132,8 +1116,6 @@
       decrement_indent_level ();
     }
 
-  print_indented_comment (cmd.trailing_comment ());
-
   indent ();
 
   m_os << "endwhile";
@@ -1142,7 +1124,7 @@
 void
 tree_print_code::visit_do_until_command (tree_do_until_command& cmd)
 {
-  print_comment_list (cmd.leading_comment ());
+  print_comment_list (cmd.leading_comments ());
 
   indent ();
 
@@ -1161,8 +1143,6 @@
       decrement_indent_level ();
     }
 
-  print_indented_comment (cmd.trailing_comment ());
-
   indent ();
 
   m_os << "until ";
@@ -1247,7 +1227,7 @@
 void
 tree_print_code::print_parens (const tree_expression& expr, const char *txt)
 {
-  int n = expr.paren_count ();
+  int n = expr.delim_count ();
 
   for (int i = 0; i < n; i++)
     m_os << txt;
@@ -1314,26 +1294,23 @@
 }
 
 void
-tree_print_code::print_comment_list (comment_list *comment_list)
+tree_print_code::print_comment_list (const comment_list& comment_list)
 {
-  if (comment_list)
-    {
-      auto p = comment_list->begin ();
+  auto p = comment_list.begin ();
 
-      while (p != comment_list->end ())
-        {
-          comment_elt elt = *p++;
+  while (p != comment_list.end ())
+    {
+      comment_elt elt = *p++;
 
-          print_comment_elt (elt);
+      print_comment_elt (elt);
 
-          if (p != comment_list->end ())
-            newline ();
-        }
+      if (p != comment_list.end ())
+        newline ();
     }
 }
 
 void
-tree_print_code::print_indented_comment (comment_list *comment_list)
+tree_print_code::print_indented_comment (const comment_list& comment_list)
 {
   increment_indent_level ();
 
--- a/libinterp/parse-tree/pt-pr-code.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-pr-code.h	Mon Apr 01 23:27:43 2024 -0400
@@ -195,11 +195,11 @@
 
   void print_parens (const tree_expression& expr, const char *txt);
 
-  void print_comment_list (comment_list *comment_list);
+  void print_comment_list (const comment_list& comment_list);
 
   void print_comment_elt (const comment_elt& comment_elt);
 
-  void print_indented_comment (comment_list *comment_list);
+  void print_indented_comment (const comment_list& comment_list);
 
   // Must create with an output stream!
 
--- a/libinterp/parse-tree/pt-select.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-select.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -39,7 +39,6 @@
 {
   delete m_expr;
   delete m_list;
-  delete m_lead_comm;
 }
 
 // If.
@@ -47,8 +46,6 @@
 tree_if_command::~tree_if_command ()
 {
   delete m_list;
-  delete m_lead_comm;
-  delete m_trail_comm;
 }
 
 // Switch cases.
@@ -57,7 +54,6 @@
 {
   delete m_label;
   delete m_list;
-  delete m_lead_comm;
 }
 
 // Switch.
@@ -66,8 +62,6 @@
 {
   delete m_expr;
   delete m_list;
-  delete m_lead_comm;
-  delete m_trail_comm;
 }
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-select.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-select.h	Mon Apr 01 23:27:43 2024 -0400
@@ -33,9 +33,11 @@
 #include "comment-list.h"
 #include "pt-cmd.h"
 #include "pt-walk.h"
+#include "token.h"
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
+class comment_list;
 class tree_expression;
 class tree_statement_list;
 
@@ -45,30 +47,23 @@
 {
 public:
 
-  tree_if_clause (int l = -1, int c = -1)
-    : tree (l, c), m_expr (nullptr), m_list (nullptr), m_lead_comm (nullptr)
+  tree_if_clause (const token& tok, tree_expression *e, tree_statement_list *sl, int l = -1, int c = -1)
+    : tree (l, c), m_tok (tok), m_expr (e), m_list (sl)
   { }
 
-  tree_if_clause (tree_statement_list *sl, comment_list *lc = nullptr,
-                  int l = -1, int c = -1)
-    : tree (l, c), m_expr (nullptr), m_list (sl), m_lead_comm (lc) { }
-
-  tree_if_clause (tree_expression *e, tree_statement_list *sl,
-                  comment_list *lc = nullptr,
-                  int l = -1, int c = -1)
-    : tree (l, c), m_expr (e), m_list (sl), m_lead_comm (lc) { }
-
-  OCTAVE_DISABLE_COPY_MOVE (tree_if_clause)
+  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_if_clause)
 
   ~tree_if_clause ();
 
+  token if_token () const { return m_tok; }
+
   bool is_else_clause () { return ! m_expr; }
 
   tree_expression * condition () { return m_expr; }
 
   tree_statement_list * commands () { return m_list; }
 
-  comment_list * leading_comment () { return m_lead_comm; }
+  comment_list leading_comments () const { return m_tok.leading_comments (); }
 
   void accept (tree_walker& tw)
   {
@@ -77,14 +72,13 @@
 
 private:
 
+  token m_tok;
+
   // The condition to test.
-  tree_expression *m_expr;
+  tree_expression *m_expr = nullptr;
 
   // The list of statements to evaluate if expr is true.
   tree_statement_list *m_list;
-
-  // Comment preceding ELSE or ELSEIF token.
-  comment_list *m_lead_comm;
 };
 
 class tree_if_command_list : public std::list<tree_if_clause *>
@@ -107,6 +101,17 @@
       }
   }
 
+  token if_token () const
+  {
+    if (! empty ())
+      {
+        tree_if_clause *p = front ();
+        return p->if_token ();
+      }
+
+    return token ();
+  }
+
   void accept (tree_walker& tw)
   {
     tw.visit_if_command_list (*this);
@@ -117,25 +122,21 @@
 {
 public:
 
-  tree_if_command (int l = -1, int c = -1)
-    : tree_command (l, c), m_list (nullptr),
-      m_lead_comm (nullptr), m_trail_comm (nullptr)
+  tree_if_command (const token& if_tok, const token& end_tok, int l = -1, int c = -1)
+    : tree_command (l, c), m_if_tok (if_tok), m_end_tok (end_tok)
   { }
 
-  tree_if_command (tree_if_command_list *lst, comment_list *lc,
-                   comment_list *tc, int l = -1, int c = -1)
-    : tree_command (l, c), m_list (lst), m_lead_comm (lc), m_trail_comm (tc)
+  tree_if_command (const token& if_tok, tree_if_command_list *lst, const token& end_tok, int l = -1, int c = -1)
+    : tree_command (l, c), m_if_tok (if_tok), m_list (lst), m_end_tok (end_tok)
   { }
 
-  OCTAVE_DISABLE_COPY_MOVE (tree_if_command)
+  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_if_command)
 
   ~tree_if_command ();
 
   tree_if_command_list * cmd_list () { return m_list; }
 
-  comment_list * leading_comment () { return m_lead_comm; }
-
-  comment_list * trailing_comment () { return m_trail_comm; }
+  comment_list leading_comments () const { return m_if_tok.leading_comments (); }
 
   void accept (tree_walker& tw)
   {
@@ -144,14 +145,12 @@
 
 private:
 
-  // List of if commands (if, elseif, elseif, ... else, endif)
-  tree_if_command_list *m_list;
+  token m_if_tok;
 
-  // Comment preceding IF token.
-  comment_list *m_lead_comm;
+  // List of if commands (if, elseif, elseif, ... else, endif)
+  tree_if_command_list *m_list = nullptr;
 
-  // Comment preceding ENDIF token.
-  comment_list *m_trail_comm;
+  token m_end_tok;
 };
 
 // Switch.
@@ -160,20 +159,15 @@
 {
 public:
 
-  tree_switch_case (int l = -1, int c = -1)
-    : tree (l, c), m_label (nullptr), m_list (nullptr), m_lead_comm (nullptr)
+  tree_switch_case (const token& tok, tree_statement_list *sl, int l = -1, int c = -1)
+    : tree (l, c), m_tok (tok), m_list (sl)
   { }
 
-  tree_switch_case (tree_statement_list *sl, comment_list *lc = nullptr,
-                    int l = -1, int c = -1)
-    : tree (l, c), m_label (nullptr), m_list (sl), m_lead_comm (lc) { }
+  tree_switch_case (const token& tok, tree_expression *e, tree_statement_list *sl, int l = -1, int c = -1)
+    : tree (l, c), m_tok (tok), m_label (e), m_list (sl)
+  { }
 
-  tree_switch_case (tree_expression *e, tree_statement_list *sl,
-                    comment_list *lc = nullptr,
-                    int l = -1, int c = -1)
-    : tree (l, c), m_label (e), m_list (sl), m_lead_comm (lc) { }
-
-  OCTAVE_DISABLE_COPY_MOVE (tree_switch_case)
+  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_switch_case)
 
   ~tree_switch_case ();
 
@@ -183,7 +177,7 @@
 
   tree_statement_list * commands () { return m_list; }
 
-  comment_list * leading_comment () { return m_lead_comm; }
+  comment_list leading_comments () const { return m_tok.leading_comments (); }
 
   void accept (tree_walker& tw)
   {
@@ -192,14 +186,13 @@
 
 private:
 
+  token m_tok;
+
   // The case label.
-  tree_expression *m_label;
+  tree_expression *m_label = nullptr;
 
   // The list of statements to evaluate if the label matches.
   tree_statement_list *m_list;
-
-  // Comment preceding CASE or OTHERWISE token.
-  comment_list *m_lead_comm;
 };
 
 class tree_switch_case_list : public std::list<tree_switch_case *>
@@ -232,17 +225,11 @@
 {
 public:
 
-  tree_switch_command (int l = -1, int c = -1)
-    : tree_command (l, c), m_expr (nullptr), m_list (nullptr),
-      m_lead_comm (nullptr), m_trail_comm (nullptr) { }
+  tree_switch_command (const token& switch_tok, tree_expression *e, tree_switch_case_list *lst, const token& end_tok, int l = -1, int c = -1)
+    : tree_command (l, c), m_switch_tok (switch_tok), m_expr (e), m_list (lst), m_end_tok (end_tok)
+  { }
 
-  tree_switch_command (tree_expression *e, tree_switch_case_list *lst,
-                       comment_list *lc, comment_list *tc,
-                       int l = -1, int c = -1)
-    : tree_command (l, c), m_expr (e), m_list (lst), m_lead_comm (lc),
-      m_trail_comm (tc) { }
-
-  OCTAVE_DISABLE_COPY_MOVE (tree_switch_command)
+  OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_switch_command)
 
   ~tree_switch_command ();
 
@@ -250,9 +237,7 @@
 
   tree_switch_case_list * case_list () { return m_list; }
 
-  comment_list * leading_comment () { return m_lead_comm; }
-
-  comment_list * trailing_comment () { return m_trail_comm; }
+  comment_list leading_comments () const { return m_switch_tok.leading_comments (); }
 
   void accept (tree_walker& tw)
   {
@@ -261,17 +246,15 @@
 
 private:
 
+  token m_switch_tok;
+
   // Value on which to switch.
   tree_expression *m_expr;
 
   // List of cases (case 1, case 2, ..., default)
   tree_switch_case_list *m_list;
 
-  // Comment preceding SWITCH token.
-  comment_list *m_lead_comm;
-
-  // Comment preceding ENDSWITCH token.
-  comment_list *m_trail_comm;
+  token m_end_tok;
 };
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-spmd.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-spmd.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -36,8 +36,6 @@
 tree_spmd_command::~tree_spmd_command ()
 {
   delete m_body;
-  delete m_lead_comm;
-  delete m_trail_comm;
 }
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-spmd.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-spmd.h	Mon Apr 01 23:27:43 2024 -0400
@@ -33,7 +33,7 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
-class tree_comment_list;
+class comment_list;
 class tree_statement_list;
 
 // Spmd.
@@ -42,9 +42,8 @@
 {
 public:
 
-  tree_spmd_command (tree_statement_list *body, comment_list *lc,
-                     comment_list *tc, int l = -1, int c = -1)
-    : tree_command (l, c), m_body (body), m_lead_comm (lc), m_trail_comm (tc)
+  tree_spmd_command (tree_statement_list *body, int l = -1, int c = -1)
+    : tree_command (l, c), m_body (body)
   { }
 
   OCTAVE_DISABLE_CONSTRUCT_COPY_MOVE (tree_spmd_command)
@@ -53,10 +52,6 @@
 
   tree_statement_list * body () { return m_body; }
 
-  comment_list * leading_comment () { return m_lead_comm; }
-
-  comment_list * trailing_comment () { return m_trail_comm; }
-
   void accept (tree_walker& tw)
   {
     tw.visit_spmd_command (*this);
@@ -66,12 +61,6 @@
 
   // List of commands.
   tree_statement_list *m_body;
-
-  // Comment preceding SPMD token.
-  comment_list *m_lead_comm;
-
-  // Comment preceding ENDSPMD token.
-  comment_list *m_trail_comm;
 };
 
 OCTAVE_END_NAMESPACE(octave)
--- a/libinterp/parse-tree/pt-stmt.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-stmt.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -57,7 +57,6 @@
 {
   delete m_command;
   delete m_expression;
-  delete m_comment_list;
 }
 
 void
@@ -107,6 +106,14 @@
             : false);
 }
 
+comment_list
+tree_statement::leading_comments () const
+{
+  return (m_command
+          ? m_command->leading_comments ()
+          : m_expression->leading_comments ());
+}
+
 std::string
 tree_statement::bp_cond () const
 {
@@ -182,6 +189,20 @@
   return retval;
 }
 
+comment_list
+tree_statement_list::leading_comments () const
+{
+  if (! empty ())
+    {
+      tree_statement *elt = front ();
+
+      if (elt)
+        return elt->leading_comments ();
+    }
+
+  return comment_list ();
+}
+
 // Create a "breakpoint" tree-walker, and get it to "walk" this
 // statement list
 // (FIXME: What does that do???)
--- a/libinterp/parse-tree/pt-stmt.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-stmt.h	Mon Apr 01 23:27:43 2024 -0400
@@ -54,14 +54,16 @@
 public:
 
   tree_statement ()
-    : m_command (nullptr), m_expression (nullptr),
-      m_comment_list (nullptr) { }
+    : m_command (nullptr), m_expression (nullptr)
+  { }
 
-  tree_statement (tree_command *c, comment_list *cl)
-    : m_command (c), m_expression (nullptr), m_comment_list (cl) { }
+  tree_statement (tree_command *c)
+    : m_command (c), m_expression (nullptr)
+  { }
 
-  tree_statement (tree_expression *e, comment_list *cl)
-    : m_command (nullptr), m_expression (e), m_comment_list (cl) { }
+  tree_statement (tree_expression *e)
+    : m_command (nullptr), m_expression (e)
+  { }
 
   OCTAVE_DISABLE_COPY_MOVE (tree_statement)
 
@@ -83,6 +85,8 @@
 
   bool is_active_breakpoint (tree_evaluator& tw) const;
 
+  comment_list leading_comments () const;
+
   std::string bp_cond () const;
 
   int line () const;
@@ -96,11 +100,9 @@
 
   tree_expression * expression () { return m_expression; }
 
-  comment_list * comment_text () { return m_comment_list; }
-
   bool is_null_statement () const
   {
-    return ! (m_command || m_expression || m_comment_list);
+    return ! (m_command || m_expression);
   }
 
   bool is_end_of_fcn_or_script () const;
@@ -129,9 +131,6 @@
 
   // Expression to evaluate.
   tree_expression *m_expression;
-
-  // Comment associated with this statement.
-  comment_list *m_comment_list;
 };
 
 // A list of statements to evaluate.
@@ -172,6 +171,8 @@
 
   bool is_script_body () const { return m_script_body; }
 
+  comment_list leading_comments () const;
+
   int set_breakpoint (int line, const std::string& condition);
 
   void delete_breakpoint (int line);
--- a/libinterp/parse-tree/pt-walk.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-walk.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -722,14 +722,14 @@
 void
 tree_walker::visit_classdef_properties_block (tree_classdef_properties_block& blk)
 {
-  tree_classdef_property_list *property_list = blk.element_list ();
+  tree_classdef_property_list *property_list = blk.property_list ();
 
   if (property_list)
     property_list->accept (*this);
 }
 
 void
-tree_walker::visit_classdef_methods_list (tree_classdef_methods_list& lst)
+tree_walker::visit_classdef_method_list (tree_classdef_method_list& lst)
 {
   for (auto ov_meth : lst)
     {
@@ -743,10 +743,10 @@
 void
 tree_walker::visit_classdef_methods_block (tree_classdef_methods_block& blk)
 {
-  tree_classdef_methods_list *methods_list = blk.element_list ();
+  tree_classdef_method_list *method_list = blk.method_list ();
 
-  if (methods_list)
-    methods_list->accept (*this);
+  if (method_list)
+    method_list->accept (*this);
 }
 
 void
@@ -756,7 +756,7 @@
 }
 
 void
-tree_walker::visit_classdef_events_list (tree_classdef_events_list& lst)
+tree_walker::visit_classdef_event_list (tree_classdef_event_list& lst)
 {
   for (auto *elt : lst)
     {
@@ -768,10 +768,10 @@
 void
 tree_walker::visit_classdef_events_block (tree_classdef_events_block& blk)
 {
-  tree_classdef_events_list *events_list = blk.element_list ();
+  tree_classdef_event_list *event_list = blk.event_list ();
 
-  if (events_list)
-    events_list->accept (*this);
+  if (event_list)
+    event_list->accept (*this);
 }
 
 void
@@ -793,7 +793,7 @@
 void
 tree_walker::visit_classdef_enum_block (tree_classdef_enum_block& blk)
 {
-  tree_classdef_enum_list *enum_list = blk.element_list ();
+  tree_classdef_enum_list *enum_list = blk.enum_list ();
 
   if (enum_list)
     enum_list->accept (*this);
@@ -802,20 +802,20 @@
 void
 tree_walker::visit_classdef_body (tree_classdef_body& body)
 {
-  for (auto *elt : body.properties_list ())
+  for (auto *elt : body.property_list ())
     {
       if (elt)
         elt->accept (*this);
     }
 
-  for (auto *elt : body.methods_list ())
+  for (auto *elt : body.method_list ())
     {
       if (elt)
         elt->accept (*this);
     }
 
 
-  for (auto *elt : body.events_list ())
+  for (auto *elt : body.event_list ())
     {
       if (elt)
         elt->accept (*this);
--- a/libinterp/parse-tree/pt-walk.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt-walk.h	Mon Apr 01 23:27:43 2024 -0400
@@ -96,10 +96,10 @@
 class tree_classdef_property;
 class tree_classdef_property_list;
 class tree_classdef_properties_block;
-class tree_classdef_methods_list;
+class tree_classdef_method_list;
 class tree_classdef_methods_block;
 class tree_classdef_event;
-class tree_classdef_events_list;
+class tree_classdef_event_list;
 class tree_classdef_events_block;
 class tree_classdef_enum;
 class tree_classdef_enum_list;
@@ -233,13 +233,13 @@
 
   virtual void visit_classdef_properties_block (tree_classdef_properties_block&);
 
-  virtual void visit_classdef_methods_list (tree_classdef_methods_list&);
+  virtual void visit_classdef_method_list (tree_classdef_method_list&);
 
   virtual void visit_classdef_methods_block (tree_classdef_methods_block&);
 
   virtual void visit_classdef_event (tree_classdef_event&);
 
-  virtual void visit_classdef_events_list (tree_classdef_events_list&);
+  virtual void visit_classdef_event_list (tree_classdef_event_list&);
 
   virtual void visit_classdef_events_block (tree_classdef_events_block&);
 
--- a/libinterp/parse-tree/pt.cc	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt.cc	Mon Apr 01 23:27:43 2024 -0400
@@ -30,6 +30,7 @@
 #include <sstream>
 #include <string>
 
+#include "comment-list.h"
 #include "interpreter.h"
 #include "ov-fcn.h"
 #include "pt.h"
@@ -39,6 +40,12 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
+comment_list
+tree::leading_comments () const
+{
+  return comment_list ();
+}
+
 // Hide the details of the string buffer so that we are less likely to
 // create a memory leak.
 
--- a/libinterp/parse-tree/pt.h	Tue Apr 02 13:25:59 2024 -0400
+++ b/libinterp/parse-tree/pt.h	Mon Apr 01 23:27:43 2024 -0400
@@ -36,6 +36,7 @@
 
 OCTAVE_BEGIN_NAMESPACE(octave)
 
+class comment_list;
 class tree_evaluator;
 class tree_walker;
 
@@ -67,6 +68,9 @@
     m_column_num = c;
   }
 
+  // FIXME: maybe make this a pure virtual function?
+  virtual comment_list leading_comments () const;
+
   virtual void set_breakpoint (const std::string& condition)
   {
     if (m_bp_cond)