changeset 7715:5b4d278ec828

parse scripts completely before executing
author John W. Eaton <jwe@octave.org>
date Wed, 16 Apr 2008 15:09:56 -0400
parents 83ea845cda36
children 9c15f385811c
files src/ChangeLog src/lex.h src/lex.l src/oct-hist.cc src/octave.cc src/ov-usr-fcn.cc src/ov-usr-fcn.h src/parse.h src/parse.y src/pt-bp.cc src/pt-bp.h src/pt-check.cc src/pt-check.h src/pt-cmd.cc src/pt-cmd.h src/pt-pr-code.cc src/pt-pr-code.h src/pt-walk.h
diffstat 18 files changed, 715 insertions(+), 461 deletions(-) [+]
line wrap: on
line diff
--- a/src/ChangeLog	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/ChangeLog	Wed Apr 16 15:09:56 2008 -0400
@@ -1,3 +1,83 @@
+2008-04-16  John W. Eaton  <jwe@octave.org>
+
+	* pt-walk.h (tree_walker::visit_function_def): New function.
+	* pt-pr-code.cc (tree_print_code::visit_function_def): New function.
+	* pt-pr-code.h: Provide decl.
+	* pt-bp.cc (tree_breakpoint::visit_function_def): New function.
+	* pt-bp.h: Provide decl.
+	* pt-check.cc (tree_checker::visit_function_def): New function.
+	* pt-check.h: Provide decl.
+
+	* parse.y (finish_function): Return ptr to tree_function_def object
+	if curr_fcn_ptr is not defined.  Avoid dereferencing invalid pointers.
+	(frob_function): Set curr_fcn_ptr only when reading a function file.
+	(function): Call finish function in both cases to generate value
+	of production.
+
+	* pt-cmd.h, pt-cmd.cc (tree_function_def): New class.
+
+	* parse.y (parse_and_execute): Delete.
+	* parse.h: Delete decl.
+
+	* pt-pb.cc (tree_breakpoint::visit_octave_user_script): New function.
+	* pt-pb.h: Provide decl.
+	* pt-check.cc (tree_checker::visit_octave_user_script): New function.
+	* pt-check.h: Provide decl.
+	* pt-pr-code.cc (tree_print_code::visit_octave_user_script):
+	New function.
+	* pt-pr-code.h: Provide decl.
+	* pt-walk.h (visit_octave_user_script): New pure virtual function.
+
+	* parse.y (make_script, process_leading_comments,
+	looking_at_function_keyword): New static functions.
+	(SCRIPT): New token.
+	(script): New non-terminal.
+	(command): Handle script here.
+	(parse_fcn_file): Eliminate EXEC_SCRIPT argument.  Change all
+	callers.  Parse scripts and return an octave_user_script rather
+	than executing them here.
+	(load_fcn_from_file): Handle script and function files the same.
+	(source_file): Call d_multi_index_op on object returned by
+	parse_fcn_file.
+	(is_function_file): Delete.
+	(gobble_leading_white_space): Don't recurse.  Simplify.  Eliminate
+	SKIP_CODE, IN_PARTS, and SAVE_COPYRIGHT arguments.  Change all
+	callers.
+	(octave_function_ptr): Delete unused typedef.
+	(get_help_from_file): If help text isn't found by
+	gobble_leading_whitespace, parse file to find it.
+
+	* lex.l (SCRIPT_FILE_BEGIN): New exclusive start state.
+	(prep_lexer_for_script): New function.
+	(grab_help_text): New arg, EOF.
+	(process_comment): New function.
+	({CCHAR}): Use it.
+	* lex.h (prep_lexer_for_script): Provide decl.
+
+	* lex.h, lex.cc, parse.y (lexical_feedback::beginning_of_function):
+	Delete data member and all uses.
+
+	* ov-usr-fcn.cc (octave_user_script::~octave_user_script):
+	Move destructor here from ov-usr-fcn.h.  Delete cmd_list.
+	(octave_user_script::subsref, octave_user_script::accept,
+	octave_user_script::traceback_error): New functions.
+	(octave_user_script::do_multi_index_op):
+	Execute cmd_list instead of calling source_file.
+	* ov-usr-fcn.h (octave_usr_script::cmd_list,
+	octave_usr_script::t_parsed, octave_usr_script::t_checked,
+	octave_usr_script::call_depth): New data members.
+	(octave_usr_script::octave_usr_script): Initialize all data members.
+	(octave_usr_script::octave_usr_script (const std::string&, const
+	std::string&, tree_statement_list *, const std::string&)):
+	New constructor.
+	(octave_usr_script::function_value,
+	octave_usr_script::user_script_value, mark_fcn_file_up_to_date,
+	octave_usr_script::stash_fcn_file_time,
+	octave_usr_script::time_parsed, octave_usr_script::time_checked,
+	octave_usr_script::subsref, octave_usr_script::body): New functions.
+	(octave_user_script::subsref, octave_user_script::accept,
+	octave_user_script::traceback_error): Provide decls.
+
 2008-04-14  Jaroslav Hajek <highegg@gmail.com>
 
 	* oct-stream.cc (octave_scan_1): Ensure digit following X is hex
--- a/src/lex.h	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/lex.h	Wed Apr 16 15:09:56 2008 -0400
@@ -50,6 +50,8 @@
 // Is the given string a keyword?
 extern bool is_keyword (const std::string& s);
 
+extern void prep_lexer_for_script (void);
+
 // For communication between the lexer and parser.
 
 class
@@ -72,10 +74,6 @@
   // TRUE means we're in the middle of defining a loop.
   int looping;
 
-  // TRUE means we think we are looking at the beginning of a
-  // function definition.
-  bool beginning_of_function;
-
   // TRUE means that we should convert spaces to a comma inside a
   // matrix definition.
   bool convert_spaces_to_comma;
--- a/src/lex.l	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/lex.l	Wed Apr 16 15:09:56 2008 -0400
@@ -26,6 +26,8 @@
 %s COMMAND_START
 %s MATRIX_START
 
+%x SCRIPT_FILE_BEGIN
+
 %x NESTED_FUNCTION_END
 %x NESTED_FUNCTION_BEGIN
 
@@ -242,7 +244,8 @@
 static int is_keyword_token (const std::string& s);
 static void prep_for_function (void);
 static void prep_for_nested_function (void);
-static std::string grab_help_text (void);
+static std::string grab_help_text (bool& eof);
+static int process_comment (char cchar, bool& eof);
 static bool match_any (char c, const char *s);
 static bool next_token_is_sep_op (void);
 static bool next_token_is_bin_op (bool spc_prev);
