changeset 16203:127cccb037bf

move more global parser and lexer variables to classes * pt-check.h, pt-check.cc (tree_checker::file_name): New data member. (tree_checker::gripe): Use it instead of curr_fcn_file_name. * input.h, input.cc, octave.cc (input_from_command_line_file): Delete global variable and all uses. * parse.h, oct-parse.in.yy (input_from_startup_file): Delete global variable and all uses. * input.h, input.cc, lex.h, lex.ll (curr_fcn_file_name, curr_fcn_file_full_name): Declare as members of lexical_feedback class. Rename to fcn_file_name and fcn_file_full_name. Change all uses. * oct-parse.in.yy (parse_fcn_file): New arg, file. Set curr_lexer->fcn_file_name and curr_lexer->fcn_file_full_name here. (load_fcn_from_file): Pass short file name to parse_fcn_file. * octave.cc (execute_command_line_file): Not here. * lex.h, lex.ll (lexical_feedback::force_script): New data member. * oct-parse.in.yy (parse_fcn_file): Set it here. * lex.h, lex.ll (lexical_feedback::input_from_terminal, lexical_feedback::input_from_file): New functions. * lex.ll (octave_lexer::handle_keyword): Set reading_fcn_file, reading_classdef_file, and reading_script_file. * lex.h, lex.ll (lexical_feedback::token_count): New variable. (COUNT_TOK_AND_RETURN): Increment it here. Don't count '\n' as a token. * lex.h, lex.ll (lexical_feedback::help_text): New variable. * parse.h, parse.in.yy (help_buf): Delete global variable and all uses. (octave_parser::frob_function, octave_parser::make_script): Use help_text. * lex.ll (octave_lexer::process_comment): Cache doc string directly in help_text variable. (looks_like_copyright): Move here from parse.in.yy. * lex.h, lex.ll (octave_lexer::prep_for_file): New function. (octave_lexer::prep_for_function_file, octave_lexer::prep_for_script_file): Delete. * parse.in.yy (INPUT_FILE_BEGIN): New start state. Delete SCRIPT_FILE_BEGIN and FCN_FILE_BEGIN. Tentatively set curr_lexer->reading_script_file to true. (parse_fcn_file): Call curr_lexer->prep_for_file. Don't call gobble_leading_whitespace. Don't attempt to determine function script, or classdef file status here. * parse.in.yy (INPUT_FILE): New token. (SCRIPT_FILE, FUNCTION_FILE): Delete. * lex.ll (octave_lexer::display_token): Update. * parse.in.yy (nl, opt_nl): New non-terminals. (function_file): Delete rule. (file): Rename from script_file. Allow opt_nl before opt_list. Don't make script if reading fcn file. * parse.in.yy (text_getc, class stdio_stream_reader, skip_white_space, looking_at_classdef_keyword, gobble_leading_white_space, looking_at_function_keyword): Delete. (get_help_from_file): Parse file to get help instead of calling gobble_leading_white_space
author John W. Eaton <jwe@octave.org>
date Wed, 06 Mar 2013 14:36:19 -0500
parents 7ce484126bb2
children 92f8bfae9b13 a8f9eb92fa6e
files libinterp/interpfcn/input.cc libinterp/interpfcn/input.h libinterp/interpfcn/load-path.cc libinterp/octave.cc libinterp/parse-tree/lex.h libinterp/parse-tree/lex.ll libinterp/parse-tree/oct-parse.in.yy libinterp/parse-tree/parse.h libinterp/parse-tree/pt-check.cc libinterp/parse-tree/pt-check.h
diffstat 10 files changed, 234 insertions(+), 550 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/interpfcn/input.cc
+++ b/libinterp/interpfcn/input.cc
@@ -94,19 +94,9 @@
 // Character to append after successful command-line completion attempts.
 static char Vcompletion_append_char = ' ';
 
-// TRUE means that input is coming from a file that was named on
-// the command line.
-bool input_from_command_line_file = false;
-
 // TRUE means that stdin is a terminal, not a pipe or redirected file.
 bool stdin_is_tty = false;
 
-// Simple name of function file we are reading.
-std::string curr_fcn_file_name;
-
-// Full name of file we are reading.
-std::string curr_fcn_file_full_name;
-
 // TRUE means this is an interactive shell.
 bool interactive = false;
 
@@ -518,21 +508,13 @@
   VPS1 = prompt;
 
   if (! (interactive || forced_interactive)
-      || (CURR_LEXER->reading_fcn_file
-          || CURR_LEXER->reading_classdef_file
-          || CURR_LEXER->reading_script_file
-          || CURR_LEXER->input_from_eval_string ()
-          || input_from_startup_file
-          || input_from_command_line_file))
+      || CURR_LEXER->reading_fcn_file
+      || CURR_LEXER->reading_classdef_file
+      || CURR_LEXER->reading_script_file
+      || CURR_LEXER->input_from_eval_string ())
     {
       frame.protect_var (forced_interactive);
       forced_interactive = true;
-
-      frame.protect_var (input_from_startup_file);
-      input_from_startup_file = false;
-
-      frame.protect_var (input_from_command_line_file);
-      input_from_command_line_file = false;
     }
 
   // octave_parser constructor sets this for us.
