changeset 16629:de91b1621260

adjust location of eof marker for files with subfunctions but no explicit end statements * ov-fcn.h (octave_function::maybe_relocate_end, octave_function::has_subfunctions, octave_function::stash_subfunction_names, octave_function::subfunction_names): New virtual functions. * ov-usr-fcn.h, ov-usr-fcn.cc (octave_user_function::end_location_line, octave_user_function::end_location_column, octave_user_function::subfcn_names): New member variables. (octave_user_function::beginning_line, octave_user_function::beginning_column, octave_user_function::stash_fcn_end_location, octave_user_function::ending_line, octave_user_function::ending_column, octave_user_function::has_subfunctions, octave_user_function::subfunction_names, octave_user_function::stash_subfunction_names, octave_user_function::maybe_relocate_end_internal, octave_user_function::maybe_relocate_end): New functions. * oct-parse.in.yy (FCN): Declare as token with tok_val type. (function_beg): Declare as tok_val type. Return FCN value. (function): Move stash_comment here from function_beg. Pass line and column info to parser.finish_function. (octave_base_parser::subfunction_names): New data member. (octave_base_parser::reset): Clear subfuntction_names. (octave_base_parser::start_function): Call stash_fcn_end_location. (octave_base_parser::frob_function): Don't call stash_fcn_location. (octave_base_parser_::finish_function): Call stash_fcn_location. Append name to subfunction_names. Call maybe_relocate_end on constructed function. (parse_fcn_file): Attach subfunction names to primary function. * pt-stmt.h, pt-stmt.cc (tree_statement::set_location): New function. * pt.h (tree::set_location): New function. * pt-cmd.h, pt-cmd.cc (tree_no_op_command::tree_no_op_command): Use EOF member variable to track auto-generated end statements that appear at the end of file. Change all callers. (tree_command::is_end_of_file): New function. * pt-stmt.h, pt-stmt.cc (tree_statement::set_location, tree_statement::is_end_of_file): New functions. * base-list.h (octave_base_list::reverse_iterator, octave_base_list::const_reverse_iterator): New typedefs. (octave_base_list::rbegin, octave_base_list::rend): New functions. * debug.h, debug.cc (bp_table::do_add_breakpoint): Handle subfunctions. (bp_table::do_remove_breakpoint_1): New function. (bp_table::do_remove_breakpoint): Use it. Handle subfunctions. (bp_table::do_remove_all_breakpoints_in_file_1): New function. (bp_table::do_remove_all_breakpoints_in_file): Use it. Handle subfunctions.
author John W. Eaton <jwe@octave.org>
date Tue, 07 May 2013 15:29:00 -0400
parents 4adf3c4bd80b
children a66c285729a6
files libinterp/interpfcn/debug.cc libinterp/interpfcn/debug.h libinterp/octave-value/ov-fcn.h libinterp/octave-value/ov-usr-fcn.cc libinterp/octave-value/ov-usr-fcn.h libinterp/parse-tree/oct-parse.in.yy libinterp/parse-tree/parse.h libinterp/parse-tree/pt-cmd.cc libinterp/parse-tree/pt-cmd.h libinterp/parse-tree/pt-stmt.cc libinterp/parse-tree/pt-stmt.h libinterp/parse-tree/pt.h liboctave/util/base-list.h
diffstat 13 files changed, 351 insertions(+), 71 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/interpfcn/debug.cc
+++ b/libinterp/interpfcn/debug.cc
@@ -316,19 +316,27 @@
     {
       if (! do_add_breakpoint_1 (dbg_fcn, fname, line, retval))
         {
-          typedef std::map<std::string, octave_value>::const_iterator
-            subfunction_map_const_iterator;
+          // Search subfunctions in the order they appear in the file.
+
+          const std::list<std::string> subfcn_names
+            = dbg_fcn->subfunction_names ();
 
           std::map<std::string, octave_value> subfcns
             = dbg_fcn->subfunctions ();
 
-          for (subfunction_map_const_iterator p = subfcns.begin ();
-               p != subfcns.end (); p++)
+          for (std::list<std::string>::const_iterator p = subfcn_names.begin ();
+               p != subfcn_names.end (); p++)
             {
-              octave_user_code *dbg_subfcn = p->second.user_code_value ();
+              std::map<std::string, octave_value>::const_iterator
+                q = subfcns.find (*p);
 
-              if (do_add_breakpoint_1 (dbg_subfcn, fname, line, retval))
-                break;
+              if (q != subfcns.end ())
+                {
+                  octave_user_code *dbg_subfcn = q->second.user_code_value ();
+
+                  if (do_add_breakpoint_1 (dbg_subfcn, fname, line, retval))
+                    break;
+                }
             }
         }
     }