@@ -285,6 +288,12 @@
 NUMBER	(({D}+\.?{D}*{EXPON}?)|(\.{D}+{EXPON}?)|(0[xX][0-9a-fA-F]+))
 %%
 
+<SCRIPT_FILE_BEGIN>. {
+    BEGIN (INITIAL);
+    yyunput (yytext[0], yytext);
+    COUNT_TOK_AND_RETURN (SCRIPT);
+  }
+
 <NESTED_FUNCTION_END>. {
     BEGIN (NESTED_FUNCTION_BEGIN);
     yyunput (yytext[0], yytext);
@@ -621,60 +630,12 @@
 %} 
 
 {CCHAR} {
-    std::string help_txt;
-
-    if (! help_buf.empty ())
-      help_txt = help_buf.top ();
-
-    if (help_txt.empty ()
-	&& lexer_flags.beginning_of_function
-	&& nesting_level.none ())
-      {
-	lexer_flags.beginning_of_function = false;
-
-	std::string txt = grab_help_text ();
-
-	if (! help_buf.empty ())
-	  help_buf.pop ();
-
-	help_buf.push (txt);
-
-	octave_comment_buffer::append (txt);
-      }
-    else
-      {
-	std::string buf;
-
-	bool begin_comment = true;
-
-	int c;
-	while ((c = yyinput ()) != EOF && c != '\n')
-	  {
-	    if (begin_comment && (c == '#' || c == '%'))
-	      ; /* Skip leading comment characters. */
-	    else
-	      buf += static_cast<char> (c);
-	  }
-
-	octave_comment_buffer::append (buf);
-      }
-
-    current_input_column = 1;
-    lexer_flags.quote_is_transpose = false;
-    lexer_flags.convert_spaces_to_comma = true;
-
-    maybe_gripe_matlab_incompatible_comment (yytext[0]);
-
-    if (YY_START == COMMAND_START)
-      BEGIN (INITIAL);
-
-    if (nesting_level.none ())
-      {
-	lexer_flags.doing_rawcommand = false;
-	COUNT_TOK_AND_RETURN ('\n');
-      }
-    else if (nesting_level.is_bracket_or_brace ())
-      COUNT_TOK_AND_RETURN (';');
+    bool eof = false;
+    int tok = process_comment (yytext[0], eof);
+    if (eof)
+      TOK_RETURN (END_OF_INPUT);
+    else if (tok > 0)
+      COUNT_TOK_AND_RETURN (tok);
   }
 
 %{
@@ -964,7 +925,6 @@
 
   lexer_flags.defining_func = true;
   lexer_flags.parsed_function_name = false;
-  lexer_flags.beginning_of_function = true;
 
   if (! (reading_fcn_file || reading_script_file))
     input_line_number = 1;
@@ -1180,7 +1140,7 @@
 // duplicates some of this code!
 
 static std::string
-grab_help_text (void)
+grab_help_text (bool& eof)
 {
   std::string buf;
 
@@ -1238,12 +1198,77 @@
 
  done:
 
-  if (c)
+  if (c == EOF)
+    eof = true;
+
+  if (c && ! eof)
     yyunput (c, yytext);
 
   return buf;
 }
 
+static int
+process_comment (char cchar, bool& eof)
+{
+  eof = false;
+
+  std::string help_txt;
+
+  if (! help_buf.empty ())
+    help_txt = help_buf.top ();
+
+  if (help_txt.empty () && nesting_level.none ())
+    {
+      std::string txt = grab_help_text (eof);
+
+      if (! help_buf.empty ())
+	help_buf.pop ();
+
+      help_buf.push (txt);
+
+      octave_comment_buffer::append (txt);
+    }
+  else
+    {
+      std::string buf;
+
+      bool begin_comment = true;
+
+      int c;
+      while ((c = yyinput ()) != EOF && c != '\n')
+	{
+	  if (begin_comment && (c == '#' || c == '%'))
+	    ; /* Skip leading comment characters. */
+	  else
+	    buf += static_cast<char> (c);
+	}
+
+      octave_comment_buffer::append (buf);
+
+      if (c == EOF)
+	eof = true;
+    }
+
+  current_input_column = 1;
+  lexer_flags.quote_is_transpose = false;
+  lexer_flags.convert_spaces_to_comma = true;
+
+  maybe_gripe_matlab_incompatible_comment (cchar);
+
+  if (YY_START == COMMAND_START)
+    BEGIN (INITIAL);
+
+  if (nesting_level.none ())
+    {
+      lexer_flags.doing_rawcommand = false;
+      return '\n';
+    }
+  else if (nesting_level.is_bracket_or_brace ())
+    return ';';
+  else
+    return 0;
+}
+
 // Return 1 if the given character matches any character in the given
 // string.
 
@@ -2347,7 +2372,6 @@
   looping = 0;
 
   // Not initially defining a function.
-  beginning_of_function = false;
   defining_func = false;
   parsed_function_name = false;
   parsing_nested_function = 0;
@@ -2426,6 +2450,11 @@
   return retval;
 }
 
+void
+prep_lexer_for_script (void)
+{
+  BEGIN (SCRIPT_FILE_BEGIN);
+}
 
 static void
 maybe_warn_separator_insert (char sep)
--- a/src/oct-hist.cc	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/oct-hist.cc	Wed Apr 16 15:09:56 2008 -0400
@@ -449,7 +449,7 @@
 
   octave_set_interrupt_handler (old_interrupt_handler);
 
-  // Write the commands to the history file since parse_and_execute
+  // Write the commands to the history file since source_file
   // disables command line history while it executes.
 
   std::fstream file (name.c_str (), std::ios::in);
@@ -488,7 +488,7 @@
   Vecho_executing_commands = ECHO_CMD_LINE;
   input_from_tmp_history_file = true;
 
-  parse_and_execute (name);
+  source_file (name);
 
   unwind_protect::run_frame ("do_edit_history");
 
@@ -517,7 +517,7 @@
   Vecho_executing_commands = ECHO_CMD_LINE;
   input_from_tmp_history_file = true;
 
-  parse_and_execute (name);
+  source_file (name);
 
   unwind_protect::run_frame ("do_run_history");
 
--- a/src/octave.cc	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/octave.cc	Wed Apr 16 15:09:56 2008 -0400
@@ -289,7 +289,11 @@
 
   input_from_startup_file = true;
 