@@ -661,9 +643,6 @@
 
   if (! (error_state || input_buf.empty ()))
     {
-      if (! input_from_startup_file)
-        command_history::add (input_buf);
-
       size_t len = input_buf.length ();
 
       octave_diary << input_buf;
--- a/libinterp/interpfcn/input.h
+++ b/libinterp/interpfcn/input.h
@@ -37,19 +37,9 @@
 
 extern OCTINTERP_API FILE *get_input_from_stdin (void);
 
-// TRUE means that input is coming from a file that was named on
-// the command line.
-extern bool input_from_command_line_file;
-
 // TRUE means that stdin is a terminal, not a pipe or redirected file.
 extern bool stdin_is_tty;
 
-// Simple name of function file we are reading.
-extern std::string curr_fcn_file_name;
-
-// Full name of file we are reading.
-extern std::string curr_fcn_file_full_name;
-
 // TRUE means this is an interactive shell.
 extern bool interactive;
 
--- a/libinterp/interpfcn/load-path.cc
+++ b/libinterp/interpfcn/load-path.cc
@@ -1956,10 +1956,6 @@
 
   unwind_protect frame;
 
-  frame.protect_var (input_from_startup_file);
-
-  input_from_startup_file = true;
-
   std::string file = file_ops::concat (dir, script_file);
 
   file_stat fs (file);
--- a/libinterp/octave.cc
+++ b/libinterp/octave.cc
@@ -355,10 +355,6 @@
 {
   unwind_protect frame;
 
-  frame.protect_var (input_from_startup_file);
-
-  input_from_startup_file = true;
-
   std::string context;
 
   bool verbose = (verbose_flag && ! inhibit_startup_message);
@@ -488,28 +484,18 @@
   octave_initialized = true;
 
   frame.protect_var (interactive);
-  frame.protect_var (input_from_command_line_file);
-
-  frame.protect_var (curr_fcn_file_name);
-  frame.protect_var (curr_fcn_file_full_name);
 
   frame.protect_var (octave_program_invocation_name);
   frame.protect_var (octave_program_name);
 
   interactive = false;
-  input_from_command_line_file = true;
 
-  curr_fcn_file_name = fname;
-  curr_fcn_file_full_name = curr_fcn_file_name;
-
-  octave_program_invocation_name = curr_fcn_file_name;
+  octave_program_invocation_name = fname;
 
-  size_t pos = curr_fcn_file_name.find_last_of (file_ops::dir_sep_chars ());
+  size_t pos = fname.find_last_of (file_ops::dir_sep_chars ());
 
-  std::string tmp = (pos != std::string::npos)
-    ? curr_fcn_file_name.substr (pos+1) : curr_fcn_file_name;
-
-  octave_program_name = tmp;
+  octave_program_name
+    = (pos != std::string::npos) ? fname.substr (pos+1) : fname;
 
   std::string context;
   bool verbose = false;
--- a/libinterp/parse-tree/lex.h
+++ b/libinterp/parse-tree/lex.h
@@ -170,12 +170,14 @@
       looking_for_object_index (false), 
       looking_at_indirect_ref (false), parsing_class_method (false),
       maybe_classdef_get_set_method (false), parsing_classdef (false),
-      quote_is_transpose (false), reading_fcn_file (false),
-      reading_script_file (false), reading_classdef_file (false),
+      quote_is_transpose (false), force_script (false),
+      reading_fcn_file (false), reading_script_file (false),
+      reading_classdef_file (false),
       input_line_number (1), current_input_column (1),
       bracketflag (0), braceflag (0),
       looping (0), defining_func (0), looking_at_function_handle (0),
-      block_comment_nesting_level (0),
+      block_comment_nesting_level (0), token_count (0),
+      help_text (), fcn_file_name (), fcn_file_full_name (),
       looking_at_object_index (), parsed_function_name (),
       pending_local_variables (), nesting_level (), token_stack ()
   {
@@ -244,6 +246,10 @@
   // return transpose or start a string?
   bool quote_is_transpose;
 
+  // TRUE means treat the current file as a script even if the first
+  // token is "function" or "classdef".
+  bool force_script;
+
   // TRUE means we're parsing a function file.
   bool reading_fcn_file;
 
@@ -277,6 +283,19 @@
   // nestng level for blcok comments.
   int block_comment_nesting_level;
 
+  // Count of tokens recognized by this lexer since initialized or
+  // since the last reset.
+  size_t token_count;
+
+  // The current help text.
+  std::string help_text;
+
+  // Simple name of function file we are reading.
+  std::string fcn_file_name;
+
+  // Full name of file we are reading.
+  std::string fcn_file_full_name;
+
   // if the front of the list is true, the closest paren, brace, or
   // bracket nesting is an index for an object.
   std::list<bool> looking_at_object_index;
@@ -372,9 +391,7 @@
 
   void reset (void);
 
-  void prep_for_script_file (void);
-
-  void prep_for_function_file (void);
+  void prep_for_file (void);
 
   int read (char *buf, unsigned int max_size);
 
@@ -480,6 +497,16 @@
     return input_reader.input_source ();
   }
 
+  bool input_from_terminal (void) const
+  {
+    return input_source () == "terminal";
+  }
+
+  bool input_from_file (void) const
+  {
+    return input_source () == "file";
+  }
+
   bool input_from_eval_string (void) const
   {
     return input_source () == "eval_string";
--- a/libinterp/parse-tree/lex.ll
+++ b/libinterp/parse-tree/lex.ll
@@ -46,8 +46,7 @@
 %s COMMAND_START
 %s MATRIX_START
 
-%x SCRIPT_FILE_BEGIN
-%x FUNCTION_FILE_BEGIN
+%x INPUT_FILE_BEGIN
 
 %{
 
@@ -147,7 +146,11 @@
 #define COUNT_TOK_AND_RETURN(tok) \
   do \
     { \
-      Vtoken_count++; \
+      if (tok != '\n') \
+        { \
+          Vtoken_count++; \
+          curr_lexer->token_count++; \
+        } \
       DISPLAY_TOK_AND_RETURN (tok); \
     } \
   while (0)
@@ -256,20 +259,17 @@
 // the parser go down a special path.
 %}
 
-<SCRIPT_FILE_BEGIN>. {
-    LEXER_DEBUG ("<SCRIPT_FILE_BEGIN>.");
+<INPUT_FILE_BEGIN>. {
+    LEXER_DEBUG ("<INPUT_FILE_BEGIN>.");
 
     BEGIN (INITIAL);
     curr_lexer->xunput (yytext[0]);
-    COUNT_TOK_AND_RETURN (SCRIPT_FILE);
-  }
-
-<FUNCTION_FILE_BEGIN>. {
-    LEXER_DEBUG ("<FUNCTION_FILE_BEGIN>.");
-
-    BEGIN (INITIAL);
-    curr_lexer->xunput (yytext[0]);
-    COUNT_TOK_AND_RETURN (FUNCTION_FILE);
+
+    // May be reset later if we see "function" or "classdef" appears
+    // as the first token.
+    curr_lexer->reading_script_file = true;
+
+    DISPLAY_TOK_AND_RETURN (INPUT_FILE);
   }
 
 %{
@@ -1324,6 +1324,7 @@
   maybe_classdef_get_set_method = false;
   parsing_classdef = false;
   quote_is_transpose = false;
+  force_script = false;
   reading_fcn_file = false;
   reading_script_file = false;
   reading_classdef_file = false;
@@ -1335,7 +1336,10 @@
   defining_func = 0;
   looking_at_function_handle = 0;
   block_comment_nesting_level = 0;
-
+  token_count = 0;
+  help_text = "";
+  fcn_file_name = "";
+  fcn_file_full_name = "";
   looking_at_object_index.clear ();
   looking_at_object_index.push_front (false);
 
@@ -1451,31 +1455,20 @@
       && ! (reading_fcn_file
             || reading_classdef_file
             || reading_script_file
-            || input_from_eval_string ()
-            || input_from_startup_file))
+            || input_from_eval_string ()))
     yyrestart (stdin, scanner);
 
-  // Clear the buffer for help text.
-  while (! help_buf.empty ())
-    help_buf.pop ();
-
   lexical_feedback::reset ();
 }
 
 void
-octave_lexer::prep_for_script_file (void)
+octave_lexer::prep_for_file (void)
 {
   OCTAVE_YYG;
 
-  BEGIN (SCRIPT_FILE_BEGIN);
-}
-
-void
-octave_lexer::prep_for_function_file (void)
-{
-  OCTAVE_YYG;
-
-  BEGIN (FUNCTION_FILE_BEGIN);
+  reading_script_file = true;
+
+  BEGIN (INPUT_FILE_BEGIN);
 }
 
 int
@@ -1517,9 +1510,9 @@
       warning ("block comment open at end of input");
 
       if ((reading_fcn_file || reading_script_file || reading_classdef_file)
-          && ! curr_fcn_file_name.empty ())
+          && ! fcn_file_name.empty ())
         warning ("near line %d of file '%s.m'",
-                 input_line_number, curr_fcn_file_name.c_str ());
+                 input_line_number, fcn_file_name.c_str ());
     }
 
   TOK_RETURN (END_OF_INPUT);
