changeset 28266:ce7a5b60e102 stable

restore pull parser interface when input is not interactive (bug #58198) * interpreter.cc (interpreter::main_loop): Always use pull parser interface when not in interactive mode. Allow possibility of choosing pull or push parser for interactive use at run time in future. * parse.h, oct-parse.yy (base_parser::run (void)): New pure virtual method. (push_parser::run (void)): New method.
author John W. Eaton <jwe@octave.org>
date Wed, 06 May 2020 10:58:16 -0400
parents d938c4d22200
children 4ee43852f5b6 a36309586b0c
files libinterp/corefcn/interpreter.cc libinterp/parse-tree/oct-parse.yy libinterp/parse-tree/parse.h
diffstat 3 files changed, 113 insertions(+), 49 deletions(-) [+]
line wrap: on
line diff
--- a/libinterp/corefcn/interpreter.cc	Tue May 05 01:01:39 2020 -0400
+++ b/libinterp/corefcn/interpreter.cc	Wed May 06 10:58:16 2020 -0400
@@ -1057,25 +1057,48 @@
 
   int interpreter::main_loop (void)
   {
+    // The big loop.  Read, Eval, Print, Loop.  Normally user
+    // interaction at the command line in a terminal session, but we may
+    // also end up here when reading from a pipe or when stdin is
+    // connected to a file by the magic of input redirection.
+
     int exit_status = 0;
 
-    // The big loop.
+    // FIXME: should this choice be a command-line option?  Note that we
+    // intend that the push parser interface only be used for
+    // interactive sessions.
 
 #if defined (OCTAVE_ENABLE_COMMAND_LINE_PUSH_PARSER)
-
-    input_reader reader (*this);
+    static bool use_command_line_push_parser = true;
+#else
+    static bool use_command_line_push_parser = false;
+#endif
 
-    push_parser repl_parser (*this);
+    // The following logic is written as it is to allow easy transition
+    // to setting USE_COMMAND_LINE_PUSH_PARSER at run time and to
+    // simplify the logic of the main loop below by using the same
+    // base_parser::run interface for both push and pull parsers.
 
-#else
+    std::shared_ptr<base_parser> repl_parser;
 
-    // The pull parser takes ownership of the lexer and will delete it
-    // when the parser goes out of scope.
-
-    parser repl_parser (m_interactive
-                        ? new lexer (*this) : new lexer (stdin, *this));
-
-#endif
+    if (m_interactive)
+      {
+        if (use_command_line_push_parser)
+          {
+            push_parser *pp = new push_parser (*this, new input_reader (*this));
+            repl_parser = std::shared_ptr<base_parser> (pp);
+          }
+        else
+          {
+            parser *pp = new parser (new lexer (*this));
+            repl_parser = std::shared_ptr<base_parser> (pp);
+          }
+      }
+    else
+      {
+        parser *pp = new parser (new lexer (stdin, *this));
+        repl_parser = std::shared_ptr<base_parser> (pp);
+      }
 
     do
       {
@@ -1083,7 +1106,7 @@
           {
             unwind_protect_var<bool> upv (m_in_top_level_repl, true);
 
-            repl_parser.reset ();
+            repl_parser->reset ();
 
             if (m_evaluator.at_top_level ())
               {
@@ -1091,44 +1114,12 @@
                 m_evaluator.reset_debug_state ();
               }
 
-#if defined (OCTAVE_ENABLE_COMMAND_LINE_PUSH_PARSER)
-
-            std::string prompt
-              = command_editor::decode_prompt_string (m_input_system.PS1 ());
-
-            do
-              {
-                // Reset status each time through the read loop so that
-                // it won't be set to -1 and cause us to exit the outer
-                // loop early if there is an exception while reading
-                // input or parsing.
-
-                exit_status = 0;
-
-                bool eof = false;
-                std::string input_line = reader.get_input (prompt, eof);
+            exit_status = repl_parser->run ();
 
-                if (eof)
-                  {
-                    exit_status = EOF;
-                    break;
-                  }
-
-                exit_status = repl_parser.run (input_line, false);
-
-                prompt = command_editor::decode_prompt_string (m_input_system.PS2 ());
-              }
-            while (exit_status < 0);
-
-#else
-
-            exit_status = repl_parser.run ();
-
-#endif
             if (exit_status == 0)
               {
                 std::shared_ptr<tree_statement_list>
-                  stmt_list = repl_parser.statement_list ();
+                  stmt_list = repl_parser->statement_list ();
 
                 if (stmt_list)
                   {
@@ -1136,7 +1127,7 @@
 
                     m_evaluator.eval (stmt_list, m_interactive);
                   }
-                else if (repl_parser.at_end_of_input ())
+                else if (repl_parser->at_end_of_input ())
                   {
                     exit_status = EOF;
                     break;
--- a/libinterp/parse-tree/oct-parse.yy	Tue May 05 01:01:39 2020 -0400
+++ b/libinterp/parse-tree/oct-parse.yy	Wed May 06 10:58:16 2020 -0400
@@ -4717,6 +4717,46 @@
     return status;
   }
 
+  int
+  push_parser::run (void)
+  {
+    if (! m_reader)
+      error ("push_parser::run requires valid input_reader");
+
+    int exit_status = 0;
+
+    input_system&  input_sys = m_interpreter.get_input_system ();
+
+    std::string prompt
+      = command_editor::decode_prompt_string (input_sys.PS1 ());
+
+    do
+      {
+        // Reset status each time through the read loop so that
+        // it won't be set to -1 and cause us to exit the outer
+        // loop early if there is an exception while reading
+        // input or parsing.
+
+        exit_status = 0;
+
+        bool eof = false;
+        std::string input_line = m_reader->get_input (prompt, eof);
+
+        if (eof)
+          {
+            exit_status = EOF;
+            break;
+          }
+
+        exit_status = run (input_line, false);
+
+        prompt = command_editor::decode_prompt_string (input_sys.PS2 ());
+      }
+    while (exit_status < 0);
+
+    return exit_status;
+  }
+
   octave_value
   parse_fcn_file (interpreter& interp, const std::string& full_file,
                   const std::string& file, const std::string& dir_name,
--- a/libinterp/parse-tree/parse.h	Tue May 05 01:01:39 2020 -0400
+++ b/libinterp/parse-tree/parse.h	Wed May 06 10:58:16 2020 -0400
@@ -36,6 +36,7 @@
 #include <set>
 #include <string>
 
+#include "input.h"
 #include "lex.h"
 #include "pt-misc.h"
 #include "symscope.h"
@@ -466,6 +467,14 @@
                     const std::string& package_name, bool require_file,
                     bool force_script, bool autoload, bool relative_lookup);
 
+    // Thih interface allows push or pull parsers to be used
+    // equivalently, provided that the push parser also owns its input
+    // method (see below).  Alternatively, the push parser interface may
+    // use a separate run method and completely separate input from
+    // lexical analysis and parsing.
+
+    virtual int run (void) = 0;
+
   protected:
 
     // Contains error message if Bison-generated parser returns non-zero
@@ -599,7 +608,16 @@
   public:
 
     push_parser (interpreter& interp)
-      : base_parser (*(new push_lexer (interp)))
+      : base_parser (*(new push_lexer (interp))),
+        m_interpreter (interp), m_reader ()
+    { }
+
+    // The parser assumes ownership of READER, which must be created
+    // with new.
+
+    push_parser (interpreter& interp, input_reader *reader)
+      : base_parser (*(new push_lexer (interp))),
+        m_interpreter (interp), m_reader (reader)
     { }
 
     // No copying!
@@ -610,7 +628,22 @@
 
     ~push_parser (void) = default;
 
+    // Use the push parser in the same way as the pull parser.  The
+    // parser arranges for input through the M_READER object.  See, for
+    // example, interpreter::main_loop.
+
+    int run (void);
+
+    // Parse INPUT.  M_READER is not used.  The user is responsible for
+    // collecting input.
+
     int run (const std::string& input, bool eof);
+
+  private:
+
+    interpreter& m_interpreter;
+
+    std::shared_ptr<input_reader> m_reader;
   };
 
   extern OCTINTERP_API std::string