-  int verbose = (verbose_flag && ! inhibit_startup_message);
+  std::string context;
+
+  bool verbose = (verbose_flag && ! inhibit_startup_message);
+
+  bool require_file = false;
 
   if (read_site_files)
     {
@@ -298,9 +302,9 @@
       // (if it exists), then from the file
       // $(prefix)/share/octave/$(version)/m/octaverc (if it exists).
 
-      parse_and_execute (Vlocal_site_defaults_file, verbose);
+      source_file (Vlocal_site_defaults_file, context, verbose, require_file);
 
-      parse_and_execute (Vsite_defaults_file, verbose);
+      source_file (Vsite_defaults_file, context, verbose, require_file);
     }
 
   if (read_init_files)
@@ -324,7 +328,7 @@
 
       if (! home_rc.empty ())
 	{
-	  parse_and_execute (home_rc, verbose);
+	  source_file (home_rc, context, verbose, require_file);
 
 	  // Names alone are not enough.
 
@@ -352,7 +356,7 @@
 	      local_rc = octave_env::make_absolute (initfile, curr_dir);
 	    }
 
-	  parse_and_execute (local_rc, verbose);
+	  source_file (local_rc, context, verbose, require_file);
 	}
     }
 
@@ -447,7 +451,11 @@
 
   try
     {
-      parse_and_execute (fname, false, "octave");
+      std::string context;
+      bool verbose = false;
+      bool require_file = true;
+
+      source_file (fname, context, verbose, require_file, "octave");
     }
   catch (octave_interrupt_exception)
     {
--- a/src/ov-usr-fcn.cc	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/ov-usr-fcn.cc	Wed Apr 16 15:09:56 2008 -0400
@@ -51,7 +51,29 @@
 // Maximum nesting level for functions called recursively.
 static int Vmax_recursion_depth = 256;
 
-// Scripts.
+// User defined scripts.
+
+DEFINE_OCTAVE_ALLOCATOR (octave_user_script);
+
+DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_user_script,
+				     "user-defined script",
+				     "user-defined script");
+
+octave_user_script::~octave_user_script (void)
+{
+  delete cmd_list;
+}
+
+octave_value_list
+octave_user_script::subsref (const std::string&,
+			     const std::list<octave_value_list>&, int)
+{
+  octave_value_list retval;
+
+  ::error ("invalid use of script in index expression");
+
+  return retval;
+}
 
 octave_value_list
 octave_user_script::do_multi_index_op (int nargout,
@@ -61,13 +83,37 @@
 
   if (! error_state)
     {
-      if (args.length () == 0)
+      if (args.length () == 0 && nargout == 0)
 	{
-	  // FIXME -- I think we need a way to protect against
-	  // recursion, but we can't use the same method as we use for
-	  // functions.
+	  if (cmd_list)
+	    {
+	      unwind_protect::begin_frame ("user_script_eval");
+
+	      unwind_protect_int (call_depth);
+	      call_depth++;
+
+	      if (call_depth <= Vmax_recursion_depth)
+		{
+		  octave_call_stack::push (this);
+
+		  unwind_protect::add (octave_call_stack::unwind_pop, 0);
+
+		  cmd_list->eval ();
 
-	  source_file (file_name);
+		  if (tree_return_command::returning)
+		    tree_return_command::returning = 0;
+
+		  if (tree_break_command::breaking)
+		    tree_break_command::breaking--;
+
+		  if (error_state)
+		    traceback_error ();
+		}
+	      else
+		::error ("max_recursion_limit exceeded");
+
+	      unwind_protect::run_frame ("user_script_eval");
+    	    }
 	}
       else
 	error ("invalid call to script");
@@ -76,6 +122,38 @@
   return retval;
 }
 
+void
+octave_user_script::accept (tree_walker& tw)
+{
+  tw.visit_octave_user_script (*this);
+}
+
+// FIXME -- this function is exactly the same as
+// octave_user_function::traceback_error.
+
+void
+octave_user_script::traceback_error (void) const
+{
+  if (error_state >= 0)
+    error_state = -1;
+
+  if (my_name.empty ())
+    {
+      if (file_name.empty ())
+	::error ("called from `?unknown?'");
+      else
+	::error ("called from file `%s'", file_name.c_str ());
+    }
+  else
+    {
+      if (file_name.empty ())
+	::error ("called from `%s'", my_name.c_str ());
+      else 
+	::error ("called from `%s' in file `%s'",
+		 my_name.c_str (), file_name.c_str ());
+    }
+}
+
 // User defined functions.
 
 DEFINE_OCTAVE_ALLOCATOR (octave_user_function);
@@ -84,12 +162,6 @@
 				     "user-defined function",
 				     "user-defined function");
 
-DEFINE_OCTAVE_ALLOCATOR (octave_user_script);
-
-DEFINE_OV_TYPEID_FUNCTIONS_AND_DATA (octave_user_script,
-				     "user-defined script",
-				     "user-defined script");
-
 // Ugh.  This really needs to be simplified (code/data?
 // extrinsic/intrinsic state?).
 