@@ -342,6 +350,56 @@
 
 
 int
+bp_table::do_remove_breakpoint_1 (octave_user_code *fcn,
+                                  const std::string& fname,
+                                  const bp_table::intmap& line)
+{
+  int retval = 0;
+
+  std::string file = fcn->fcn_file_name ();
+
+  tree_statement_list *cmds = fcn->body ();
+
+  // FIXME -- move the operation on cmds to the
+  // tree_statement_list class?
+
+  if (cmds)
+    {
+      octave_value_list results = cmds->list_breakpoints ();
+
+      if (results.length () > 0)
+        {
+          octave_idx_type len = line.size ();
+
+          for (int i = 0; i < len; i++)
+            {
+              const_intmap_iterator p = line.find (i);
+
+              if (p != line.end ())
+                {
+                  int lineno = p->second;
+
+                  cmds->delete_breakpoint (lineno);
+
+                  if (! file.empty ())
+                    octave_link::update_breakpoint (false, file, lineno);
+                }
+            }
+
+          results = cmds->list_breakpoints ();
+
+          bp_set_iterator it = bp_set.find (fname);
+          if (results.length () == 0 && it != bp_set.end ())
+            bp_set.erase (it);
+        }
+
+      retval = results.length ();
+    }
+
+  return retval;
+}
+
+int
 bp_table::do_remove_breakpoint (const std::string& fname,
                                 const bp_table::intmap& line)
 {
@@ -360,41 +418,28 @@
 
       if (dbg_fcn)
         {
-          std::string file = dbg_fcn->fcn_file_name ();
+          retval = do_remove_breakpoint_1 (dbg_fcn, fname, line);
 
-          tree_statement_list *cmds = dbg_fcn->body ();
+          // Search subfunctions in the order they appear in the file.
 
-          // FIXME -- move the operation on cmds to the
-          // tree_statement_list class?
-          if (cmds)
-            {
-              octave_value_list results = cmds->list_breakpoints ();
+          const std::list<std::string> subfcn_names
+            = dbg_fcn->subfunction_names ();
 
-              if (results.length () > 0)
-                {
-                  for (int i = 0; i < len; i++)
-                    {
-                      const_intmap_iterator p = line.find (i);
+          std::map<std::string, octave_value> subfcns
+            = dbg_fcn->subfunctions ();
 
-                      if (p != line.end ())
-                        {
-                          int lineno = p->second;
-
-                          cmds->delete_breakpoint (lineno);
+          for (std::list<std::string>::const_iterator p = subfcn_names.begin ();
+               p != subfcn_names.end (); p++)
+            {
+              std::map<std::string, octave_value>::const_iterator
+                q = subfcns.find (*p);
 
-                          if (! file.empty ())
-                            octave_link::update_breakpoint (false, file, lineno);
-                        }
-                    }
-
-                  results = cmds->list_breakpoints ();
+              if (q != subfcns.end ())
+                {
+                  octave_user_code *dbg_subfcn = q->second.user_code_value ();
 
-                  bp_set_iterator it = bp_set.find (fname);
-                  if (results.length () == 0 && it != bp_set.end ())
-                    bp_set.erase (it);
+                  retval += do_remove_breakpoint_1 (dbg_subfcn, fname, line);
                 }
-
-              retval = results.length ();
             }
         }
       else
@@ -406,6 +451,27 @@
   return retval;
 }
 
+bp_table::intmap
+bp_table::do_remove_all_breakpoints_in_file_1 (octave_user_code *fcn,
+                                               const std::string& fname)
+{
+  intmap retval;
+
+  std::string file = fcn->fcn_file_name ();
+
+  tree_statement_list *cmds = fcn->body ();
+
+  if (cmds)
+    {
+      retval = cmds->remove_all_breakpoints (file);
+
+      bp_set_iterator it = bp_set.find (fname);
+      if (it != bp_set.end ())
+        bp_set.erase (it);
+    }
+
+  return retval;
+}
 
 bp_table::intmap
 bp_table::do_remove_all_breakpoints_in_file (const std::string& fname,
@@ -417,17 +483,24 @@
 
   if (dbg_fcn)
     {
-      std::string file = dbg_fcn->fcn_file_name ();
+      retval = do_remove_all_breakpoints_in_file_1 (dbg_fcn, fname);
+
+      // Order is not important here.
 
-      tree_statement_list *cmds = dbg_fcn->body ();
+      typedef std::map<std::string, octave_value>::const_iterator
+        subfcns_const_iterator;
 
-      if (cmds)
+      std::map<std::string, octave_value> subfcns = dbg_fcn->subfunctions ();
+
+      for (subfcns_const_iterator p = subfcns.begin ();
+           p != subfcns.end (); p++)
         {
-          retval = cmds->remove_all_breakpoints (file);
+          octave_user_code *dbg_subfcn = p->second.user_code_value ();
 
-          bp_set_iterator it = bp_set.find (fname);
-          if (it != bp_set.end ())
-            bp_set.erase (it);
+          intmap tmp = do_remove_all_breakpoints_in_file_1 (dbg_subfcn, fname);
+
+          // Merge new list with retval.
+          retval.insert (tmp.begin (), tmp.end ());
         }
     }
   else if (! silent)
--- a/libinterp/interpfcn/debug.h
+++ b/libinterp/interpfcn/debug.h
@@ -120,8 +120,14 @@
 
   intmap do_add_breakpoint (const std::string& fname, const intmap& lines);
 
+  int do_remove_breakpoint_1 (octave_user_code *fcn, const std::string&,
+                              const intmap& lines);
+
   int do_remove_breakpoint (const std::string&, const intmap& lines);
 
+  intmap do_remove_all_breakpoints_in_file_1 (octave_user_code *fcn,
+                                              const std::string& fname);
+
   intmap do_remove_all_breakpoints_in_file (const std::string& fname,
                                             bool silent);
 
--- a/libinterp/octave-value/ov-fcn.h
+++ b/libinterp/octave-value/ov-fcn.h
@@ -134,6 +134,18 @@
 
   virtual void unlock_subfunctions (void) { }
 
+  virtual void maybe_relocate_end (void) { }
+
+  // Not valid until after the function is completley parsed.
+  virtual bool has_subfunctions (void) const { return false; }
+
+  virtual void stash_subfunction_names (const std::list<std::string>&) { }
+
+  virtual std::list<std::string> subfunction_names (void) const
+  {
+    return std::list<std::string> ();
+  }
+
   void mark_relative (void) { relative = true; }
 
   bool is_relative (void) const { return relative; }
--- a/libinterp/octave-value/ov-usr-fcn.cc
+++ b/libinterp/octave-value/ov-usr-fcn.cc
@@ -248,6 +248,70 @@
   file_name = nm;
 }
 
+// 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
+// the next line after the function keyword if there are no statements.
+// More precisely, the new location should probably be on the next line
+// after the end of the parameter list, but we aren't tracking that
+// information (yet).
+
+void
+octave_user_function::maybe_relocate_end_internal (void)
+{
+  if (cmd_list && ! cmd_list->empty ())
+    {
+      tree_statement *last_stmt = cmd_list->back ();
+
+      if (last_stmt && last_stmt->is_end_of_fcn_or_script ()
+          && last_stmt->is_end_of_file ())
+        {
+          tree_statement_list::reverse_iterator
+            next_to_last_elt = cmd_list->rbegin ();
+
+          next_to_last_elt++;
+
+          int new_eof_line;
+          int new_eof_col;
+
+          if (next_to_last_elt == cmd_list->rend ())
+            {
+              new_eof_line = beginning_line ();
+              new_eof_col = beginning_column ();
+            }
+          else
+            {
+              tree_statement *next_to_last_stmt = *next_to_last_elt;
+
+              new_eof_line = next_to_last_stmt->line ();
+              new_eof_col = next_to_last_stmt->column ();
+            }
+
+          last_stmt->set_location (new_eof_line + 1, new_eof_col);
+        }
+    }
+}
+
+void
+octave_user_function::maybe_relocate_end (void)
+{
+  std::map<std::string, octave_value> fcns = subfunctions ();
+
+  if (! fcns.empty ())
+    {
+      for (std::map<std::string, octave_value>::iterator p = fcns.begin ();
+           p != fcns.end (); p++)
+        {
+          octave_user_function *f = (p->second).user_function_value ();
+
+          if (f)
+            f->maybe_relocate_end_internal ();
+        }
+    }
+
+  maybe_relocate_end_internal ();
+}
+
 std::string
 octave_user_function::profiler_name (void) const
 {
@@ -320,6 +384,19 @@
   return symbol_table::subfunctions_defined_in_scope (local_scope);
 }
 
+bool
+octave_user_function::has_subfunctions (void) const
+{
+  return ! subfcn_names.empty ();
+}
+
+void
+octave_user_function::stash_subfunction_names
+  (const std::list<std::string>& names)
+{
+  subfcn_names = names;
+}
+
 octave_value_list
 octave_user_function::all_va_args (const octave_value_list& args)
 {
--- a/libinterp/octave-value/ov-usr-fcn.h
+++ b/libinterp/octave-value/ov-usr-fcn.h
@@ -208,6 +208,20 @@
       location_column = col;
     }
 
+  int beginning_line (void) const { return location_line; }
+  int beginning_column (void) const { return location_column; }
+
+  void stash_fcn_end_location (int line, int col)
+    {
+      end_location_line = line;
+      end_location_column = col;
+    }
+
+  int ending_line (void) const { return end_location_line; }
+  int ending_column (void) const { return end_location_column; }
+
+  void maybe_relocate_end (void);
+
   void stash_parent_fcn_name (const std::string& p) { parent_name = p; }
 
   void stash_parent_fcn_scope (symbol_table::scope_id ps) { parent_scope = ps; }
@@ -266,6 +280,15 @@
 
   std::map<std::string, octave_value> subfunctions (void) const;
 
+  bool has_subfunctions (void) const;
+
+  void stash_subfunction_names (const std::list<std::string>& names);
+
+  std::list<std::string> subfunction_names (void) const
+  {
+    return subfcn_names;
+  }
+
   octave_value_list all_va_args (const octave_value_list& args);
 
   void stash_function_name (const std::string& s) { my_name = s; }
@@ -406,10 +429,16 @@
   // Location where this function was defined.
   int location_line;
   int location_column;
+  int end_location_line;
+  int end_location_column;
 
   // The name of the parent function, if any.
   std::string parent_name;
 
+  // The list of subfunctions (if any) in the order they appear in the
+  // file.
+  std::list<std::string> subfcn_names;
+
   // The time the file was parsed.
   octave_time t_parsed;
 
@@ -458,6 +487,8 @@
   jit_function_info *jit_info;
 #endif
 
+  void maybe_relocate_end_internal (void);
+
   void print_code_function_header (void);
 
   void print_code_function_trailer (void);
--- a/libinterp/parse-tree/oct-parse.in.yy
+++ b/libinterp/parse-tree/oct-parse.in.yy
@@ -219,14 +219,16 @@
 %token <tok_val> METAQUERY
 %token <tok_val> SUPERCLASSREF
 %token <tok_val> GET SET
+%token <tok_val> FCN
 
 // Other tokens.
 %token END_OF_INPUT LEXICAL_ERROR
-%token FCN INPUT_FILE CLASSDEF
+%token INPUT_FILE CLASSDEF
 // %token VARARGIN VARARGOUT
 
 // Nonterminals we construct.
-%type <comment_type> stash_comment function_beg classdef_beg
+%type <tok_val> function_beg
+%type <comment_type> stash_comment classdef_beg
 %type <comment_type> properties_beg methods_beg events_beg enum_beg
 %type <sep_type> sep_no_nl opt_sep_no_nl nl opt_nl sep opt_sep
 %type <tree_type> input
@@ -1119,9 +1121,9 @@
                     if (! lexer.reading_fcn_file)
                       {
                         tree_statement *end_of_script
-                          = parser.make_end ("endscript",
-                                                  lexer.input_line_number,
-                                                  lexer.current_input_column);
+                          = parser.make_end ("endscript", true,
+                                             lexer.input_line_number,
+                                             lexer.current_input_column);
 
                         parser.make_script ($3, end_of_script);
                       }
@@ -1134,23 +1136,25 @@
 // Function definition
 // ===================
 
-function_beg    : push_fcn_symtab FCN stash_comment
+function_beg    : push_fcn_symtab FCN
                   {
-                    $$ = $3;
+                    $$ = $2;
                     if (lexer.reading_classdef_file
                         || lexer.parsing_classdef)
                       lexer.maybe_classdef_get_set_method = true;
                   }
                 ;
 
-function        : function_beg function1
+function        : function_beg stash_comment function1
                   {
-                    $$ = parser.finish_function (0, $2, $1);
+                    $$ = parser.finish_function (0, $3, $2, $1->line (),
+                                                 $1->column ());
                     parser.recover_from_parsing_function ();
                   }
-                | function_beg return_list '=' function1
+                | function_beg stash_comment return_list '=' function1
                   {
-                    $$ = parser.finish_function ($2, $4, $1);
+                    $$ = parser.finish_function ($3, $5, $2, $1->line (),
+                                                 $1->column ());
                     parser.recover_from_parsing_function ();
                   }
                 ;
@@ -1199,7 +1203,8 @@
                   {
                     parser.endfunction_found = true;
                     if (parser.end_token_ok ($1, token::function_end))
-                      $$ = parser.make_end ("endfunction", $1->line (), $1->column ());
+                      $$ = parser.make_end ("endfunction", false,
+                                            $1->line (), $1->column ());
                     else
                       ABORT_PARSE;
                   }
@@ -1233,9 +1238,9 @@
                         YYABORT;
                       }
 
-                    $$ = parser.make_end ("endfunction",
-                                                lexer.input_line_number,
-                                                lexer.current_input_column);
+                    $$ = parser.make_end ("endfunction", true,
+                                          lexer.input_line_number,
+                                          lexer.current_input_column);
                   }
                 ;
 