@@ -1691,11 +1684,11 @@
         case static_kw:
           if ((reading_fcn_file || reading_script_file
                || reading_classdef_file)
-              && ! curr_fcn_file_full_name.empty ())
+              && ! fcn_file_full_name.empty ())
             warning_with_id ("Octave:deprecated-keyword",
                              "the 'static' keyword is obsolete and will be removed from a future version of Octave; please use 'persistent' instead; near line %d of file '%s'",
                              input_line_number,
-                             curr_fcn_file_full_name.c_str ());
+                             fcn_file_full_name.c_str ());
           else
             warning_with_id ("Octave:deprecated-keyword",
                              "the 'static' keyword is obsolete and will be removed from a future version of Octave; please use 'persistent' instead; near line %d",
@@ -1834,6 +1827,12 @@
         case classdef_kw:
           // 'classdef' is always a keyword.
           promptflag--;
+
+          if (! force_script && token_count == 0 && input_from_file ())
+            {
+              reading_classdef_file = true;
+              reading_script_file = false;
+            }
           break;
 
         case function_kw:
@@ -1842,6 +1841,12 @@
           defining_func++;
           parsed_function_name.push (false);
 
+          if (! force_script && token_count == 0 && input_from_file ())
+            {
+              reading_fcn_file = true;
+              reading_script_file = false;
+            }
+
           if (! (reading_fcn_file || reading_script_file
                  || reading_classdef_file))
             input_line_number = 1;
@@ -1851,8 +1856,8 @@
           {
             if ((reading_fcn_file || reading_script_file
                  || reading_classdef_file)
-                && ! curr_fcn_file_full_name.empty ())
-              tok_val = new token (curr_fcn_file_full_name, l, c);
+                && ! fcn_file_full_name.empty ())
+              tok_val = new token (fcn_file_full_name, l, c);
             else
               tok_val = new token ("stdin", l, c);
           }
@@ -2131,6 +2136,21 @@
   return buf;
 }
 