--- a/src/ov-usr-fcn.h	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/ov-usr-fcn.h	Wed Apr 16 15:09:56 2008 -0400
@@ -50,13 +50,23 @@
 {
 public:
 
-  octave_user_script (void) { }
+  octave_user_script (void)
+    : octave_function (), cmd_list (0), file_name () { }
+
+  octave_user_script (const std::string& fnm, const std::string& nm,
+		      tree_statement_list *cmds,
+		      const std::string& ds = std::string ())
+    : octave_function (nm, ds), cmd_list (cmds), file_name (fnm) { }
 
   octave_user_script (const std::string& fnm, const std::string& nm,
 		      const std::string& ds = std::string ())
-    : octave_function (nm, ds), file_name (fnm) { }
+    : octave_function (nm, ds), cmd_list (0), file_name (fnm) { }
+
+  ~octave_user_script (void);
 
-  ~octave_user_script (void) { }
+  octave_function *function_value (bool = false) { return this; }
+
+  octave_user_script *user_script_value (bool = false) { return this; }
 
   // Scripts and user functions are both considered "scripts" because
   // they are written in Octave's scripting language.
@@ -65,16 +75,58 @@
 
   void stash_fcn_file_name (const std::string& nm) { file_name = nm; }
 
+  void mark_fcn_file_up_to_date (const octave_time& t) { t_checked = t; }
+
+  void stash_fcn_file_time (const octave_time& t)
+    {
+      t_parsed = t;
+      mark_fcn_file_up_to_date (t);
+    }
+
   std::string fcn_file_name (void) const { return file_name; }
 
+  octave_time time_parsed (void) const { return t_parsed; }
+
+  octave_time time_checked (void) const { return t_checked; }
+
+  octave_value subsref (const std::string& type,
+			const std::list<octave_value_list>& idx)
+    {
+      octave_value_list tmp = subsref (type, idx, 1);
+      return tmp.length () > 0 ? tmp(0) : octave_value ();
+    }
+
+  octave_value_list subsref (const std::string& type,
+			     const std::list<octave_value_list>& idx,
+			     int nargout);
+
   octave_value_list
   do_multi_index_op (int nargout, const octave_value_list& args);
 
+  tree_statement_list *body (void) { return cmd_list; }
+
+  void traceback_error (void) const;
+
+  void accept (tree_walker& tw);
+
 private:
 
-  // The name of the file to parse.
+  // The list of commands that make up the body of this function.
+  tree_statement_list *cmd_list;
+
+  // The name of the file we parsed.
   std::string file_name;
 
+  // The time the file was parsed.
+  octave_time t_parsed;
+
+  // The time the file was last checked to see if it needs to be
+  // parsed again.
+  octave_time t_checked;
+
+  // Used to keep track of recursion depth.
+  int call_depth;
+
   // No copying!
 
   octave_user_script (const octave_user_script& f);
--- a/src/parse.h	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/parse.h	Wed Apr 16 15:09:56 2008 -0400
@@ -81,13 +81,6 @@
 // Keep a count of how many END tokens we expect.
 extern int end_tokens_expected;
 
-extern OCTINTERP_API void
-parse_and_execute (FILE *f);
-
-extern OCTINTERP_API void
-parse_and_execute (const std::string& s, bool verbose = false,
-		   const char *warn_for = 0);
-
 extern OCTINTERP_API std::string
 get_help_from_file (const std::string& nm, bool& symbol_found,
 		    std::string& file);
@@ -110,7 +103,9 @@
 
 extern OCTINTERP_API void
 source_file (const std::string& file_name,
-	     const std::string& context = std::string ());
+	     const std::string& context = std::string (),
+	     bool verbose = false, bool require_file = true,
+	     const std::string& warn_for = std::string ());
 
 extern OCTINTERP_API octave_value_list
 feval (const std::string& name,
--- a/src/parse.y	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/parse.y	Wed Apr 16 15:09:56 2008 -0400
@@ -259,6 +259,10 @@
 make_assign_op (int op, tree_argument_list *lhs, token *eq_tok,
 		tree_expression *rhs);
 
+// Define a script.
+static void
+make_script (tree_statement_list *cmds);
+
 // Begin defining a function.
 static octave_user_function *
 start_function (tree_parameter_list *param_list, tree_statement_list *body);
@@ -268,7 +272,7 @@
 frob_function (const std::string& fname, octave_user_function *fcn);
 
 // Finish defining a function.
-static void
+static tree_function_def *
 finish_function (tree_parameter_list *ret_list,
 		 octave_user_function *fcn, octave_comment_list *lc);
 
@@ -399,7 +403,7 @@
 
 // Other tokens.
 %token END_OF_INPUT LEXICAL_ERROR
-%token FCN
+%token FCN SCRIPT
 // %token VARARGIN VARARGOUT
 %token CLOSE_BRACE
 
@@ -424,7 +428,7 @@
 %type <tree_parameter_list_type> param_list param_list1 param_list2
 %type <tree_parameter_list_type> return_list return_list1
 %type <tree_command_type> command select_command loop_command
-%type <tree_command_type> jump_command except_command function
+%type <tree_command_type> jump_command except_command function script
 %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
@@ -521,10 +525,7 @@
 		;
 
 list1		: statement
-		  {
-		    lexer_flags.beginning_of_function = false;
-		    $$ = new tree_statement_list ($1);
-		  }
+		  { $$ = new tree_statement_list ($1); }
 		| list1 sep statement
 		  {
 		    set_stmt_print_flag ($1, $2, true);
@@ -908,6 +909,8 @@
 		  { $$ = $1; }
 		| function
 		  { $$ = $1; }
+		| script
+		  { $$ = $1; }
 		;
 
 // =====================
@@ -1206,6 +1209,17 @@
 		  }
 		;
 
+// ===========
+// Script file
+// ===========
+
+script		: SCRIPT opt_list END_OF_INPUT
+		  {
+		    make_script ($2);
+		    $$ = 0;
+		  }
+		;
+
 // ===================
 // Function definition
 // ===================
@@ -1216,15 +1230,13 @@
 
 function	: function_beg function1
 		  {
-		    $2->stash_leading_comment ($1);
+		    $$ = finish_function (0, $2, $1);
 		    recover_from_parsing_function ();
-		    $$ = 0;
 		  }
 		| function_beg return_list '=' function1
 		  {
-		    finish_function ($2, $4, $1);
+		    $$ = finish_function ($2, $4, $1);
 		    recover_from_parsing_function ();
-		    $$ = 0;
 		  }
 		;
 
@@ -2386,6 +2398,30 @@
   return retval;
 }
 
+// Define a function.
+
+static void
+make_script (tree_statement_list *cmds)
+{
+  std::string doc_string;
+
+  if (! help_buf.empty ())
+    {
+      doc_string = help_buf.top ();
+      help_buf.pop ();
+    }
+
+  octave_user_script *script
+    = new octave_user_script (curr_fcn_file_full_name, curr_fcn_file_name,
+			      cmds, doc_string);
+
+  octave_time now;
+
+  script->stash_fcn_file_time (now);
+
+  curr_fcn_ptr = script;
+}
+
 // Begin defining a function.
 
 static octave_user_function *
@@ -2511,34 +2547,35 @@
 	  symbol_table::reset_parent_scope ();
 	}
     }
-  else if (! reading_fcn_file)
-    {
-      std::string nm = fcn->name ();
-
-      symbol_table::install_cmdline_function (nm, octave_value (fcn));
-
-      // Make sure that any variable with the same name as the new
-      // function is cleared.
-
-      symbol_table::varref (nm) = octave_value ();
-    }
+  else if (reading_fcn_file)
+    curr_fcn_ptr = fcn;
   else
-    curr_fcn_ptr = fcn;
+    curr_fcn_ptr = 0;
 
   return fcn;
 }
 
-// Finish defining a function.
-
-static void
+static tree_function_def *
 finish_function (tree_parameter_list *ret_list,
 		 octave_user_function *fcn, octave_comment_list *lc)
 {
-  ret_list->mark_as_formal_parameters ();
-
-  fcn->stash_leading_comment (lc);
-
-  fcn->define_ret_list (ret_list);
+  tree_function_def *retval = 0;
+
+  if (ret_list)
+    ret_list->mark_as_formal_parameters ();
+
+  if (fcn)
+    {
+      if (lc)
+	fcn->stash_leading_comment (lc);
+
+      fcn->define_ret_list (ret_list);
+    }
+
+  if (! curr_fcn_ptr)
+    retval = new tree_function_def (fcn);
+
+  return retval;
 }
 
 static void