@@ -1255,7 +1260,8 @@
                     lexer.parsing_classdef = false;
 
                     if (parser.end_token_ok ($1, token::classdef_end))
-                      $$ = parser.make_end ("endclassdef", $1->line (), $1->column ());
+                      $$ = parser.make_end ("endclassdef", false,
+                                            $1->line (), $1->column ());
                     else
                       ABORT_PARSE;
                   }
@@ -1494,6 +1500,7 @@
   curr_class_name = "";
   function_scopes.clear ();
   primary_fcn_ptr  = 0;
+  subfunction_names.clear ();
 
   delete stmt_list;
   stmt_list = 0;
@@ -2598,15 +2605,17 @@
       octave_comment_list *tc = octave_comment_buffer::get_comment ();
 
       fcn->stash_trailing_comment (tc);
+      fcn->stash_fcn_end_location (end_fcn_stmt->line (),
+                                   end_fcn_stmt->column ());
     }
 
   return fcn;
 }
 
 tree_statement *
-octave_base_parser::make_end (const std::string& type, int l, int c)
+octave_base_parser::make_end (const std::string& type, bool eof, int l, int c)
 {
-  return make_statement (new tree_no_op_command (type, l, c));
+  return make_statement (new tree_no_op_command (type, eof, l, c));
 }
 
 // Do most of the work for defining a function.