+static bool
+looks_like_copyright (const std::string& s)
+{
+  bool retval = false;
+
+  if (! s.empty ())
+    {
+      size_t offset = s.find_first_not_of (" \t");
+
+      retval = (s.substr (offset, 9) == "Copyright" || s.substr (offset, 6) == "Author");
+    }
+
+  return retval;
+}
+
 int
 octave_lexer::process_comment (bool start_in_block, bool& eof)
 {
@@ -2138,11 +2158,6 @@
 
   eof = false;
 
-  std::string help_txt;
-
-  if (! help_buf.empty ())
-    help_txt = help_buf.top ();
-
   char *yytxt = flex_yytext ();
   flex_stream_reader flex_reader (this, yytxt);
 
@@ -2156,13 +2171,9 @@
   if (lexer_debug_flag)
     std::cerr << "C: " << txt << std::endl;
 
-  if (help_txt.empty () && nesting_level.none ())
-    {
-      if (! help_buf.empty ())
-        help_buf.pop ();
-
-      help_buf.push (txt);
-    }
+  if (nesting_level.none () && help_text.empty () && ! txt.empty ()
+      && ! looks_like_copyright (txt))
+    help_text = txt;
 
   octave_comment_buffer::append (txt);
 
@@ -3513,7 +3524,7 @@
 void
 octave_lexer::maybe_warn_separator_insert (char sep)
 {
-  std::string nm = curr_fcn_file_full_name;
+  std::string nm = fcn_file_full_name;
 
   if (nm.empty ())
     warning_with_id ("Octave:separator-insert",
@@ -3528,7 +3539,7 @@
 void
 octave_lexer::gripe_single_quote_string (void)
 {
-  std::string nm = curr_fcn_file_full_name;
+  std::string nm = fcn_file_full_name;
 
   if (nm.empty ())
     warning_with_id ("Octave:single-quote-string",
@@ -3543,7 +3554,7 @@
 void
 octave_lexer::gripe_matlab_incompatible (const std::string& msg)
 {
-  std::string nm = curr_fcn_file_full_name;
+  std::string nm = fcn_file_full_name;
 
   if (nm.empty ())
     warning_with_id ("Octave:matlab-incompatible",
@@ -3707,8 +3718,7 @@
     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 INPUT_FILE: std::cerr << "INPUT_FILE\n"; break;
     case SUPERCLASSREF: std::cerr << "SUPERCLASSREF\n"; break;
     case METAQUERY: std::cerr << "METAQUERY\n"; break;
     case GET: std::cerr << "GET\n"; break;
@@ -3750,12 +3760,8 @@
       std::cerr << "MATRIX_START" << std::endl;
       break;
 
-    case SCRIPT_FILE_BEGIN:
-      std::cerr << "SCRIPT_FILE_BEGIN" << std::endl;
-      break;
-
-    case FUNCTION_FILE_BEGIN:
-      std::cerr << "FUNCTION_FILE_BEGIN" << std::endl;
+    case INPUT_FILE_BEGIN:
+      std::cerr << "INPUT_FILE_BEGIN" << std::endl;
       break;
 
     default:
--- a/libinterp/parse-tree/oct-parse.in.yy
+++ b/libinterp/parse-tree/oct-parse.in.yy
@@ -95,9 +95,6 @@
 #define malloc GNULIB_NAMESPACE::malloc
 #endif
 
-// Buffer for help text snagged from function files.
-std::stack<std::string> help_buf;
-
 // TRUE means we are using readline.
 // (--no-line-editing)
 bool line_editing = true;
@@ -105,9 +102,6 @@
 // TRUE means we printed messages about reading startup files.
 bool reading_startup_message_printed = false;
 
-// TRUE means input is coming from startup file.
-bool input_from_startup_file = false;
-
 // Keep track of symbol table information when parsing functions.
 symtab_context parser_symtab_context;
 
@@ -238,14 +232,14 @@
 
 // Other tokens.
 %token END_OF_INPUT LEXICAL_ERROR
-%token FCN SCRIPT_FILE FUNCTION_FILE CLASSDEF
+%token FCN INPUT_FILE CLASSDEF
 // %token VARARGIN VARARGOUT
 %token CLOSE_BRACE
 
 // Nonterminals we construct.
 %type <comment_type> stash_comment function_beg classdef_beg
 %type <comment_type> properties_beg methods_beg events_beg enum_beg
-%type <sep_type> sep_no_nl opt_sep_no_nl sep opt_sep opt_comma
+%type <sep_type> sep_no_nl opt_sep_no_nl nl opt_nl sep opt_sep opt_comma
 %type <tree_type> input
 %type <tree_constant_type> string constant magic_colon
 %type <tree_anon_fcn_handle_type> anon_fcn_handle
@@ -267,8 +261,7 @@
 %type <tree_parameter_list_type> superclasses opt_superclasses
 %type <tree_command_type> command select_command loop_command
 %type <tree_command_type> jump_command except_command function
-%type <tree_command_type> script_file classdef
-%type <tree_command_type> function_file function_list
+%type <tree_command_type> file classdef
 %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
@@ -329,8 +322,6 @@
                     promptflag = 1;
                     YYACCEPT;
                   }
-                | function_file
-                  { YYACCEPT; }
                 | simple_list parse_error
                   { ABORT_PARSE; }
                 | parse_error
@@ -761,7 +752,7 @@
                   { $$ = $1; }
                 | function
                   { $$ = $1; }
-                | script_file
+                | file
                   { $$ = $1; }
                 | classdef
                   { $$ = $1; }
@@ -844,7 +835,7 @@
 
 if_cmd_list1    : expression opt_sep opt_list
                   {
-                    $1->mark_braindead_shortcircuit (curr_fcn_file_full_name);
+                    $1->mark_braindead_shortcircuit (curr_lexer->fcn_file_full_name);
 
                     $$ = curr_parser.start_if_command ($1, $3);
                   }
@@ -857,7 +848,7 @@
 
 elseif_clause   : ELSEIF stash_comment opt_sep expression opt_sep opt_list
                   {
-                    $4->mark_braindead_shortcircuit (curr_fcn_file_full_name);
+                    $4->mark_braindead_shortcircuit (curr_lexer->fcn_file_full_name);
 
                     $$ = curr_parser.make_elseif_clause ($1, $4, $6, $2);
                   }
@@ -916,7 +907,7 @@
 
 loop_command    : WHILE stash_comment expression opt_sep opt_list END
                   {
-                    $3->mark_braindead_shortcircuit (curr_fcn_file_full_name);
+                    $3->mark_braindead_shortcircuit (curr_lexer->fcn_file_full_name);
 
                     if (! ($$ = curr_parser.make_while_command ($1, $3, $5, $6, $2)))
                       ABORT_PARSE;
@@ -1013,11 +1004,13 @@
 
                     curr_parser.function_scopes.push_back (symbol_table::current_scope ());
 
-                    if (! curr_lexer->reading_script_file && curr_parser.curr_fcn_depth == 1
+                    if (! curr_lexer->reading_script_file
+                        && curr_parser.curr_fcn_depth == 1
                         && ! curr_parser.parsing_subfunctions)
                       curr_parser.primary_fcn_scope = symbol_table::current_scope ();
 
-                    if (curr_lexer->reading_script_file && curr_parser.curr_fcn_depth > 1)
+                    if (curr_lexer->reading_script_file
+                        && curr_parser.curr_fcn_depth > 1)
                       curr_parser.bison_error ("nested functions not implemented in this context");
                   }
                 ;
@@ -1117,35 +1110,26 @@
                   }
                 ;
 
-// ===========
-// Script file
-// ===========
-
-script_file     : SCRIPT_FILE opt_list END_OF_INPUT
+// =======================
+// Script or function file
+// =======================
+
+file            : INPUT_FILE opt_nl opt_list END_OF_INPUT
                   {
-                    tree_statement *end_of_script
-                      = curr_parser.make_end ("endscript",
-                                               curr_lexer->input_line_number,
-                                               curr_lexer->current_input_column);
-
-                    curr_parser.make_script ($2, end_of_script);
+                    if (! curr_lexer->reading_fcn_file)
+                      {
+                        tree_statement *end_of_script
+                          = curr_parser.make_end ("endscript",
+                                                  curr_lexer->input_line_number,
+                                                  curr_lexer->current_input_column);
+
+                        curr_parser.make_script ($3, end_of_script);
+                      }
 
                     $$ = 0;
                   }
                 ;
 
-// =============
-// Function file
-// =============
-
-function_file   : FUNCTION_FILE function_list opt_sep END_OF_INPUT
-                  { $$ = 0; }
-                ;
-
-function_list   : function
-                | function_list sep function
-                ;
-
 // ===================
 // Function definition
 // ===================
@@ -1153,8 +1137,8 @@
 function_beg    : push_fcn_symtab FCN stash_comment
                   {
                     $$ = $3;
-
-                    if (curr_lexer->reading_classdef_file || curr_lexer->parsing_classdef)
+                    if (curr_lexer->reading_classdef_file
+                        || curr_lexer->parsing_classdef)
                       curr_lexer->maybe_classdef_get_set_method = true;
                   }
                 ;
@@ -1441,6 +1425,18 @@
                   { $$ = $1; }
                 ;
 
+opt_nl          : // empty
+                  { $$ = 0; }
+                | nl
+                  { $$ = $1; }
+                ;
+
+nl              : '\n'
+                  { $$ = '\n'; }
+                | nl '\n'
+                  { $$ = $1; }
+                ;
+
 sep             : ','
                   { $$ = ','; }
                 | ';'
@@ -1667,7 +1663,7 @@
   if (expr->is_assignment_expression ()
       && expr->paren_count () < 2)
     {
-      if (curr_fcn_file_full_name.empty ())
+      if (curr_lexer->fcn_file_full_name.empty ())
         warning_with_id
           ("Octave:assign-as-truth-value",
            "suggest parenthesis around assignment used as truth value");
@@ -1675,7 +1671,7 @@
         warning_with_id
           ("Octave:assign-as-truth-value",
            "suggest parenthesis around assignment used as truth value near line %d, column %d in file '%s'",
-           expr->line (), expr->column (), curr_fcn_file_full_name.c_str ());
+           expr->line (), expr->column (), curr_lexer->fcn_file_full_name.c_str ());
     }
 }
 
@@ -1686,14 +1682,14 @@
 {
   if (! expr->is_constant ())
     {
-      if (curr_fcn_file_full_name.empty ())
+      if (curr_lexer->fcn_file_full_name.empty ())
         warning_with_id ("Octave:variable-switch-label",
                          "variable switch label");
       else
         warning_with_id
           ("Octave:variable-switch-label",
            "variable switch label near line %d, column %d in file '%s'",
-           expr->line (), expr->column (), curr_fcn_file_full_name.c_str ());
+           expr->line (), expr->column (), curr_lexer->fcn_file_full_name.c_str ());
     }
 }
 
@@ -1955,7 +1951,7 @@
     = new tree_anon_fcn_handle (param_list, ret_list, body, fcn_scope, l, c);
   // FIXME: Stash the filename.  This does not work and produces
   // errors when executed.
-  //retval->stash_file_name (curr_fcn_file_name);
+  //retval->stash_file_name (curr_lexer->fcn_file_name);
 
   return retval;
 }
@@ -2579,22 +2575,17 @@
 octave_parser::make_script (tree_statement_list *cmds,
                             tree_statement *end_script)
 {
-  std::string doc_string;
-
-  if (! help_buf.empty ())
-    {
-      doc_string = help_buf.top ();
-      help_buf.pop ();
-    }
-
   if (! cmds)
     cmds = new tree_statement_list ();
 
   cmds->append (end_script);
 
   octave_user_script *script
-    = new octave_user_script (curr_fcn_file_full_name, curr_fcn_file_name,
-                              cmds, doc_string);
+    = new octave_user_script (curr_lexer->fcn_file_full_name,
+                              curr_lexer->fcn_file_name,
+                              cmds, curr_lexer->help_text);
+
+  curr_lexer->help_text = "";
 
   octave_time now;
 
@@ -2657,23 +2648,23 @@
   if (! autoloading && curr_lexer->reading_fcn_file
       && curr_fcn_depth == 1 && ! parsing_subfunctions)
   {
-    // FIXME -- should curr_fcn_file_name already be
+    // FIXME -- should curr_lexer->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;
+    std::string nm = curr_lexer->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);
+      nm = curr_lexer->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.c_str (), curr_lexer->fcn_file_full_name.c_str ());
 
         id_name = nm;
       }
@@ -2683,7 +2674,7 @@
     {
       octave_time now;
 
-      fcn->stash_fcn_file_name (curr_fcn_file_full_name);
+      fcn->stash_fcn_file_name (curr_lexer->fcn_file_full_name);
       fcn->stash_fcn_file_time (now);
       fcn->mark_as_system_fcn_file ();
 
@@ -2692,7 +2683,7 @@
 
       if (curr_fcn_depth > 1 || parsing_subfunctions)
         {
-          fcn->stash_parent_fcn_name (curr_fcn_file_name);
+          fcn->stash_parent_fcn_name (curr_lexer->fcn_file_name);
 
           if (curr_fcn_depth > 1)
             fcn->stash_parent_fcn_scope (function_scopes[function_scopes.size ()-2]);
@@ -2718,24 +2709,25 @@
         warning_with_id ("Octave:future-time-stamp",
                          "time stamp for '%s' is in the future", nm.c_str ());
     }
-  else if (! (input_from_tmp_history_file || input_from_startup_file)
+  else if (! input_from_tmp_history_file
+           && ! curr_lexer->force_script
            && curr_lexer->reading_script_file
-           && curr_fcn_file_name == id_name)
+           && curr_lexer->fcn_file_name == id_name)
     {
       warning ("function '%s' defined within script file '%s'",
-               id_name.c_str (), curr_fcn_file_full_name.c_str ());
+               id_name.c_str (), curr_lexer->fcn_file_full_name.c_str ());
     }
 
   fcn->stash_function_name (id_name);
   fcn->stash_fcn_location (curr_lexer->input_line_number,
                            curr_lexer->current_input_column);
 
-  if (! help_buf.empty () && curr_fcn_depth == 1
+  if (! curr_lexer->help_text.empty () && curr_fcn_depth == 1
       && ! parsing_subfunctions)
     {
-      fcn->document (help_buf.top ());
-
-      help_buf.pop ();
+      fcn->document (curr_lexer->help_text);
+
+      curr_lexer->help_text = "";
     }
 
   if (curr_lexer->reading_fcn_file && curr_fcn_depth == 1
@@ -2947,7 +2939,7 @@
         {
           if (curr_lexer->reading_script_file)
             warning ("ignoring persistent declaration near line %d of file '%s'",
-                     l, curr_fcn_file_full_name.c_str ());
+                     l, curr_lexer->fcn_file_full_name.c_str ());
           else
             warning ("ignoring persistent declaration near line %d", l);
         }
@@ -3032,7 +3024,7 @@
         warning_with_id
           ("Octave:missing-semicolon",
            "missing semicolon near line %d, column %d in file '%s'",
-            tmp->line (), tmp->column (), curr_fcn_file_full_name.c_str ());
+            tmp->line (), tmp->column (), curr_lexer->fcn_file_full_name.c_str ());
     }
 }
 