@@ -2551,7 +2588,6 @@
   symtab_context.pop ();
 
   lexer_flags.defining_func = false;
-  lexer_flags.beginning_of_function = false;
   lexer_flags.parsed_function_name = false;
   lexer_flags.looking_at_return_list = false;
   lexer_flags.looking_at_parameter_list = false;
@@ -2767,77 +2803,6 @@
     }
 }
 
-void
-parse_and_execute (FILE *f)
-{
-  unwind_protect::begin_frame ("parse_and_execute");
-
-  unwind_protect_ptr (global_command);
-
-  YY_BUFFER_STATE old_buf = current_buffer ();
-  YY_BUFFER_STATE new_buf = create_buffer (f);
-
-  unwind_protect::add (restore_input_buffer, old_buf);
-  unwind_protect::add (delete_input_buffer, new_buf);
-
-  switch_to_buffer (new_buf);
-
-  unwind_protect_bool (line_editing);
-  unwind_protect_bool (get_input_from_eval_string);
-  unwind_protect_bool (parser_end_of_input);
-
-  line_editing = false;
-  get_input_from_eval_string = false;
-  parser_end_of_input = false;
-
-  int retval;
-  do
-    {
-      reset_parser ();
-
-      retval = yyparse ();
-
-      if (retval == 0)
-        {
-          if (global_command)
-	    {
-	      global_command->eval ();
-
-	      delete global_command;
-
-	      global_command = 0;
-
-	      OCTAVE_QUIT;
-
-	      bool quit = (tree_return_command::returning
-			   || tree_break_command::breaking);
-
-	      if (tree_return_command::returning)
-		tree_return_command::returning = 0;
-
-	      if (tree_break_command::breaking)
-		tree_break_command::breaking--;
-
-	      if (error_state)
-		{
-		  error ("near line %d of file `%s'", input_line_number,
-			 curr_fcn_file_full_name.c_str ());
-
-		  break;
-		}
-
-	      if (quit)
-		break;
-	    }
-	  else if (parser_end_of_input)
-	    break;
-        }
-    }
-  while (retval == 0);
-
-  unwind_protect::run_frame ("parse_and_execute");
-}
-
 static void
 safe_fclose (void *f)
 {
@@ -2854,51 +2819,6 @@
     fclose (static_cast<FILE *> (f));
 }
 
-void
-parse_and_execute (const std::string& s, bool verbose, const char *warn_for)
-{
-  unwind_protect::begin_frame ("parse_and_execute_2");
-
-  unwind_protect_bool (reading_script_file);
-  unwind_protect_str (curr_fcn_file_full_name);
-
-  reading_script_file = true;
-  curr_fcn_file_full_name = s;
-
-  FILE *f = get_input_from_file (s, 0);
-
-  if (f)
-    {
-      unwind_protect::add (safe_fclose, f);
-
-      octave_user_script *script = new octave_user_script (s, s, "");
-      octave_call_stack::push (script);
-      unwind_protect::add (octave_call_stack::unwind_pop_script, 0);
-
-      unwind_protect_int (input_line_number);
-      unwind_protect_int (current_input_column);
-
-      input_line_number = 0;
-      current_input_column = 1;
-
-      if (verbose)
-	{
-	  std::cout << "reading commands from " << s << " ... ";
-	  reading_startup_message_printed = true;
-	  std::cout.flush ();
-	}
-
-      parse_and_execute (f);
-
-      if (verbose)
-	std::cout << "done." << std::endl;
-    }
-  else if (warn_for)
-    error ("%s: unable to open file `%s'", warn_for, s.c_str ());
-
-  unwind_protect::run_frame ("parse_and_execute_2");
-}
-
 static bool
 looks_like_copyright (const std::string& s)
 {
@@ -2930,33 +2850,13 @@
 }
 
 // Eat whitespace and comments from FFILE, returning the text of the
-// comments read if it doesn't look like a copyright notice.  If
-// IN_PARTS, consider each block of comments separately; otherwise,
-// grab them all at once.  If UPDATE_POS is TRUE, line and column
-// number information is updated.  If SAVE_COPYRIGHT is TRUE, then
-// comments that are recognized as a copyright notice are saved in the
-// comment buffer.  If SKIP_CODE is TRUE, then ignore code, otherwise
-// stop at the first non-whitespace character that is not part of a
+// comments read if it doesn't look like a copyright notice.  The
+// parser line and column number information is updated.  Processing
+// stops at the first non-whitespace character that is not part of a
 // comment.
 
-// FIXME -- skipping code will fail for something like this:
-//
-//   function foo (x)
-//     fprintf ('%d\n', x);
-//
-//   % This is the help for foo.
-//
-// because we recognize the '%' in the fprintf format as a comment
-// character.  Fixing this will probably require actually parsing the
-// file properly.
-
-// FIXME -- grab_help_text() in lex.l duplicates some of this
-// code!
-
 static std::string
