changeset 9474:25ed2d6aacf6

Parse nested functions more accurately.
author David Grundberg <individ@acc.umu.se>
date Thu, 30 Jul 2009 11:52:58 -0400
parents 833109a9f37f
children 983de84e4bf3
files doc/interpreter/contributors.in src/ChangeLog src/lex.h src/lex.l src/parse.h src/parse.y src/symtab.cc src/symtab.h
diffstat 8 files changed, 252 insertions(+), 272 deletions(-) [+]
line wrap: on
line diff
--- a/doc/interpreter/contributors.in	Thu Jul 30 13:23:21 2009 +0200
+++ b/doc/interpreter/contributors.in	Thu Jul 30 11:52:58 2009 -0400
@@ -62,6 +62,7 @@
 Brian Gough
 Steffen Groot
 Etienne Grossmann
+David Grundberg
 Peter Gustafson
 Kai Habel
 William P. Y. Hadisoeseno
--- a/src/ChangeLog	Thu Jul 30 13:23:21 2009 +0200
+++ b/src/ChangeLog	Thu Jul 30 11:52:58 2009 -0400
@@ -1,3 +1,48 @@
+2009-07-30  David Grundberg  <individ@acc.umu.se>
+
+	* symtab.h (symbol_table::parent_scope): Remove.
+	(symbol_table::set_parent_scope): Remove.
+	(symbol_table::reset_parent_scope): Remove.
+	(symbol_table::install_subfunction): Require scope parameter
+	instead of xparent_scope default.
+	* symtab.cc: Remove symbol_table::xparent_scope
+	* lex.h (lexical_feedback::parsing_nested_function): Remove.
+	* lex.l	(is_keyword_token): Don't ignore endfunctions.
+	(prep_lexer_for_script_file): Renamed from prep_lexer_for_script.
+	(prep_lexer_for_function_file): New function.
+	(display_token): Display SCRIPT_FILE and FUNCTION_FILE.
+	(display_state): Display FUNCTION_FILE_BEGIN
+	(FUNCTION_FILE_BEGIN): New state.
+	(NESTED_FUNCTION_END, NESTED_FUNCTION_BEGIN): Remove states.
+	(prep_for_function, prep_for_nested_function): Remove functions.
+	* parse.h: Remove extern declaration parent_function_name,
+	end_tokens_expected.
+	* parse.y: Add variables current_function_depth,
+	max_function_depth, parsing_subfunctions, seen_endfunction. Remove
+	parent_function_name. Rename curr_fcn_ptr to primary_fcn_ptr. Add
+	token FUNCTION_FILE. Rename token SCRIPT to SCRIPT_FILE.
+	(function_file): New rule.
+	(input): Accept function_file.
+	(script_file): Rule renamed from script.
+	(function_file): New rule.
+	(function_list): New rule.
+	(push_fcn_symtab): Parse nested functions.
+	(fcn_name): Remove parent_function_name.
+	(function_end): Use seen_endfunction. New error messages.
+	(make_script): Use primary_fcn_ptr.
+	(frob_function): Simplify control structures. Don't use
+	symbol_table::parent_scope.
+	(push_fcn_symtab, function_end, frob_function, finish_function):
+	Use current_function_depth instead of
+	lexical_feedback::parsing_nested_function.
+	(make_return_command): Use current_function_depth instead of
+	lexical_feedback::defining_func.
+	(make_break_command, make_decl_command, maybe_warn_missing_semi):
+	Ditto
+	(parse_fcn_file): Warn when nested functions have been
+	declared. Remove superfluous local variables. Parse function files
+	using function_file rule.
+
 2009-07-30  Jaroslav Hajek  <highegg@gmail.com>
 
 	* data.cc (Fmerge): New DEFUN.
--- a/src/lex.h	Thu Jul 30 13:23:21 2009 +0200
+++ b/src/lex.h	Thu Jul 30 11:52:58 2009 -0400
@@ -46,7 +46,8 @@
 // Is the given string a keyword?
 extern bool is_keyword (const std::string& s);
 
-extern void prep_lexer_for_script (void);
+extern void prep_lexer_for_script_file (void);
+extern void prep_lexer_for_function_file (void);
 
 // For communication between the lexer and parser.
 
@@ -117,15 +118,9 @@
   bool looking_at_indirect_ref;
 
   // TRUE means that we've already seen the name of this function.
-  // Should only matter if defining_func is also TRUE.
+  // Should only matter if current_function_level > 0
   bool parsed_function_name;
 