@@ -3100,7 +3092,7 @@
 
   if (curr_lexer->reading_fcn_file || curr_lexer->reading_script_file || curr_lexer->reading_classdef_file)
     output_buf << "parse error near line " << curr_lexer->input_line_number
-               << " of file " << curr_fcn_file_full_name;
+               << " of file " << curr_lexer->fcn_file_full_name;
   else
     output_buf << "parse error:";
 
@@ -3152,216 +3144,9 @@
     fclose (static_cast<FILE *> (f));
 }
 
-static bool
-looks_like_copyright (const std::string& s)
-{
-  bool retval = false;
-
-  if (! s.empty ())
-    {
-      size_t offset = s.find_first_not_of (" \t");
-
-      retval = (s.substr (offset, 9) == "Copyright" || s.substr (offset, 6) == "Author");
-    }
-
-  return retval;
-}
-
-static int
-text_getc (FILE *f)
-{
-  int c = gnulib::getc (f);
-
-  // Convert CRLF into just LF and single CR into LF.
-
-  if (c == '\r')
-    {
-      c = gnulib::getc (f);
-
-      if (c != '\n')
-        {
-          ungetc (c, f);
-          c = '\n';
-        }
-    }
-
-  return c;
-}
-
-class
-stdio_stream_reader : public stream_reader
-{
-public:
-
-  stdio_stream_reader (FILE *f_arg, int& l, int& c)
-    : stream_reader (), f (f_arg), line_num (l), column_num (c)
-  { }
-
-  int getc (void)
-  {
-    char c = ::text_getc (f);
-
-    if (c == '\n')
-      {
-        line_num++;
-        column_num = 0;
-      }
-    else
-      {
-        // FIXME -- try to be smarter about tabs?
-        column_num++;
-      }
-        
-    return c;
-  }
-
-  int ungetc (int c)
-  {
-    if (c == '\n')
-      {   
-        line_num--;
-        column_num = 0;
-      }
-    else
-      {
-        // FIXME -- try to be smarter about tabs?
-        column_num--;
-      }
-
-    return ::ungetc (c, f);
-  }
-
-private:
-
-  FILE *f;
-
-  int& line_num;
-
-  int& column_num;
-
-  // No copying!
-
-  stdio_stream_reader (const  stdio_stream_reader&);
-
-  stdio_stream_reader & operator = (const  stdio_stream_reader&);
-};
-
-static bool
-skip_white_space (stream_reader& reader)
-{
-  int c = 0;
-
-  while ((c = reader.getc ()) != EOF)
-    {
-      switch (c)
-        {
-        case ' ':
-        case '\t':
-        case '\n':
-          break;
-
-        default:
-          reader.ungetc (c);
-          goto done;
-        }
-    }
-
- done:
-
-  return (c == EOF);
-}
-
-static bool
-looking_at_classdef_keyword (FILE *ffile)
-{
-  bool status = false;
-
-  long pos = gnulib::ftell (ffile);
-
-  char buf [10];
-  gnulib::fgets (buf, 10, ffile);
-  size_t len = strlen (buf);
-  if (len > 8 && strncmp (buf, "classdef", 8) == 0
-      && ! (isalnum (buf[8]) || buf[8] == '_'))
-    status = true;
-
-  gnulib::fseek (ffile, pos, SEEK_SET);
-
-  return status;
- }
-
-static std::string
-gobble_leading_white_space (FILE *ffile, bool& eof, int& line_num,
-                            int& column_num)
-{
-  std::string help_txt;
-
-  eof = false;
-
-  // TRUE means we have already cached the help text.
-  bool have_help_text = false;
-
-  std::string txt;
-
-  stdio_stream_reader stdio_reader (ffile, line_num, column_num);
-
-  while (true)
-    {
-      eof = skip_white_space (stdio_reader);
-
-      if (eof)
-        break;
-
-      txt = CURR_LEXER->grab_comment_block (stdio_reader, true, eof);
-
-      if (txt.empty ())
-        break;
-
-      if (! (have_help_text || looks_like_copyright (txt)))
-        {
-          help_txt = txt;
-          have_help_text = true;
-        }
-
-      octave_comment_buffer::append (txt);
-
-      if (eof)
-        break;
-    }
-
-  return help_txt;
-}
-
-static std::string
-gobble_leading_white_space (FILE *ffile, bool& eof)
-{
-  int line_num = 1;
-  int column_num = 1;
-
-  return gobble_leading_white_space (ffile, eof, line_num, column_num);
-}
-
-static bool
-looking_at_function_keyword (FILE *ffile)
-{
-  bool status = false;
-
-  long pos = gnulib::ftell (ffile);
-
-  char buf [10];
-  gnulib::fgets (buf, 10, ffile);
-  size_t len = strlen (buf);
-  if (len > 8 && strncmp (buf, "function", 8) == 0
-      && ! (isalnum (buf[8]) || buf[8] == '_'))
-    status = true;
-
-  gnulib::fseek (ffile, pos, SEEK_SET);
-
-  return status;
-}
-
 static octave_function *