-gobble_leading_white_space (FILE *ffile, bool in_parts,
-			    bool update_pos, bool save_copyright,
-			    bool skip_code)
+gobble_leading_white_space (FILE *ffile)
 {
   std::string help_txt;
 
@@ -2980,8 +2880,7 @@
 
   while ((c = text_getc (ffile)) != EOF)
     {
-      if (update_pos)
-	current_input_column++;
+      current_input_column++;
 
       if (begin_comment)
 	{
@@ -3006,38 +2905,32 @@
 
 	  if (c == '\n')
 	    {
-	      if (update_pos)
-		{
-		  input_line_number++;
-		  current_input_column = 0;
-		}
+	      input_line_number++;
+	      current_input_column = 0;
 
 	      in_comment = false;
 	      discard_space = true;
-
-	      if (in_parts)
-		{
-		  if ((c = text_getc (ffile)) != EOF)
-		    {
-		      if (update_pos)
-			current_input_column--;
-		      ungetc (c, ffile);
-		      if (c == '\n')
-			break;
-		    }
-		  else
-		    break;
-		}
 	    }
 	}
       else
 	{
 	  switch (c)
 	    {
+	    case '\n':
+	      input_line_number++;
+	      current_input_column = 0;
+	      // fall through...
+
 	    case ' ':
 	    case '\t':
-	      if (first_comments_seen)
-		have_help_text = true;
+	      if (first_comments_seen && ! have_help_text)
+		{
+		  if (looks_like_copyright (help_txt))
+		    help_txt.resize (0);
+
+		  if (! help_txt.empty ())
+		    have_help_text = true;
+		}
 	      break;
 
 	    case '%':
@@ -3046,122 +2939,48 @@
 	      in_comment = true;
 	      break;
 
-	    case '\n':
-	      if (first_comments_seen)
-		have_help_text = true;
-	      if (update_pos)
-		{
-		  input_line_number++;
-		  current_input_column = 0;
-		}
-	      continue;
-
 	    default:
-	      if (skip_code)
-		continue;
-	      else
-		{
-		  if (update_pos)
-		    current_input_column--;
-		  ungetc (c, ffile);
-		  goto done;
-		}
+	      current_input_column--;
+	      ungetc (c, ffile);
+	      goto done;
 	    }
 	}
     }
 
  done:
 
-  if (! help_txt.empty ())
-    {
-      if (looks_like_copyright (help_txt))
-	{
-	  if (save_copyright)
-	    octave_comment_buffer::append (help_txt);
-
-	  help_txt.resize (0);
-	}
-
-      if (in_parts && help_txt.empty ())
-	help_txt = gobble_leading_white_space (ffile, in_parts, update_pos,
-					       false, skip_code);
-    }
-
   return help_txt;
 }
 
-std::string
-get_help_from_file (const std::string& nm, bool& symbol_found,
-		    std::string& file)
+static void
+process_leading_comments (FILE *fptr)
 {
-  std::string retval;
-
-  file = fcn_file_in_path (nm);
-
-  if (! file.empty ())
-    {
-      symbol_found = true;
-
-      FILE *fptr = fopen (file.c_str (), "r");
-
-      if (fptr)
-	{
-	  unwind_protect::add (safe_fclose, fptr);
-
-	  retval = gobble_leading_white_space (fptr, true, true, false, true);
-
-	  unwind_protect::run ();
-	}
-    }
-
-  return retval;
+  std::string txt = gobble_leading_white_space (fptr);
+
+  help_buf.push (txt);
+
+  octave_comment_buffer::append (txt);
 }
 
-std::string
-get_help_from_file (const std::string& nm, bool& symbol_found)
+static bool
+looking_at_function_keyword (FILE *ffile)
 {
-  std::string file;
-  return get_help_from_file (nm, symbol_found, file);
-}
-
-static int
-is_function_file (FILE *ffile)
-{
-  int status = 0;
+  bool status = false;
 
   long pos = ftell (ffile);
 
-  gobble_leading_white_space (ffile, false, false, false, false);
-
   char buf [10];
   fgets (buf, 10, ffile);
-  int len = strlen (buf);
+  size_t len = strlen (buf);
   if (len > 8 && strncmp (buf, "function", 8) == 0
       && ! (isalnum (buf[8]) || buf[8] == '_'))
-    status = 1;
+    status = true;
 
   fseek (ffile, pos, SEEK_SET);
 
   return status;
 }
 
-static int
-is_function_file (const std::string& fname)
-{
-  int retval = 0;
-
-  FILE *fid = fopen (fname.c_str (), "r");
-
-  if (fid)
-    {
-      retval = is_function_file (fid);
-
-      fclose (fid);
-    }
-
-  return retval;
-}
-
 static void
 restore_command_history (void *)
 {
@@ -3174,11 +2993,10 @@
   command_editor::set_input_stream (static_cast<FILE *> (f));
 }
 
-typedef octave_function * octave_function_ptr;
-
 static octave_function *
 parse_fcn_file (const std::string& ff, const std::string& dispatch_type,
-		bool exec_script, bool force_script = false)
+		bool force_script = false, bool require_file = true,
+		const std::string& warn_for = std::string ())
 {
   unwind_protect::begin_frame ("parse_fcn_file");
 
@@ -3210,96 +3028,89 @@
   parent_function_name = "";
   current_class_name = dispatch_type;
 
+  // The next four lines must be in this order.
+  unwind_protect::add (restore_command_history, 0);
+
+  // FIXME -- we shouldn't need both the
+  // command_history object and the
+  // Vsaving_history variable...
+  command_history::ignore_entries ();
+
+  unwind_protect_bool (Vsaving_history);
+
+  Vsaving_history = false;
+
   FILE *ffile = get_input_from_file (ff, 0);
 
   unwind_protect::add (safe_fclose, ffile);
 
   if (ffile)
     {
-      // Check to see if this file defines a function or is just a
-      // list of commands.
-
-      if (! force_script && is_function_file (ffile))
+      process_leading_comments (ffile);
+
+      std::string file_type;
+
+      bool parsing_script = false;
+
+      if (! force_script && looking_at_function_keyword (ffile))
 	{
-	  // FIXME -- we shouldn't need both the
-	  // command_history object and the
-	  // Vsaving_history variable...
-	  command_history::ignore_entries ();
-
-	  unwind_protect::add (restore_command_history, 0);
+	  file_type = "function";
 
 	  unwind_protect_int (Vecho_executing_commands);
-	  unwind_protect_bool (Vsaving_history);
 	  unwind_protect_bool (reading_fcn_file);
 	  unwind_protect_bool (get_input_from_eval_string);
 	  unwind_protect_bool (parser_end_of_input);
 
 	  Vecho_executing_commands = ECHO_OFF;
-	  Vsaving_history = false;
 	  reading_fcn_file = true;
 	  get_input_from_eval_string = false;
 	  parser_end_of_input = false;
-
-	  YY_BUFFER_STATE old_buf = current_buffer ();
-	  YY_BUFFER_STATE new_buf = create_buffer (ffile);
-
-	  unwind_protect::add (restore_input_buffer, old_buf);
-	  unwind_protect::add (delete_input_buffer, new_buf);
-
-	  switch_to_buffer (new_buf);
-
-	  unwind_protect_ptr (curr_fcn_ptr);
-	  curr_fcn_ptr = 0;
-
-	  reset_parser ();
-
-	  std::string txt
-	    = gobble_leading_white_space (ffile, true, true, true, false);
-
-	  help_buf.push (txt);
-
-	  octave_comment_buffer::append (txt);
-
-	  // FIXME -- this should not be necessary.
-	  gobble_leading_white_space (ffile, false, true, false, false);
-
-	  lexer_flags.parsing_class_method = ! dispatch_type.empty ();
-
-	  int status = yyparse ();
-
-	  fcn_ptr = curr_fcn_ptr;
-
-	  if (status != 0)
-	    error ("parse error while reading function file %s", ff.c_str ());
 	}
-      else if (exec_script)
+      else
 	{
+	  file_type = "script";
+
 	  // The value of `reading_fcn_file' will be restored to the
 	  // proper value when we unwind from this frame.
 	  reading_fcn_file = old_reading_fcn_file_state;
 
-	  // FIXME -- we shouldn't need both the
-	  // command_history object and the
-	  // Vsaving_history variable...
-	  command_history::ignore_entries ();
-
-	  unwind_protect::add (restore_command_history, 0);
-
-	  unwind_protect_bool (Vsaving_history);
 	  unwind_protect_bool (reading_script_file);
 
-	  Vsaving_history = false;
 	  reading_script_file = true;
 
-	  octave_user_script *script = new octave_user_script (ff, ff, "");
-	  octave_call_stack::push (script);
-	  unwind_protect::add (octave_call_stack::unwind_pop_script, 0);
-
-	  parse_and_execute (ffile);
+	  parsing_script = true;
 	}
+
+      YY_BUFFER_STATE old_buf = current_buffer ();
+      YY_BUFFER_STATE new_buf = create_buffer (ffile);
+
+      unwind_protect::add (restore_input_buffer, old_buf);
+      unwind_protect::add (delete_input_buffer, new_buf);
+
+      switch_to_buffer (new_buf);
+
+      unwind_protect_ptr (curr_fcn_ptr);
+      curr_fcn_ptr = 0;
+
+      reset_parser ();
+
+      if (parsing_script)
+	prep_lexer_for_script ();
+
+      lexer_flags.parsing_class_method = ! dispatch_type.empty ();
+
+      int status = yyparse ();
+
+      fcn_ptr = curr_fcn_ptr;
+
+      if (status != 0)
+	error ("parse error while reading %s file %s",
+	       file_type.c_str(), ff.c_str ());
     }
-  else
+  else if (require_file)
     error ("no such file, `%s'", ff.c_str ());
+  else if (! warn_for.empty ())
+    error ("%s: unable to open file `%s'", warn_for.c_str (), ff.c_str ());    
 
   unwind_protect::run_frame ("parse_fcn_file");
 
@@ -3307,6 +3118,52 @@
 }
 
 std::string