@@ -2695,8 +2704,6 @@
     }
 
   fcn->stash_function_name (id_name);
-  fcn->stash_fcn_location (lexer.input_line_number,
-                           lexer.current_input_column);
 
   if (! lexer.help_text.empty () && curr_fcn_depth == 1
       && ! parsing_subfunctions)
@@ -2716,7 +2723,8 @@
 tree_function_def *
 octave_base_parser::finish_function (tree_parameter_list *ret_list,
                                      octave_user_function *fcn,
-                                     octave_comment_list *lc)
+                                     octave_comment_list *lc,
+                                     int l, int c)
 {
   tree_function_def *retval = 0;
 
@@ -2742,6 +2750,9 @@
       if (curr_fcn_depth > 1 || parsing_subfunctions)
         {
           fcn->mark_as_subfunction ();
+          fcn->stash_fcn_location (l, c);
+
+          subfunction_names.push_back (nm);
 
           if (endfunction_found && function_scopes.size () > 1)
             {
@@ -3301,6 +3312,19 @@
 
       fcn_ptr = parser.primary_fcn_ptr;
 
+      if (fcn_ptr)
+        {
+          fcn_ptr->maybe_relocate_end ();
+
+          if (parser.parsing_subfunctions)
+            {
+              if (! parser.endfunction_found)
+                parser.subfunction_names.reverse ();
+
+              fcn_ptr->stash_subfunction_names (parser.subfunction_names);
+            }
+        }
+
       if (status != 0)
         error ("parse error while reading file %s", full_file.c_str ());
     }
--- a/libinterp/parse-tree/parse.h
+++ b/libinterp/parse-tree/parse.h
@@ -136,7 +136,7 @@
       parsing_subfunctions (false), max_fcn_depth (0),
       curr_fcn_depth (0), primary_fcn_scope (-1),
       curr_class_name (), function_scopes (), primary_fcn_ptr (0),
-      stmt_list (0),
+      subfunction_names (), stmt_list (0),
       lexer (lxr)
   {
     init ();
@@ -270,7 +270,7 @@
                   tree_statement *end_function);
 
   // Create a no-op statement for end_function.
-  tree_statement *make_end (const std::string& type, int l, int c);
+  tree_statement *make_end (const std::string& type, bool eof, int l, int c);
 
   // Do most of the work for defining a function.
   octave_user_function *
@@ -279,7 +279,8 @@
   // Finish defining a function.
   tree_function_def *
   finish_function (tree_parameter_list *ret_list,
-                   octave_user_function *fcn, octave_comment_list *lc);
+                   octave_user_function *fcn, octave_comment_list *lc,
+                   int l, int c);
 
   // Reset state after parsing function.
   void
@@ -377,6 +378,11 @@
   // Pointer to the primary user function or user script function.
   octave_function *primary_fcn_ptr;
 
+  // List of subfunction names, initially in the order they are
+  // installed in the symbol table, then ordered as they appear in the
+  // file.  Eventually stashed in the primary function object.
+  std::list<std::string> subfunction_names;
+
   // Result of parsing input.
   tree_statement_list *stmt_list;
 
--- a/libinterp/parse-tree/pt-cmd.cc
+++ b/libinterp/parse-tree/pt-cmd.cc
@@ -33,7 +33,8 @@
 tree_no_op_command::dup (symbol_table::scope_id,
                          symbol_table::context_id) const
 {
-  return new tree_no_op_command (orig_cmd, line (), column ());
+  return new tree_no_op_command (orig_cmd, is_end_of_file (),
+                                 line (), column ());
 }
 
 void
--- a/libinterp/parse-tree/pt-cmd.h
+++ b/libinterp/parse-tree/pt-cmd.h
@@ -63,9 +63,9 @@
 {
 public:
 
-  tree_no_op_command (const std::string& cmd = "no_op", int l = -1, int c = -1)
-    : tree_command (l, c), eof (cmd == "endfunction" || cmd == "endscript"),
-      orig_cmd (cmd) { }
+  tree_no_op_command (const std::string& cmd = "no_op", bool e = false,
+                      int l = -1, int c = -1)
+    : tree_command (l, c), eof (e), orig_cmd (cmd) { }
 
   ~tree_no_op_command (void) { }
 
@@ -74,7 +74,12 @@
 
   void accept (tree_walker& tw);
 
-  bool is_end_of_fcn_or_script (void) const { return eof; }
+  bool is_end_of_fcn_or_script (void) const
+  {
+    return (orig_cmd == "endfunction" || orig_cmd == "endscript");
+  }
+
+  bool is_end_of_file (void) const { return eof; }
 
   std::string original_command (void) { return orig_cmd; }
 
--- a/libinterp/parse-tree/pt-stmt.cc
+++ b/libinterp/parse-tree/pt-stmt.cc
@@ -107,6 +107,15 @@
 }
 
 void
+tree_statement::set_location (int l, int c)
+{
+  if (cmd)
+    cmd->set_location (l, c);
+  else if (expr)
+    expr->set_location (l, c);
+}
+
+void
 tree_statement::echo_code (void)
 {
   tree_print_code tpc (octave_stdout, VPS4);
@@ -131,6 +140,23 @@
   return retval;
 }
 
+bool
+tree_statement::is_end_of_file (void) const
+{
+  bool retval = false;
+
+  if (cmd)
+    {
+      tree_no_op_command *no_op_cmd
+        = dynamic_cast<tree_no_op_command *> (cmd);
+
+      if (no_op_cmd)
+        retval = no_op_cmd->is_end_of_file ();
+    }
+
+  return retval;
+}
+
 tree_statement *
 tree_statement::dup (symbol_table::scope_id scope,
                      symbol_table::context_id context) const
--- a/libinterp/parse-tree/pt-stmt.h
+++ b/libinterp/parse-tree/pt-stmt.h
@@ -74,6 +74,8 @@
   int line (void) const;
   int column (void) const;
 
+  void set_location (int l, int c);
+
   void echo_code (void);
 
   tree_command *command (void) { return cmd; }
@@ -86,6 +88,8 @@
 
   bool is_end_of_fcn_or_script (void) const;
 
+  bool is_end_of_file (void) const;
+
   // Allow modification of this statement.  Note that there is no
   // checking.  If you use these, are you sure you knwo what you are
   // doing?
--- a/libinterp/parse-tree/pt.h
+++ b/libinterp/parse-tree/pt.h
@@ -50,6 +50,12 @@
 
   void column (int c) { column_num = c; }
 
+  void set_location (int l, int c)
+  {
+    line_num = l;
+    column_num = c;
+  }
+
   virtual void set_breakpoint (void) { bp = true; }
 
   virtual void delete_breakpoint (void) { bp = false; }
--- a/liboctave/util/base-list.h
+++ b/liboctave/util/base-list.h
@@ -36,6 +36,9 @@
   typedef typename std::list<elt_type>::iterator iterator;
   typedef typename std::list<elt_type>::const_iterator const_iterator;
 
+  typedef typename std::list<elt_type>::reverse_iterator reverse_iterator;
+  typedef typename std::list<elt_type>::const_reverse_iterator const_reverse_iterator;
+
   bool empty (void) const { return lst.empty (); }
 
   size_t size (void) const { return lst.size (); }
@@ -74,6 +77,12 @@
   iterator end (void) { return iterator (lst.end ()); }
   const_iterator end (void) const { return const_iterator (lst.end ()); }
 
+  reverse_iterator rbegin (void) { return reverse_iterator (lst.rbegin ()); }
+  const_reverse_iterator rbegin (void) const { return const_reverse_iterator (lst.rbegin ()); }
+
+  reverse_iterator rend (void) { return reverse_iterator (lst.rend ()); }
+  const_reverse_iterator rend (void) const { return const_reverse_iterator (lst.rend ()); }
+
   elt_type& front (void) { return lst.front (); }
   elt_type& back (void) { return lst.back (); }