-parse_fcn_file (const std::string& ff, const std::string& dispatch_type,
+parse_fcn_file (const std::string& full_file, const std::string& file,
+                const std::string& dispatch_type,
                 bool require_file, bool force_script, bool autoload,    
                 bool relative_lookup, const std::string& warn_for)
 {
@@ -3386,8 +3171,8 @@
 
   FILE *ffile = 0;
 
-  if (! ff.empty ())
-    ffile = gnulib::fopen (ff.c_str (), "rb");
+  if (! full_file.empty ())
+    ffile = gnulib::fopen (full_file.c_str (), "rb");
 
   frame.add_fcn (safe_fclose, ffile);
 
@@ -3400,137 +3185,75 @@
 
       octave_parser curr_parser (ffile);
 
-      curr_parser.curr_lexer->reading_fcn_file = true;
-
       curr_parser.curr_class_name = dispatch_type;
       curr_parser.autoloading = autoload;
       curr_parser.fcn_file_from_relative_lookup = relative_lookup;
 
-      std::string help_txt
-        = gobble_leading_white_space
-            (ffile, eof,
-             curr_parser.curr_lexer->input_line_number,
-             curr_parser.curr_lexer->current_input_column);
-
-      if (! help_txt.empty ())
-        help_buf.push (help_txt);
-
-      if (! eof)
-        {
-          std::string file_type;
-
-          frame.protect_var (Vecho_executing_commands);
-
-          Vecho_executing_commands = ECHO_OFF;
-
-          if (! force_script && looking_at_function_keyword (ffile))
-            {
-              file_type = "function";
-            }
-          else
-            {
-              curr_parser.curr_lexer->reading_fcn_file = false;
-
-              if (! force_script && looking_at_classdef_keyword (ffile))
-                {
-                  file_type = "classdef";
-
-                  curr_parser.curr_lexer->reading_classdef_file = true;
-
-                  // FIXME -- Should classdef files be handled as
-                  // scripts or separately?  Currently, without
-                  // setting up for reading script files, parsing
-                  // classdef files fails.
-                  curr_parser.curr_lexer->reading_script_file = true;
-                }
-              else
-                {
-                  file_type = "script";
-
-                  curr_parser.curr_lexer->reading_script_file = true;
-                }
-            }
-
-          // Do this with an unwind-protect cleanup function so that
-          // the forced variables will be unmarked in the event of an
-          // interrupt.
-          symbol_table::scope_id scope = symbol_table::top_scope ();
-          frame.add_fcn (symbol_table::unmark_forced_variables, scope);
-
-          if (! help_txt.empty ())
-            help_buf.push (help_txt);
-
-          if (curr_parser.curr_lexer->reading_script_file)
-            curr_parser.curr_lexer->prep_for_script_file ();
-          else
-            curr_parser.curr_lexer->prep_for_function_file ();
-
-          curr_parser.curr_lexer->parsing_class_method = ! dispatch_type.empty ();
-
-          int status = curr_parser.run ();
-
-          fcn_ptr = curr_parser.primary_fcn_ptr;
-
-          if (status != 0)
-            error ("parse error while reading %s file %s",
-                   file_type.c_str (), ff.c_str ());
-        }
-      else
-        {
-          int l = curr_parser.curr_lexer->input_line_number;
-          int c = curr_parser.curr_lexer->current_input_column;
-
-          tree_statement *end_of_script
-            = curr_parser.make_end ("endscript", l, c);
-
-          curr_parser.make_script (0, end_of_script);
-
-          fcn_ptr = curr_parser.primary_fcn_ptr;
-        }
+      // Do this with an unwind-protect cleanup function so that
+      // the forced variables will be unmarked in the event of an
+      // interrupt.
+      symbol_table::scope_id scope = symbol_table::top_scope ();
+      frame.add_fcn (symbol_table::unmark_forced_variables, scope);
+
+      curr_parser.curr_lexer->force_script = force_script;
+      curr_parser.curr_lexer->prep_for_file ();
+      curr_parser.curr_lexer->parsing_class_method = ! dispatch_type.empty ();
+
+      curr_parser.curr_lexer->fcn_file_name = file;
+      curr_parser.curr_lexer->fcn_file_full_name = full_file;
+
+      int status = curr_parser.run ();
+
+      fcn_ptr = curr_parser.primary_fcn_ptr;
+
+      if (status != 0)
+        error ("parse error while reading file %s", full_file.c_str ());
     }
   else if (require_file)
-    error ("no such file, '%s'", ff.c_str ());
+    error ("no such file, '%s'", full_file.c_str ());
   else if (! warn_for.empty ())
-    error ("%s: unable to open file '%s'", warn_for.c_str (), ff.c_str ());
+    error ("%s: unable to open file '%s'", warn_for.c_str (),
+           full_file.c_str ());
 
   return fcn_ptr;
 }
 
 std::string
 get_help_from_file (const std::string& nm, bool& symbol_found,
-                    std::string& file)
+                    std::string& full_file)
 {
   std::string retval;
 
-  file = fcn_file_in_path (nm);
+  full_file = fcn_file_in_path (nm);
+
+  std::string file = full_file;
+
+  size_t file_len = file.length ();
+
+  if ((file_len > 4 && file.substr (file_len-4) == ".oct")
+      || (file_len > 4 && file.substr (file_len-4) == ".mex")
+      || (file_len > 2 && file.substr (file_len-2) == ".m"))
+    {
+      file = octave_env::base_pathname (file);
+      file = file.substr (0, file.find_last_of ('.'));
+
+      size_t pos = file.find_last_of (file_ops::dir_sep_str ());
+      if (pos != std::string::npos)
+        file = file.substr (pos+1);
+    }
 
   if (! file.empty ())
     {
       symbol_found = true;
 
-      FILE *fptr = gnulib::fopen (file.c_str (), "r");
-
-      if (fptr)
+      octave_function *fcn
+        = parse_fcn_file (full_file, file, "", true, false, false, false, "");
+
+      if (fcn)
         {
-          unwind_protect frame;
-          frame.add_fcn (safe_fclose, fptr);
-
-          bool eof;
-          retval = gobble_leading_white_space (fptr, eof);
-
-          if (retval.empty ())
-            {
-              octave_function *fcn = parse_fcn_file (file, "", true,
-                                                     false, false,
-                                                     false, "");
-
-              if (fcn)
-                {
-                  retval = fcn->doc_string ();
-
-                  delete fcn;
-                }
-            }
+          retval = fcn->doc_string ();
+
+          delete fcn;
         }
     }
 
@@ -3633,14 +3356,9 @@
     {
       // Temporarily load m-file version of mex-file, if it exists,
       // to get the help-string to use.
-      frame.protect_var (curr_fcn_file_name);
-      frame.protect_var (curr_fcn_file_full_name);
-
-      curr_fcn_file_name = nm;
-      curr_fcn_file_full_name = file.substr (0, len - 2);
 
       octave_function *tmpfcn = parse_fcn_file (file.substr (0, len - 2),
-                                                dispatch_type, false,
+                                                nm, dispatch_type, false,
                                                 autoload, autoload,
                                                 relative_lookup, "");
 
@@ -3652,15 +3370,7 @@
     }
   else if (len > 2)
     {
-      // These are needed by yyparse.
-
-      frame.protect_var (curr_fcn_file_name);
-      frame.protect_var (curr_fcn_file_full_name);
-
-      curr_fcn_file_name = nm;
-      curr_fcn_file_full_name = file;
-
-      retval = parse_fcn_file (file, dispatch_type, true, autoload,
+      retval = parse_fcn_file (file, nm, dispatch_type, true, autoload,
                                autoload, relative_lookup, "");
     }
 
@@ -3830,12 +3540,6 @@
 
   unwind_protect frame;
 
-  frame.protect_var (curr_fcn_file_name);
-  frame.protect_var (curr_fcn_file_full_name);
-
-  curr_fcn_file_name = file_name;
-  curr_fcn_file_full_name = file_full_name;
-
   if (source_call_depth.find (file_full_name) == source_call_depth.end ())
     source_call_depth[file_full_name] = -1;
 
@@ -3864,9 +3568,9 @@
 
   if (! error_state)
     {
-      octave_function *fcn = parse_fcn_file (file_full_name, "",
-                                             require_file, true, false,
-                                             false, warn_for);
+      octave_function *fcn = parse_fcn_file (file_full_name, file_name,
+                                             "", require_file, true,
+                                             false, false, warn_for);
 
       if (! error_state)
         {
--- a/libinterp/parse-tree/parse.h
+++ b/libinterp/parse-tree/parse.h
@@ -70,18 +70,12 @@
 // Nonzero means print parser debugging info (-d).
 extern int octave_debug;
 
-// Buffer for help text snagged from function files.
-extern std::stack<std::string> help_buf;
-
 // TRUE means we are using readline.
 extern bool line_editing;
 
 // TRUE means we printed messages about reading startup files.
 extern bool reading_startup_message_printed;
 
-// TRUE means input is coming from startup file.
-extern bool input_from_startup_file;
-
 extern OCTINTERP_API std::string
 get_help_from_file (const std::string& nm, bool& symbol_found,
                     std::string& file);
--- a/libinterp/parse-tree/pt-check.cc
+++ b/libinterp/parse-tree/pt-check.cc
@@ -554,8 +554,8 @@
 void
 tree_checker::gripe (const std::string& msg, int line)
 {
-  if (curr_fcn_file_name.empty ())
+  if (file_name.empty ())
     error ("%s", msg.c_str ());
   else
-    error ("%s: %d: %s", curr_fcn_file_name.c_str (), line, msg.c_str ());
+    error ("%s: %d: %s", file_name.c_str (), line, msg.c_str ());
 }
--- a/libinterp/parse-tree/pt-check.h
+++ b/libinterp/parse-tree/pt-check.h
@@ -35,7 +35,7 @@
 public:
 
   tree_checker (void)
-    : do_lvalue_check (false) { }
+    : do_lvalue_check (false), file_name () { }
 
   ~tree_checker (void) { }
 
@@ -125,6 +125,8 @@
 
   bool do_lvalue_check;
 
+  std::string file_name;
+
   void do_decl_command (tree_decl_command&);
 
   void gripe (const std::string& msg, int line);