+get_help_from_file (const std::string& nm, bool& symbol_found,
+		    std::string& file)
+{
+  std::string retval;
+
+  file = fcn_file_in_path (nm);
+
+  if (! file.empty ())
+    {
+      symbol_found = true;
+
+      FILE *fptr = fopen (file.c_str (), "r");
+
+      if (fptr)
+	{
+	  unwind_protect::add (safe_fclose, fptr);
+
+	  retval = gobble_leading_white_space (fptr);
+
+	  if (retval.empty ())
+	    {
+	      octave_function *fcn = parse_fcn_file (file, "");
+
+	      if (fcn)
+		{
+		  retval = fcn->doc_string ();
+
+		  delete fcn;
+		}
+	    }
+
+	  unwind_protect::run ();
+	}
+    }
+
+  return retval;
+}
+
+std::string
+get_help_from_file (const std::string& nm, bool& symbol_found)
+{
+  std::string file;
+  return get_help_from_file (nm, symbol_found, file);
+}
+
+std::string
 lookup_autoload (const std::string& nm)
 {
   std::string retval;
@@ -3402,20 +3259,15 @@
     retval = octave_dynamic_loader::load_mex (nm, file, fcn_file_from_relative_lookup);
   else if (len > 2)
     {
-      if (is_function_file (file))
-	{
-	  // These are needed by yyparse.
-
-	  unwind_protect_str (curr_fcn_file_name);
-	  unwind_protect_str (curr_fcn_file_full_name);
-
-	  curr_fcn_file_name = nm;
-	  curr_fcn_file_full_name = file;
-
-	  retval = parse_fcn_file (file, dispatch_type, false, autoloading);
-	}
-      else
-	retval = new octave_user_script (file, fcn_name);
+      // These are needed by yyparse.
+
+      unwind_protect_str (curr_fcn_file_name);
+      unwind_protect_str (curr_fcn_file_full_name);
+
+      curr_fcn_file_name = nm;
+      curr_fcn_file_full_name = file;
+
+      retval = parse_fcn_file (file, dispatch_type, autoloading);
     }
 
   if (retval)
@@ -3530,7 +3382,8 @@
 }
 
 void
-source_file (const std::string& file_name, const std::string& context)
+source_file (const std::string& file_name, const std::string& context,
+	     bool verbose, bool require_file, const std::string& warn_for)
 {
   std::string file_full_name = file_ops::tilde_expand (file_name);
 
@@ -3557,9 +3410,31 @@
 
   if (! error_state)
     {
-      parse_fcn_file (file_full_name, "", true, true);
-
-      if (error_state)
+      octave_function *fcn = parse_fcn_file (file_full_name, "", true,
+					     require_file, warn_for);
+
+      if (! error_state)
+	{
+	  if (fcn && fcn->is_user_script ())
+	    {
+	      octave_value_list args;
+
+	      if (verbose)
+		{
+		  std::cout << "executing commands from " << file_full_name << " ... ";
+		  reading_startup_message_printed = true;
+		  std::cout.flush ();
+		}
+
+	      fcn->do_multi_index_op (0, args);
+
+	      if (verbose)
+		std::cout << "done." << std::endl;
+
+	      delete fcn;
+	    }
+	}
+      else
 	error ("source: error sourcing file `%s'",
 	       file_full_name.c_str ());
     }
--- a/src/pt-bp.cc	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/pt-bp.cc	Wed Apr 16 15:09:56 2008 -0400
@@ -270,6 +270,12 @@
 }
 
 void 
+tree_breakpoint::visit_octave_user_script (octave_user_script&)
+{
+  // FIXME -- should anything happen here?
+}
+
+void 
 tree_breakpoint::visit_octave_user_function (octave_user_function&)
 {
   // We should not visit octave user functions because the function we
@@ -289,6 +295,18 @@
   // Do nothing.
 }
 