-  // Are we parsing a nested function?
-  //   1 ==> Yes.
-  //   0 ==> No.
-  //  -1 ==> Yes, but it is the last one because we have seen EOF.
-  int parsing_nested_function;
-
   // TRUE means we are parsing a class method.
   bool parsing_class_method;
 
--- a/src/lex.l	Thu Jul 30 13:23:21 2009 +0200
+++ b/src/lex.l	Thu Jul 30 11:52:58 2009 -0400
@@ -28,9 +28,7 @@
 %s MATRIX_START
 
 %x SCRIPT_FILE_BEGIN
-
-%x NESTED_FUNCTION_END
-%x NESTED_FUNCTION_BEGIN
+%x FUNCTION_FILE_BEGIN
 
 %{
 #ifdef HAVE_CONFIG_H
@@ -282,8 +280,6 @@
 static void fixup_column_count (char *s);
 static void do_comma_insert_check (void);
 static int is_keyword_token (const std::string& s);
-static void prep_for_function (void);
-static void prep_for_nested_function (void);
 static int process_comment (bool start_in_block, bool& eof);
 static bool match_any (char c, const char *s);
 static bool next_token_is_sep_op (void);
@@ -329,34 +325,25 @@
 NUMBER	(({D}+\.?{D}*{EXPON}?)|(\.{D}+{EXPON}?)|(0[xX][0-9a-fA-F]+))
 %%
 
+%{
+// Make script and function files start with a bogus token. This makes
+// the parser go down a special path.
+%}
+
 <SCRIPT_FILE_BEGIN>. {
     LEXER_DEBUG ("<SCRIPT_FILE_BEGIN>.");
 
     BEGIN (INITIAL);
     xunput (yytext[0], yytext);
-    COUNT_TOK_AND_RETURN (SCRIPT);
+    COUNT_TOK_AND_RETURN (SCRIPT_FILE);
   }
 
-<NESTED_FUNCTION_END>. {
-    LEXER_DEBUG ("<NESTED_FUNCTION_END>.");
-
-    BEGIN (NESTED_FUNCTION_BEGIN);
-    xunput (yytext[0], yytext);
-
-    lexer_flags.at_beginning_of_statement = true;
-
-    COUNT_TOK_AND_RETURN (';');
-  }
-
-<NESTED_FUNCTION_BEGIN>. {
-    LEXER_DEBUG ("<NESTED_FUNCTION_BEGIN>.");
+<FUNCTION_FILE_BEGIN>. {
+    LEXER_DEBUG ("<FUNCTION_FILE_BEGIN>.");
 
     BEGIN (INITIAL);
     xunput (yytext[0], yytext);
-
-    prep_for_nested_function ();
-
-    COUNT_TOK_AND_RETURN (FCN);
+    COUNT_TOK_AND_RETURN (FUNCTION_FILE);
   }
 
 %{
@@ -1004,8 +991,6 @@
 . {
     LEXER_DEBUG (".");
 
-    // EOF happens here if we are parsing nested functions.
-
     xunput (yytext[0], yytext);
 
     int c = text_yyinput ();
@@ -1058,13 +1043,10 @@
   BEGIN (INITIAL);
 
   parser_end_of_input = false;
-  end_tokens_expected = 0;
 
   while (! symtab_context.empty ())
     symtab_context.pop ();
 
-  symbol_table::reset_parent_scope ();
-
   // We do want a prompt by default.
   promptflag = 1;
 
@@ -1381,32 +1363,6 @@
   delete_buffer (static_cast<YY_BUFFER_STATE> (buf));
 }
 
-static void
-prep_for_function (void)
-{
-  end_tokens_expected++;
-
-  promptflag--;
-
-  lexer_flags.defining_func = true;
-  lexer_flags.parsed_function_name = false;
-
-  if (! (reading_fcn_file || reading_script_file))
-    input_line_number = 1;
-}
-
-static void
-prep_for_nested_function (void)
-{
-  lexer_flags.parsing_nested_function = 1;
-  help_buf.push (std::string ());
-  prep_for_function ();
-  // We're still only expecting one end token for this set of functions.
-  end_tokens_expected--;
-  yylval.tok_val = new token (input_line_number, current_input_column);
-  token_stack.push (yylval.tok_val);
-}
-
 static bool
 inside_any_object_index (void)
 {
@@ -1466,71 +1422,48 @@
 		  && ! (lexer_flags.looking_at_return_list
 			|| lexer_flags.parsed_function_name)))
 	    return 0;
-	  else
-	    {
-	      if (reading_fcn_file && end_tokens_expected == 1)
-		return -1;
-	      else
-		{
-		  yylval.tok_val = new token (token::simple_end, l, c);
-		  lexer_flags.at_beginning_of_statement = true;
-		  end_tokens_expected--;
-		}
-	    }
+
+	  yylval.tok_val = new token (token::simple_end, l, c);
+	  lexer_flags.at_beginning_of_statement = true;
 	  break;
 
 	case end_try_catch_kw:
 	  yylval.tok_val = new token (token::try_catch_end, l, c);
 	  lexer_flags.at_beginning_of_statement = true;
-	  end_tokens_expected--;
 	  break;
 
 	case end_unwind_protect_kw:
 	  yylval.tok_val = new token (token::unwind_protect_end, l, c);
 	  lexer_flags.at_beginning_of_statement = true;
-	  end_tokens_expected--;
 	  break;
 
 	case endfor_kw:
 	  yylval.tok_val = new token (token::for_end, l, c);
 	  lexer_flags.at_beginning_of_statement = true;
-	  end_tokens_expected--;
 	  break;
 
 	case endfunction_kw:
-	  {
-	    if (reading_fcn_file && end_tokens_expected == 1)
-	      return -1;
-	    else
-	      {
-		yylval.tok_val = new token (token::function_end, l, c);
-		lexer_flags.at_beginning_of_statement = true;
-		end_tokens_expected--;
-	      }
-	  }
+	  yylval.tok_val = new token (token::function_end, l, c);
+	  lexer_flags.at_beginning_of_statement = true;
 	  break;
 
 	case endif_kw:
 	  yylval.tok_val = new token (token::if_end, l, c);
 	  lexer_flags.at_beginning_of_statement = true;
-	  end_tokens_expected--;
 	  break;
 
 	case endswitch_kw:
 	  yylval.tok_val = new token (token::switch_end, l, c);
 	  lexer_flags.at_beginning_of_statement = true;
-	  end_tokens_expected--;
 	  break;
 
 	case endwhile_kw:
 	  yylval.tok_val = new token (token::while_end, l, c);
 	  lexer_flags.at_beginning_of_statement = true;
-	  end_tokens_expected--;
 	  break;
 
 	case for_kw:
 	case while_kw:
-	  end_tokens_expected++;
 	  promptflag--;
 	  lexer_flags.looping++;
 	  break;
@@ -1544,57 +1477,22 @@
 	case try_kw:
 	case unwind_protect_kw:
 	  lexer_flags.at_beginning_of_statement = true;
-	  end_tokens_expected++;
 	  promptflag--;
 	  break;
 
 	case if_kw:
 	case switch_kw:
-	  end_tokens_expected++;
 	  promptflag--;
 	  break;
 
 	case function_kw:
-	  {
-	    if (lexer_flags.defining_func)
-	      {
-		if (reading_fcn_file)
-		  {
-		    if (lexer_flags.parsing_nested_function)
-		      {
-			BEGIN (NESTED_FUNCTION_END);
-
-			yylval.tok_val = new token (token::function_end, l, c);
-			token_stack.push (yylval.tok_val);
-
-			lexer_flags.at_beginning_of_statement = true;
-
-			return END;
-		      }
-		    else
-		      {
-			prep_for_nested_function ();
-
-			return FCN;
-		      }
-		  }
-		else
-		  {
-		    error ("nested functions not implemented in this context");
-
-		    if ((reading_fcn_file || reading_script_file)
-			&& ! curr_fcn_file_name.empty ())
-		      error ("near line %d of file `%s.m'",
-			     input_line_number, curr_fcn_file_name.c_str ());
-		    else
-		      error ("near line %d", input_line_number);
-
-		    return LEXICAL_ERROR;
-		  }
-	      }
-	    else
-	      prep_for_function ();
-	  }
+	  promptflag--;
+
+	  lexer_flags.defining_func = true;
+	  lexer_flags.parsed_function_name = false;
+
+	  if (! (reading_fcn_file || reading_script_file))
+	    input_line_number = 1;
 	  break;
 
         case magic_file_kw:
@@ -3254,7 +3152,6 @@
   // Not initially defining a function.
   defining_func = false;
   parsed_function_name = false;
-  parsing_nested_function = 0;
   parsing_class_method = false;
 
   // Not initiallly looking at a function handle.
@@ -3341,11 +3238,17 @@
 }
 
 void
-prep_lexer_for_script (void)
+prep_lexer_for_script_file (void)
 {
   BEGIN (SCRIPT_FILE_BEGIN);
 }
 
+void
+prep_lexer_for_function_file (void)
+{
+  BEGIN (FUNCTION_FILE_BEGIN);
+}
+
 static void
 maybe_warn_separator_insert (char sep)
 {
@@ -3495,6 +3398,8 @@
     case LEXICAL_ERROR: std::cerr << "LEXICAL_ERROR\n\n"; break;
     case FCN: std::cerr << "FCN\n"; break;
     case CLOSE_BRACE: std::cerr << "CLOSE_BRACE\n"; break;
+    case SCRIPT_FILE: std::cerr << "SCRIPT_FILE\n"; break;
+    case FUNCTION_FILE: std::cerr << "FUNCTION_FILE\n"; break;
     case '\n': std::cerr << "\\n\n"; break;
     case '\r': std::cerr << "\\r\n"; break;
     case '\t': std::cerr << "TAB\n"; break;
@@ -3532,12 +3437,8 @@
       std::cerr << "SCRIPT_FILE_BEGIN" << std::endl;
       break;
 
-    case NESTED_FUNCTION_END:
-      std::cerr << "NESTED_FUNCTION_END" << std::endl;
-      break;
-
-    case NESTED_FUNCTION_BEGIN:
-      std::cerr << "NESTED_FUNCTION_BEGIN" << std::endl;
+    case FUNCTION_FILE_BEGIN:
+      std::cerr << "FUNCTION_FILE_BEGIN" << std::endl;
       break;
 
     default:
--- a/src/parse.h	Thu Jul 30 13:23:21 2009 +0200
+++ b/src/parse.h	Thu Jul 30 11:52:58 2009 -0400
@@ -65,17 +65,10 @@
 // Keep track of symbol table information when parsing functions.
 extern std::stack<symbol_table::scope_id> symtab_context;
 
-// Name of parent function when parsing function files that might
-// contain nested functions.
-extern std::string parent_function_name;
-
 // Name of the current class when we are parsing class methods or
 // constructors.
 extern std::string current_class_name;
 
-// Keep a count of how many END tokens we expect.
-extern int end_tokens_expected;
-
 extern OCTINTERP_API std::string
 get_help_from_file (const std::string& nm, bool& symbol_found,
 		    std::string& file);
--- a/src/parse.y	Thu Jul 30 13:23:21 2009 +0200
+++ b/src/parse.y	Thu Jul 30 11:52:58 2009 -0400
@@ -2,6 +2,7 @@
 
 Copyright (C) 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
               2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009  John W. Eaton
+Copyright (C) 2009 David Grundberg
 
 This file is part of Octave.
 
@@ -100,16 +101,27 @@
 // TRUE means input is coming from startup file.
 bool input_from_startup_file = false;
 
-// Keep a count of how many END tokens we expect.
-int end_tokens_expected = 0;
+// = 0 currently outside any function.
+// = 1 inside the primary function or a subfunction.
+// > 1 means we are looking at a function definition that seems to be
+//     inside a function. Note that the function still might not be a
+//     nested function.
+static int current_function_depth = 0;
+
+// Maximum function depth detected. Just here to determine whether
+// we have nested functions or just implicitly ended subfunctions.
+static int max_function_depth = 0;
+
+// FALSE if we are still at the primary function. Subfunctions can
+// only be declared inside function files.
+static int parsing_subfunctions = false;
+
+// Have we found an explicit end to a function?
+static bool endfunction_found = false;
 
 // Keep track of symbol table information when parsing functions.
 std::stack<symbol_table::scope_id> symtab_context;
 
-// Name of parent function when parsing function files that might
-// contain nested functions.
-std::string parent_function_name;
-
 // Name of the current class when we are parsing class methods or
 // constructors.
 std::string current_class_name;
@@ -121,9 +133,12 @@
 // element.
 static bool fcn_file_from_relative_lookup = false;
 
-// If nonzero, this is a pointer to the function we just finished
-// parsing.
-static octave_function *curr_fcn_ptr = 0;
+// Pointer to the primary user function or user script function.
+static octave_function *primary_fcn_ptr = 0;
+
+// Scope where we install all subfunctions and nested functions. Only
+// used while reading function files.
+static symbol_table::scope_id primary_fcn_scope;
 
 // List of autoloads (function -> file mapping).
 static std::map<std::string, std::string> autoload_map;
@@ -424,7 +439,7 @@
 
 // Other tokens.
 %token END_OF_INPUT LEXICAL_ERROR
-%token FCN SCRIPT
+%token FCN SCRIPT_FILE FUNCTION_FILE
 // %token VARARGIN VARARGOUT
 %token CLOSE_BRACE
 
@@ -449,7 +464,8 @@
 %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 script
+%type <tree_command_type> jump_command except_command function script_file
+%type <tree_command_type> function_file function_list
 %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
@@ -494,6 +510,10 @@
 		    promptflag = 1;
 		    YYACCEPT;
 		  }
+		| function_file
+		  {
+		    YYACCEPT;
+		  }
 		| simple_list parse_error
 		  { ABORT_PARSE; }
 		| parse_error
@@ -903,7 +923,7 @@
 		  { $$ = $1; }
 		| function
 		  { $$ = $1; }
-		| script
+		| script_file
 		  { $$ = $1; }
 		;
 
@@ -1112,11 +1132,20 @@
 
 push_fcn_symtab	: // empty
 		  {
+		    current_function_depth++;
+
+		    if (max_function_depth < current_function_depth)
+		      max_function_depth = current_function_depth;
+
 		    symtab_context.push (symbol_table::current_scope ());
 		    symbol_table::set_scope (symbol_table::alloc_scope ());
 
-		    if (! lexer_flags.parsing_nested_function)
-		      symbol_table::set_parent_scope (symbol_table::current_scope ());
+		    if (! reading_script_file && current_function_depth == 1
+			&& ! parsing_subfunctions)
+		      primary_fcn_scope = symbol_table::current_scope ();
+
+		    if (reading_script_file && current_function_depth > 1)
+		      yyerror ("nested functions not implemented in this context");
 		  }
 		;
 
@@ -1218,7 +1247,7 @@
 // Script file
 // ===========
 
-script		: SCRIPT opt_list END_OF_INPUT
+script_file	: SCRIPT_FILE opt_list END_OF_INPUT
 		  {
 		    tree_statement *end_of_script
 		      = make_end ("endscript", input_line_number,
@@ -1230,6 +1259,17 @@
 		  }
 		;
 
+// =============
+// Function file
+// =============
+
+function_file   : FUNCTION_FILE function_list opt_sep END_OF_INPUT
+		;
+
+function_list   : function
+		| function_list sep function
+		;
+
 // ===================
 // Function definition
 // ===================
@@ -1254,12 +1294,8 @@
 		  {
 		    std::string id_name = $1->name ();
 
-		    if (reading_fcn_file
-		        && ! lexer_flags.parsing_nested_function)
-		      parent_function_name = (curr_fcn_file_name == id_name)
-			? id_name : curr_fcn_file_name;
-
 		    lexer_flags.parsed_function_name = true;
+		    lexer_flags.defining_func = false;
 
 		    $$ = $1;
 		  }
@@ -1284,6 +1320,7 @@
 
 function_end	: END
 		  {
+		    endfunction_found = true;
 		    if (end_token_ok ($1, token::function_end))
 		      $$ = make_end ("endfunction", $1->line (), $1->column ());
 		    else
@@ -1291,15 +1328,29 @@
 		  }
 		| END_OF_INPUT
 		  {
-		    if (lexer_flags.parsing_nested_function)
-		      lexer_flags.parsing_nested_function = -1;
-
-		    if (reading_fcn_file || reading_script_file
-			|| get_input_from_eval_string)
-		      $$ = make_end ("endfunction", input_line_number,
-				     current_input_column);
-		    else
-		      YYABORT;
+// A lot of tests are based on the assumption that this is OK
+// 		    if (reading_script_file)
+// 		      {
+// 			yyerror ("function body open at end of script");
+// 			YYABORT;
+// 		      }
+
+		    if (endfunction_found)
+		      {
+			yyerror ("inconsistent function endings -- "
+				 "if one function is explicitly ended, "
+				 "so must all the others");
+			YYABORT;
+		      }
+
+		    if (! reading_fcn_file && ! reading_script_file)
+		      {
+			yyerror ("function body open at end of input");
+			YYABORT;
+		      }
+
+		    $$ = make_end ("endfunction", input_line_number,
+				   current_input_column);
 		  }
 		;
 
@@ -2241,7 +2292,7 @@
   // so that we don't turn eval ("break;") inside a function, script,
   // or loop into a no-op command.
 
-  if (lexer_flags.looping || lexer_flags.defining_func
+  if (lexer_flags.looping || current_function_depth > 0
       || reading_script_file || tree_evaluator::in_fcn_or_script_body
       || tree_evaluator::in_loop_command)
     retval = new tree_break_command (l, c);
@@ -2294,7 +2345,7 @@
       // that we don't turn eval ("return;") inside a function, script,
       // or loop into a no-op command.
 
-      if (lexer_flags.defining_func || reading_script_file
+      if (current_function_depth > 0 || reading_script_file
           || tree_evaluator::in_fcn_or_script_body)
         retval = new tree_return_command (l, c);
       else
@@ -2503,7 +2554,7 @@
   return retval;
 }
 
-// Define a function.
+// Define a script.
 
 static void
 make_script (tree_statement_list *cmds, tree_statement *end_script)
@@ -2529,7 +2580,7 @@
 
   script->stash_fcn_file_time (now);
 
-  curr_fcn_ptr = script;
+  primary_fcn_ptr = script;
 
   // Unmark any symbols that may have been tagged as local variables
   // while parsing (for example, by force_local_variable in lex.l).
@@ -2581,35 +2632,34 @@
   // the file does not match the name of the function stated in the
   // file.  Matlab doesn't provide a diagnostic (it ignores the stated
   // name).
+  if (! autoloading && reading_fcn_file
+      && (current_function_depth == 1
+	  && ! (parsing_subfunctions || lexer_flags.parsing_class_method)))
+  {
+    // FIXME -- should curr_fcn_file_name already be
+    // preprocessed when we get here?  It seems to only be a
+    // problem with relative file names.
+
+    std::string nm = curr_fcn_file_name;
+
+    size_t pos = nm.find_last_of (file_ops::dir_sep_chars ());
+
+    if (pos != std::string::npos)
+      nm = curr_fcn_file_name.substr (pos+1);
+
+    if (nm != id_name)
+      {
+	warning_with_id
+	  ("Octave:function-name-clash",
+	   "function name `%s' does not agree with function file name `%s'",
+	   id_name.c_str (), curr_fcn_file_full_name.c_str ());
+
+	id_name = nm;
+      }
+  }
 
   if (reading_fcn_file || autoloading)
     {
-      if (! (autoloading
-	     || lexer_flags.parsing_nested_function
-	     || lexer_flags.parsing_class_method))
-	{
-	  // FIXME -- should curr_fcn_file_name already be
-	  // preprocessed when we get here?  It seems to only be a
-	  // problem with relative file names.
-
-	  std::string nm = curr_fcn_file_name;
-
-	  size_t pos = nm.find_last_of (file_ops::dir_sep_chars ());
-
-	  if (pos != std::string::npos)
-	    nm = curr_fcn_file_name.substr (pos+1);
-
-	  if (nm != id_name)
-	    {
-	      warning_with_id
-		("Octave:function-name-clash",
-		 "function name `%s' does not agree with function file name `%s'",
-		 id_name.c_str (), curr_fcn_file_full_name.c_str ());
-
-	      id_name = nm;
-	    }
-	}
-
       octave_time now;
 
       fcn->stash_fcn_file_name (curr_fcn_file_full_name);
@@ -2619,11 +2669,11 @@
       if (fcn_file_from_relative_lookup)
 	fcn->mark_relative ();
 
-      if (lexer_flags.parsing_nested_function)
+      if (current_function_depth > 1 || parsing_subfunctions)
         {
-          fcn->stash_parent_fcn_name (parent_function_name);
-          fcn->stash_parent_fcn_scope (symbol_table::parent_scope ());
-	}
+	  fcn->stash_parent_fcn_name (curr_fcn_file_name);
+	  fcn->stash_parent_fcn_scope (primary_fcn_scope);
+        }
 
       if (lexer_flags.parsing_class_method)
 	{
@@ -2653,18 +2703,18 @@
 
   fcn->stash_function_name (id_name);
 
-  if (! help_buf.empty ())
+  if (! help_buf.empty () && current_function_depth == 1
+      && ! parsing_subfunctions)
     {
       fcn->document (help_buf.top ());
 
       help_buf.pop ();
     }
 
-  if (reading_fcn_file && ! lexer_flags.parsing_nested_function)
-    curr_fcn_ptr = fcn;
-  else
-    curr_fcn_ptr = 0;
-
+  if (reading_fcn_file && current_function_depth == 1
+      && ! parsing_subfunctions)
+    primary_fcn_ptr = fcn;
+  
   return fcn;
 }
 
@@ -2693,23 +2743,21 @@
 
       fcn->define_ret_list (ret_list);
 
-      if (lexer_flags.parsing_nested_function)
-	{
+      if (current_function_depth > 1 || parsing_subfunctions)
+        {
+          // FIXME -- is this flag used to determine if the function is a
+          // _subfunction_ somewhere?
 	  fcn->mark_as_nested_function ();
 
-	  symbol_table::install_subfunction (nm, octave_value (fcn));
-
-	  if (lexer_flags.parsing_nested_function < 0)
-	    {
-	      lexer_flags.parsing_nested_function = 0;
-	      symbol_table::reset_parent_scope ();
-	    }
+	  symbol_table::install_subfunction (nm, octave_value (fcn),
+					     primary_fcn_scope);
 	}
-      else if (! curr_fcn_ptr)
+
+      if (! primary_fcn_ptr)
 	{
 	  // FIXME -- there should be a better way to indicate that we
 	  // should create a tree_function_def object other than
-	  // looking at curr_fcn_ptr...
+	  // looking at primary_fcn_ptr...
 
 	  retval = new tree_function_def (fcn);
 	}
@@ -2733,7 +2781,12 @@
   symbol_table::set_scope (symtab_context.top ());
   symtab_context.pop ();
 
-  lexer_flags.defining_func = false;
+  if (reading_fcn_file && current_function_depth == 1
+      && ! parsing_subfunctions)
+    parsing_subfunctions = true;
+
+  current_function_depth--;
+
   lexer_flags.parsed_function_name = false;
   lexer_flags.looking_at_return_list = false;
   lexer_flags.looking_at_parameter_list = false;
@@ -2835,7 +2888,7 @@
       break;
 
     case STATIC:
-      if (lexer_flags.defining_func)
+      if (current_function_depth > 0)
 	retval = new tree_static_command (lst, l, c);
       else
 	{
@@ -2912,7 +2965,7 @@
 static void
 maybe_warn_missing_semi (tree_statement_list *t)
 {
-  if (lexer_flags.defining_func)
+  if (current_function_depth > 0)
     {
       tree_statement *tmp = t->back();
 
@@ -3153,8 +3206,6 @@
 
   // Open function file and parse.
 
-  bool old_reading_fcn_file_state = reading_fcn_file;
-
   FILE *in_stream = command_editor::get_input_stream ();
 
   unwind_protect::add_fcn (command_editor::set_input_stream,
@@ -3164,19 +3215,23 @@
 
   unwind_protect::protect_var (input_line_number);
   unwind_protect::protect_var (current_input_column);
-  unwind_protect::protect_var (end_tokens_expected);
   unwind_protect::protect_var (reading_fcn_file);
   unwind_protect::protect_var (line_editing);
-  unwind_protect::protect_var (parent_function_name);
   unwind_protect::protect_var (current_class_name);
+  unwind_protect::protect_var (current_function_depth);
+  unwind_protect::protect_var (max_function_depth);
+  unwind_protect::protect_var (parsing_subfunctions);
+  unwind_protect::protect_var (endfunction_found);
 
   input_line_number = 1;
   current_input_column = 1;
-  end_tokens_expected = 0;
   reading_fcn_file = true;
   line_editing = false;
-  parent_function_name = "";
   current_class_name = dispatch_type;
+  current_function_depth = 0;
+  max_function_depth = 0;
+  parsing_subfunctions = false;
+  endfunction_found = false;
 
   // The next four lines must be in this order.
   unwind_protect::add_fcn (command_history::ignore_entries, ! Vsaving_history);
@@ -3204,10 +3259,10 @@
 	{
 	  std::string file_type;
 
-	  bool parsing_script = false;
-
 	  unwind_protect::protect_var (get_input_from_eval_string);
 	  unwind_protect::protect_var (parser_end_of_input);
+	  unwind_protect::protect_var (reading_fcn_file);
+	  unwind_protect::protect_var (reading_script_file);
 
 	  get_input_from_eval_string = false;
 	  parser_end_of_input = false;
@@ -3217,7 +3272,6 @@
 	      file_type = "function";
 
 	      unwind_protect::protect_var (Vecho_executing_commands);
-	      unwind_protect::protect_var (reading_fcn_file);
 
 	      Vecho_executing_commands = ECHO_OFF;
 	      reading_fcn_file = true;
@@ -3226,17 +3280,11 @@
 	    {
 	      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;
-
-	      unwind_protect::protect_var (reading_script_file);
-
-	      reading_script_file = true;
-
-	      parsing_script = true;
+	      reading_fcn_file = false;
 	    }
 
+	  reading_script_file = ! reading_fcn_file;
+	  
 	  YY_BUFFER_STATE old_buf = current_buffer ();
 	  YY_BUFFER_STATE new_buf = create_buffer (ffile);
 
@@ -3245,8 +3293,8 @@
 
 	  switch_to_buffer (new_buf);
 
-	  unwind_protect::protect_var (curr_fcn_ptr);
-	  curr_fcn_ptr = 0;
+	  unwind_protect::protect_var (primary_fcn_ptr);
+	  primary_fcn_ptr = 0;
 
 	  reset_parser ();
 
@@ -3259,14 +3307,21 @@
 	  if (! help_txt.empty ())
 	    help_buf.push (help_txt);
 
-	  if (parsing_script)
-	    prep_lexer_for_script ();
+	  if (reading_script_file)
+	    prep_lexer_for_script_file ();
+	  else
+	    prep_lexer_for_function_file ();
 
 	  lexer_flags.parsing_class_method = ! dispatch_type.empty ();
 
 	  int status = yyparse ();
 
-	  fcn_ptr = curr_fcn_ptr;
+	  fcn_ptr = primary_fcn_ptr;
+
+	  if (reading_fcn_file && endfunction_found && max_function_depth > 1)
+	    warning_with_id ("Octave:nested-functions-coerced",
+			     "nested functions are coerced into subfunctions "
+			     "in file %s", ff.c_str ());
 
 	  if (status != 0)
 	    error ("parse error while reading %s file %s",
@@ -3879,6 +3934,10 @@
   unwind_protect::protect_var (parser_end_of_input);
   unwind_protect::protect_var (line_editing);
   unwind_protect::protect_var (current_eval_string);
+  unwind_protect::protect_var (current_function_depth);
+  unwind_protect::protect_var (max_function_depth);
+  unwind_protect::protect_var (parsing_subfunctions);
+  unwind_protect::protect_var (endfunction_found);
 
   input_line_number = 1;
   current_input_column = 1;
@@ -3886,6 +3945,10 @@
   input_from_eval_string_pending = true;
   parser_end_of_input = false;
   line_editing = false;
+  current_function_depth = 0;
+  max_function_depth = 0;
+  parsing_subfunctions = false;
+  endfunction_found = false;
 
   current_eval_string = s;
 
--- a/src/symtab.cc	Thu Jul 30 13:23:21 2009 +0200
+++ b/src/symtab.cc	Thu Jul 30 11:52:58 2009 -0400
@@ -62,8 +62,6 @@
 
 symbol_table::scope_id symbol_table::xcurrent_scope = 1;
 
-symbol_table::scope_id symbol_table::xparent_scope = -1;
-
 symbol_table::context_id symbol_table::xcurrent_context = 0;
 
 // Should Octave always check to see if function files have changed
--- a/src/symtab.h	Thu Jul 30 13:23:21 2009 +0200
+++ b/src/symtab.h	Thu Jul 30 11:52:58 2009 -0400
@@ -888,9 +888,6 @@
 
   static context_id current_context (void) { return xcurrent_context; }
 
-  // We use parent_scope to handle parsing subfunctions.
-  static scope_id parent_scope (void) { return xparent_scope; }
-
   static scope_id alloc_scope (void) { return scope_id_cache::alloc (); }
 
   static void set_scope (scope_id scope)
@@ -942,16 +939,6 @@
       }
   }
 
-  static void set_parent_scope (scope_id scope)
-  {
-    xparent_scope = scope;
-  }
-
-  static void reset_parent_scope (void)
-  {
-    set_parent_scope (-1);
-  }
-
   static void erase_scope (scope_id scope)
   {
     assert (scope != xglobal_scope);
@@ -1207,7 +1194,7 @@
 
   static void install_subfunction (const std::string& name,
 				   const octave_value& fcn,
-				   scope_id scope = xparent_scope)
+				   scope_id scope)
   {
     fcn_table_iterator p = fcn_table.find (name);
 
@@ -1852,9 +1839,6 @@
 
   static scope_id xcurrent_scope;
 
-  // We use parent_scope to handle parsing subfunctions.
-  static scope_id xparent_scope;
-
   static context_id xcurrent_context;
 
   symbol_table (void)