Mercurial > jwe > octave
changeset 29259:11343ca3c125
allow Octave to operate as a server, executing commands from a queue
* event-manager.h (interpreter_events::interpreter_output):
New virtual fucntion.
(event_manager::interpreter_output): New function.
* interpreter.cc (interpeter::m_parser, interpeter::m_exit_status,
interpeter::m_server_mode): New data members.
(interpreter::parse_and_execute, interpreter::server_loop,
(interpreter::parse_and_execute): New functions.
(interpreter::execute): If server option is set, execute server_loop
instead of main_loop.
* pager.cc (output_system::sync): Also bypass pager if running in
server mode.
(output_system::do_sync): Send output to event manager if running in
server mode and bypass_pager is true.
* options-usage.h, octave.h, octave.cc, main.in.cc:
Handle new --server option.
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Thu, 17 Dec 2020 20:18:51 -0500 |
parents | 28913793f678 |
children | e818ceb2a74e |
files | libinterp/corefcn/event-manager.h libinterp/corefcn/interpreter.cc libinterp/corefcn/interpreter.h libinterp/corefcn/pager.cc libinterp/octave.cc libinterp/octave.h libinterp/options-usage.h src/main.in.cc |
diffstat | 8 files changed, 240 insertions(+), 8 deletions(-) [+] |
line wrap: on
line diff
--- a/libinterp/corefcn/event-manager.h Sun Jan 03 14:39:31 2021 -0500 +++ b/libinterp/corefcn/event-manager.h Thu Dec 17 20:18:51 2020 -0500 @@ -195,6 +195,9 @@ virtual void unregister_doc (const std::string& /*file*/) { } + virtual void interpreter_output (const std::string& /*msg*/) { } + + virtual void gui_status_update (const std::string& /*feature*/, const std::string& /*status*/) { } @@ -503,6 +506,17 @@ return false; } + bool interpreter_output (const std::string& msg) + { + if (enabled ()) + { + instance->interpreter_output (msg); + return true; + } + else + return false; + } + bool gui_status_update (const std::string& feature, const std::string& status) { if (enabled ())
--- a/libinterp/corefcn/interpreter.cc Sun Jan 03 14:39:31 2021 -0500 +++ b/libinterp/corefcn/interpreter.cc Thu Dec 17 20:18:51 2020 -0500 @@ -467,7 +467,10 @@ m_cdef_manager (*this), m_gtk_manager (), m_event_manager (*this), + m_parser (), m_gh_manager (nullptr), + m_exit_status (0), + m_server_mode (false), m_interactive (false), m_read_site_files (true), m_read_init_files (m_app_context != nullptr), @@ -755,6 +758,53 @@ m_initialized = true; } + void interpreter::parse_and_execute (const std::string& input, + bool& incomplete_parse) + { + incomplete_parse = false; + + unwind_protect_var<bool> upv (m_in_top_level_repl, true); + + if (m_evaluator.at_top_level ()) + { + m_evaluator.dbstep_flag (0); + m_evaluator.reset_debug_state (); + } + + bool eof = false; + + if (command_history::add (input)) + m_event_manager.append_history (input); + + m_exit_status = m_parser->run (input, eof); + + if (m_exit_status == 0) + { + std::shared_ptr<tree_statement_list> + stmt_list = m_parser->statement_list (); + + if (stmt_list) + { + command_editor::increment_current_command_number (); + + m_evaluator.eval (stmt_list, m_interactive); + + m_event_manager.set_workspace (); + } + else if (m_parser->at_end_of_input ()) + m_exit_status = EOF; + } + else + incomplete_parse = true; + + if (m_exit_status == -1) + m_exit_status = 0; + else + m_parser->reset (); + + m_event_manager.pre_input_event (); + } + // FIXME: this function is intended to be executed only once. Should // we enforce that restriction? @@ -801,7 +851,10 @@ if (options.forced_interactive ()) command_editor::blink_matching_paren (false); - exit_status = main_loop (); + if (options.server ()) + exit_status = server_loop (); + else + exit_status = main_loop (); } } catch (const exit_exception& xe) @@ -1335,6 +1388,91 @@ return exit_status; } + int interpreter::server_loop (void) + { + // Process events from the event queue. + + unwind_protect_var<bool> upv (m_server_mode, true); + + m_exit_status = 0; + + if (! m_parser) + m_parser = std::shared_ptr<push_parser> (new push_parser (*this)); + + do + { + try + { + // FIXME: Running the event queue should be decoupled from + // the command_editor. We should also use a condition + // variable to manage the execution of entries in the queue + // and eliminate the need for the busy-wait loop. + + command_editor::run_event_hooks (); + + octave::sleep (0.1); + } + catch (const interrupt_exception&) + { + recover_from_exception (); + + m_parser->reset (); + + // Required newline when the user does Ctrl+C at the prompt. + if (m_interactive) + octave_stdout << "\n"; + } + catch (const index_exception& e) + { + recover_from_exception (); + + m_parser->reset (); + + std::cerr << "error: unhandled index exception: " + << e.message () << " -- trying to return to prompt" + << std::endl; + } + catch (const execution_exception& ee) + { + m_error_system.save_exception (ee); + m_error_system.display_exception (ee, std::cerr); + + if (m_interactive) + { + recover_from_exception (); + + m_parser->reset (); + } + else + { + // We should exit with a nonzero status. + m_exit_status = 1; + break; + } + } + catch (const std::bad_alloc&) + { + recover_from_exception (); + + m_parser->reset (); + + std::cerr << "error: out of memory -- trying to return to prompt" + << std::endl; + } + } + while (m_exit_status == 0); + + if (m_exit_status == EOF) + { + if (m_interactive) + octave_stdout << "\n"; + + m_exit_status = 0; + } + + return m_exit_status; + } + tree_evaluator& interpreter::get_evaluator (void) { return m_evaluator;
--- a/libinterp/corefcn/interpreter.h Sun Jan 03 14:39:31 2021 -0500 +++ b/libinterp/corefcn/interpreter.h Thu Dec 17 20:18:51 2020 -0500 @@ -29,6 +29,7 @@ #include "octave-config.h" #include <map> +#include <memory> #include <stack> #include <string> @@ -71,6 +72,7 @@ namespace octave { + class push_parser; class profiler; class child_list; @@ -147,6 +149,12 @@ void initialize (void); + // Parse a line of input. If input ends at a complete statement + // boundary, execute the resulting parse tree. Useful to handle + // parsing user input when running in server mode. + + void parse_and_execute (const std::string& input, bool& incomplete_parse); + // Initialize the interpreter (if not already done by an explicit // call to initialize), execute startup files, --eval option code, // script files, and/or interactive commands. @@ -155,6 +163,11 @@ void shutdown (void); + bool server_mode (void) const + { + return m_server_mode; + } + bool interactive (void) const { return m_interactive; @@ -302,6 +315,11 @@ return m_event_manager; } + std::shared_ptr<push_parser> get_parser (void) + { + return m_parser; + } + gh_manager& get_gh_manager (void) { return *m_gh_manager; @@ -505,6 +523,8 @@ int main_loop (void); + int server_loop (void); + void execute_atexit_fcns (void); application *m_app_context; @@ -553,8 +573,15 @@ event_manager m_event_manager; + std::shared_ptr<push_parser> m_parser; + gh_manager *m_gh_manager; + int m_exit_status; + + // TRUE means we are executing in the server_loop function. + bool m_server_mode; + // TRUE means this is an interactive interpreter (forced or not). bool m_interactive;
--- a/libinterp/corefcn/pager.cc Sun Jan 03 14:39:31 2021 -0500 +++ b/libinterp/corefcn/pager.cc Thu Dec 17 20:18:51 2020 -0500 @@ -377,13 +377,17 @@ bool output_system::sync (const char *buf, int len) { - if (! m_interpreter.interactive () + // FIXME: The following seems to be a bit of a mess. + + if (m_interpreter.server_mode () + || ! m_interpreter.interactive () || application::forced_interactive () || m_really_flush_to_pager || (m_page_screen_output && m_page_output_immediately) || ! m_page_screen_output) { - bool bypass_pager = (! m_interpreter.interactive () + bool bypass_pager = (m_interpreter.server_mode () + || ! m_interpreter.interactive () || application::forced_interactive () || ! m_page_screen_output || (m_really_flush_to_pager @@ -439,8 +443,17 @@ { if (bypass_pager) { - std::cout.write (msg, len); - std::cout.flush (); + if (m_interpreter.server_mode ()) + { + event_manager& evmgr = m_interpreter.get_event_manager (); + + evmgr.interpreter_output (std::string (msg, len)); + } + else + { + std::cout.write (msg, len); + std::cout.flush (); + } } else {
--- a/libinterp/octave.cc Sun Jan 03 14:39:31 2021 -0500 +++ b/libinterp/octave.cc Thu Dec 17 20:18:51 2020 -0500 @@ -212,6 +212,10 @@ m_persist = true; break; + case SERVER_OPTION: + m_server = true; + break; + case TEXI_MACROS_FILE_OPTION: if (octave_optarg_wrapper ()) m_texi_macros_file = octave_optarg_wrapper (); @@ -255,6 +259,7 @@ m.assign ("read_history_file", read_history_file ()); m.assign ("read_init_files", read_init_files ()); m.assign ("read_site_files", read_site_files ()); + m.assign ("server", server ()); m.assign ("set_initial_path", set_initial_path ()); m.assign ("traditional", traditional ()); m.assign ("verbose_flag", verbose_flag ()); @@ -398,8 +403,21 @@ std::cerr << "error: --gui and --no-line-editing are mutually exclusive options" << std::endl; octave_print_terse_usage_and_exit (); } + if (m_options.server ()) + { + std::cerr << "error: --gui and --server are mutually exclusive options" << std::endl; + octave_print_terse_usage_and_exit (); + } } + if (m_options.server ()) + { + if (m_options.forced_interactive ()) + { + std::cerr << "error: --server and --forced-interactive are mutually exclusive options" << std::endl; + octave_print_terse_usage_and_exit (); + } + } m_is_octave_program = ((m_have_script_file || m_have_eval_option_code) && ! m_options.persist ()
--- a/libinterp/octave.h Sun Jan 03 14:39:31 2021 -0500 +++ b/libinterp/octave.h Thu Dec 17 20:18:51 2020 -0500 @@ -69,6 +69,7 @@ bool read_history_file (void) const { return m_read_history_file; } bool read_init_files (void) const { return m_read_init_files; } bool read_site_files (void) const { return m_read_site_files; } + bool server (void) const { return m_server; } bool set_initial_path (void) const { return m_set_initial_path; } bool traditional (void) const { return m_traditional; } bool verbose_flag (void) const { return m_verbose_flag; } @@ -99,6 +100,7 @@ void read_history_file (bool arg) { m_read_history_file = arg; } void read_init_files (bool arg) { m_read_init_files = arg; } void read_site_files (bool arg) { m_read_site_files = arg; } + void server (bool arg) { m_server = arg; } void set_initial_path (bool arg) { m_set_initial_path = arg; } void traditional (bool arg) { m_traditional = arg; } void verbose_flag (bool arg) { m_verbose_flag = arg; } @@ -170,6 +172,10 @@ // (--norc; --no-site-file; -f) bool m_read_site_files = true; + // If TRUE, start the command server. + // (--server) + bool m_server = false; + // TRUE means we set the initial path to configured defaults. // (--no-init-path) bool m_set_initial_path = true;
--- a/libinterp/options-usage.h Sun Jan 03 14:39:31 2021 -0500 +++ b/libinterp/options-usage.h Thu Dec 17 20:18:51 2020 -0500 @@ -41,7 +41,7 @@ [--jit-compiler] [--line-editing] [--no-gui] [--no-history]\n\ [--no-init-file] [--no-init-path] [--no-line-editing]\n\ [--no-site-file] [--no-window-system] [--norc] [-p path]\n\ - [--path path] [--persist] [--silent] [--traditional]\n\ + [--path path] [--persist] [--server] [--silent] [--traditional]\n\ [--verbose] [--version] [file]"; // This is here so that it's more likely that the usage message and @@ -75,8 +75,9 @@ #define NO_LINE_EDITING_OPTION 15 #define NO_SITE_FILE_OPTION 16 #define PERSIST_OPTION 17 -#define TEXI_MACROS_FILE_OPTION 18 -#define TRADITIONAL_OPTION 19 +#define SERVER_OPTION 18 +#define TEXI_MACROS_FILE_OPTION 19 +#define TRADITIONAL_OPTION 20 struct octave_getopt_options long_opts[] = { { "braindead", octave_no_arg, 0, TRADITIONAL_OPTION }, @@ -106,6 +107,7 @@ { "path", octave_required_arg, 0, 'p' }, { "persist", octave_no_arg, 0, PERSIST_OPTION }, { "quiet", octave_no_arg, 0, 'q' }, + { "server", octave_no_arg, 0, SERVER_OPTION }, { "silent", octave_no_arg, 0, 'q' }, { "texi-macros-file", octave_required_arg, 0, TEXI_MACROS_FILE_OPTION }, { "traditional", octave_no_arg, 0, TRADITIONAL_OPTION }, @@ -156,6 +158,7 @@ --norc, -f Don't read any initialization files.\n\ --path PATH, -p PATH Add PATH to head of function search path.\n\ --persist Go interactive after --eval or reading from FILE.\n\ + --server Enter server mode at startup.\n\ --silent, --quiet, -q Don't print message at startup.\n\ --texi-macros-file FILE Use Texinfo macros in FILE for makeinfo command.\n\ --traditional Set variables for closer MATLAB compatibility.\n\
--- a/src/main.in.cc Sun Jan 03 14:39:31 2021 -0500 +++ b/src/main.in.cc Thu Dec 17 20:18:51 2020 -0500 @@ -213,6 +213,7 @@ int retval = 0; int idx_gui = -1; + bool server = false; bool start_gui = false; bool gui_libs = true; @@ -291,6 +292,11 @@ persist_octave = true; new_argv[k++] = argv[i]; } + else if (! strcmp (argv[i], "--server")) + { + server = true; + new_argv[k++] = argv[i]; + } else if (! strcmp (argv[i], "--eval") || (strlen (argv[i]) > 0 && argv[i][0] != '-')) { @@ -350,6 +356,13 @@ return 1; } + if (server) + { + std::cerr << "octave: conflicting options: --server and --gui" + << std::endl; + return 1; + } + #if ! defined (HAVE_OCTAVE_QT_GUI) std::cerr << "octave: GUI features missing or disabled in this build" << std::endl;