+void
+tree_breakpoint::visit_function_def (tree_function_def& fdef)
+{
+  if (found)
+    return;
+
+  octave_function *fcn = fdef.function ();
+
+  if (fcn)
+    fcn->accept (*this);
+}
+
 void 
 tree_breakpoint::visit_identifier (tree_identifier& id)
 {
--- a/src/pt-bp.h	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/pt-bp.h	Wed Apr 16 15:09:56 2008 -0400
@@ -69,12 +69,16 @@
 
   void visit_complex_for_command (tree_complex_for_command&);
 
+  void visit_octave_user_script (octave_user_script&);
+
   void visit_octave_user_function (octave_user_function&);
 
   void visit_octave_user_function_header (octave_user_function&);
 
   void visit_octave_user_function_trailer (octave_user_function&);
 
+  void visit_function_def (tree_function_def&);
+
   void visit_identifier (tree_identifier&);
 
   void visit_if_clause (tree_if_clause&);
--- a/src/pt-check.cc	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/pt-check.cc	Wed Apr 16 15:09:56 2008 -0400
@@ -181,6 +181,15 @@
 }
 
 void
+tree_checker::visit_octave_user_script (octave_user_script& fcn)
+{
+  tree_statement_list *cmd_list = fcn.body ();
+
+  if (cmd_list)
+    cmd_list->accept (*this);
+}
+
+void
 tree_checker::visit_octave_user_function (octave_user_function& fcn)
 {
   tree_statement_list *cmd_list = fcn.body ();
@@ -190,6 +199,15 @@
 }
 
 void
+tree_checker::visit_function_def (tree_function_def& fdef)
+{
+  octave_function *fcn = fdef.function ();
+
+  if (fcn)
+    fcn->accept (*this);
+}
+
+void
 tree_checker::visit_identifier (tree_identifier& /* id */)
 {
 }
--- a/src/pt-check.h	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/pt-check.h	Wed Apr 16 15:09:56 2008 -0400
@@ -58,8 +58,12 @@
 
   void visit_complex_for_command (tree_complex_for_command&);
 
+  void visit_octave_user_script (octave_user_script&);
+
   void visit_octave_user_function (octave_user_function&);
 
+  void visit_function_def (tree_function_def&);
+
   void visit_identifier (tree_identifier&);
 
   void visit_if_clause (tree_if_clause&);
--- a/src/pt-cmd.cc	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/pt-cmd.cc	Wed Apr 16 15:09:56 2008 -0400
@@ -42,6 +42,38 @@
   tw.visit_no_op_command (*this);
 }
 
+// Function definition.
+
+void
+tree_function_def::eval (void)
+{
+  octave_function *f = function ();
+
+  if (f)
+    {
+      std::string nm = f->name ();
+
+      symbol_table::install_cmdline_function (nm, fcn);
+
+      // Make sure that any variable with the same name as the new
+      // function is cleared.
+
+      symbol_table::varref (nm) = octave_value ();
+    }
+}
+
+tree_command *
+tree_function_def::dup (symbol_table::scope_id)
+{
+  return new tree_function_def (fcn, line (), column ());
+}
+
+void
+tree_function_def::accept (tree_walker& tw)
+{
+  tw.visit_function_def (*this);
+}
+
 /*
 ;;; Local Variables: ***
 ;;; mode: C++ ***
--- a/src/pt-cmd.h	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/pt-cmd.h	Wed Apr 16 15:09:56 2008 -0400
@@ -28,6 +28,7 @@
 
 class tree_walker;
 
+#include "ov-fcn.h"
 #include "pt.h"
 #include "pt-bp.h"
 #include "symtab.h"
@@ -88,6 +89,40 @@
   tree_no_op_command& operator = (const tree_no_op_command&);
 };
 
+// Function definition.
+
+class
+tree_function_def : public tree_command
+{
+public:
+
+  tree_function_def (octave_function *f, int l = -1, int c = -1)
+    : tree_command (l, c), fcn (f) { }
+
+  ~tree_function_def (void) { }
+
+  void eval (void);
+
+  tree_command *dup (symbol_table::scope_id scope);
+
+  void accept (tree_walker& tw);
+
+  octave_function *function (void) { return fcn.function_value (); }
+
+private:
+
+  octave_value fcn;
+
+  tree_function_def (const octave_value& v, int l = -1, int c = -1)
+    : tree_command (l, c), fcn (v) { }
+
+  // No copying!
+
+  tree_function_def (const tree_function_def&);
+
+  tree_function_def& operator = (const tree_function_def&);
+};
+
 #endif
 
 /*
--- a/src/pt-pr-code.cc	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/pt-pr-code.cc	Wed Apr 16 15:09:56 2008 -0400
@@ -284,6 +284,17 @@
 }
 
 void
+tree_print_code::visit_octave_user_script (octave_user_script& fcn)
+{
+  reset ();
+
+  tree_statement_list *cmd_list = fcn.body ();
+
+  if (cmd_list)
+    cmd_list->accept (*this);
+}
+
+void
 tree_print_code::visit_octave_user_function (octave_user_function& fcn)
 {
   reset ();
@@ -407,6 +418,17 @@
 }
 
 void
+tree_print_code::visit_function_def (tree_function_def& fdef)
+{
+  indent ();
+
+  octave_function *fcn = fdef.function ();
+
+  if (fcn)
+    fcn->accept (*this);
+}
+
+void
 tree_print_code::visit_identifier (tree_identifier& id)
 {
   indent ();
--- a/src/pt-pr-code.h	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/pt-pr-code.h	Wed Apr 16 15:09:56 2008 -0400
@@ -75,12 +75,16 @@
 
   void visit_complex_for_command (tree_complex_for_command&);
 
+  void visit_octave_user_script (octave_user_script&);
+
   void visit_octave_user_function (octave_user_function&);
 
   void visit_octave_user_function_header (octave_user_function&);
 
   void visit_octave_user_function_trailer (octave_user_function&);
 
+  void visit_function_def (tree_function_def&);
+
   void visit_identifier (tree_identifier&);
 
   void visit_if_clause (tree_if_clause&);
--- a/src/pt-walk.h	Wed Apr 16 14:19:59 2008 -0400
+++ b/src/pt-walk.h	Wed Apr 16 15:09:56 2008 -0400
@@ -35,7 +35,9 @@
 class tree_decl_init_list;
 class tree_simple_for_command;
 class tree_complex_for_command;
+class octave_user_script;
 class octave_user_function;
+class tree_function_def;
 class tree_identifier;
 class tree_if_clause;
 class tree_if_command;
@@ -102,9 +104,15 @@
   visit_complex_for_command (tree_complex_for_command&) = 0;
 
   virtual void
+  visit_octave_user_script (octave_user_script&) = 0;
+
+  virtual void
   visit_octave_user_function (octave_user_function&) = 0;
 
   virtual void
+  visit_function_def (tree_function_def&) = 0;
+
+  virtual void
   visit_identifier (tree_identifier&) = 0;
 
   virtual void