view libinterp/corefcn/interpreter.cc @ 29949:f254c302bb9c

remove JIT compiler from Octave sources As stated in the NEWS file entry added with this changeset, no one has ever seriously taken on further development of the JIT compiler in Octave since it was first added as part of a Google Summer of Code project in 2012 and it still does nothing significant. It is out of date with the default interpreter that walks the parse tree. Even though we have fixed the configure script to disable it by default, people still ask questions about how to build it, but it doesn’t seem that they are doing that to work on it but because they think it will make Octave code run faster (it never did, except for some extremely simple bits of code as examples for demonstration purposes only). * NEWS: Note change. * configure.ac, acinclude.m4: Eliminate checks and macros related to the JIT compiler and LLVM. * basics.txi, install.txi, octave.texi, vectorize.txi: Remove mention of JIT compiler and LLVM. * jit-ir.cc, jit-ir.h, jit-typeinfo.cc, jit-typeinfo.h, jit-util.cc, jit-util.h, pt-jit.cc, pt-jit.h: Delete. * libinterp/parse-tree/module.mk: Update. * Array-jit.cc: Delete. * libinterp/template-inst/module.mk: Update. * test/jit.tst: Delete. * test/module.mk: Update. * interpreter.cc (interpreter::interpreter): Don't check options for debug_jit or jit_compiler. * toplev.cc (F__octave_config_info__): Remove JIT compiler and LLVM info from struct. * ov-base.h (octave_base_value::grab, octave_base_value::release): Delete. * ov-builtin.h, ov-builtin.cc (octave_builtin::to_jit, octave_builtin::stash_jit): Delete. (octave_builtin::m_jtype): Delete data member and all uses. * ov-usr-fcn.h, ov-usr-fcn.cc (octave_user_function::m_jit_info): Delete data member and all uses. (octave_user_function::get_info, octave_user_function::stash_info): Delete. * options.h (DEBUG_JIT_OPTION, JIT_COMPILER_OPTION): Delete macro definitions and all uses. * octave.h, octave.cc (cmdline_options::cmdline_options): Don't handle DEBUG_JIT_OPTION, JIT_COMPILER_OPTION): Delete. (cmdline_options::debug_jit, cmdline_options::jit_compiler): Delete functions and all uses. (cmdline_options::m_debug_jit, cmdline_options::m_jit_compiler): Delete data members and all uses. (octave_getopt_options long_opts): Remove "debug-jit" and "jit-compiler" from the list. * pt-eval.cc (tree_evaluator::visit_simple_for_command, tree_evaluator::visit_complex_for_command, tree_evaluator::visit_while_command, tree_evaluator::execute_user_function): Eliminate JIT compiler code. * pt-loop.h, pt-loop.cc (tree_while_command::get_info, tree_while_command::stash_info, tree_simple_for_command::get_info, tree_simple_for_command::stash_info): Delete functions and all uses. (tree_while_command::m_compiled, tree_simple_for_command::m_compiled): Delete member variable and all uses. * usage.h (usage_string, octave_print_verbose_usage_and_exit): Remove [--debug-jit] and [--jit-compiler] from the message. * Array.h (Array<T>::Array): Remove constructor that was only intended to be used by the JIT compiler. (Array<T>::jit_ref_count, Array<T>::jit_slice_data, Array<T>::jit_dimensions, Array<T>::jit_array_rep): Delete. * Marray.h (MArray<T>::MArray): Remove constructor that was only intended to be used by the JIT compiler. * NDArray.h (NDArray::NDarray): Remove constructor that was only intended to be used by the JIT compiler. * dim-vector.h (dim_vector::to_jit): Delete. (dim_vector::dim_vector): Remove constructor that was only intended to be used by the JIT compiler. * codeql-analysis.yaml, make.yaml: Don't require llvm-dev. * subst-config-vals.in.sh, subst-cross-config-vals.in.sh: Don't substitute OCTAVE_CONF_LLVM_CPPFLAGS, OCTAVE_CONF_LLVM_LDFLAGS, or OCTAVE_CONF_LLVM_LIBS. * Doxyfile.in: Don't define HAVE_LLVM. * aspell-octave.en.pws: Eliminate jit, JIT, and LLVM from the list of spelling exceptions. * build-env.h, build-env.in.cc (LLVM_CPPFLAGS, LLVM_LDFLAGS, LLVM_LIBS): Delete variables and all uses. * libinterp/corefcn/module.mk (%canon_reldir%_libcorefcn_la_CPPFLAGS): Remove $(LLVM_CPPFLAGS) from the list. * libinterp/parse-tree/module.mk (%canon_reldir%_libparse_tree_la_CPPFLAGS): Remove $(LLVM_CPPFLAGS) from the list.
author John W. Eaton <jwe@octave.org>
date Tue, 10 Aug 2021 16:42:29 -0400
parents b39203a44db2
children 4c88a452519c
line wrap: on
line source

////////////////////////////////////////////////////////////////////////
//
// Copyright (C) 1993-2021 The Octave Project Developers
//
// See the file COPYRIGHT.md in the top-level directory of this
// distribution or <https://octave.org/copyright/>.
//
// This file is part of Octave.
//
// Octave is free software: you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Octave is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Octave; see the file COPYING.  If not, see
// <https://www.gnu.org/licenses/>.
//
////////////////////////////////////////////////////////////////////////

#if defined (HAVE_CONFIG_H)
#  include "config.h"
#endif

#include <cstdio>

#include <iostream>
#include <set>
#include <string>
#include <thread>

#include "cmd-edit.h"
#include "cmd-hist.h"
#include "file-ops.h"
#include "file-stat.h"
#include "file-ops.h"
#include "fpucw-wrappers.h"
#include "lo-blas-proto.h"
#include "lo-error.h"
#include "oct-env.h"
#include "quit.h"
#include "str-vec.h"
#include "signal-wrappers.h"
#include "unistd-wrappers.h"

#include "builtin-defun-decls.h"
#include "defaults.h"
#include "Cell.h"
#include "defun.h"
#include "display.h"
#include "error.h"
#include "event-manager.h"
#include "file-io.h"
#include "graphics.h"
#include "help.h"
#include "input.h"
#include "interpreter-private.h"
#include "interpreter.h"
#include "load-path.h"
#include "load-save.h"
#include "octave.h"
#include "oct-hist.h"
#include "oct-map.h"
#include "oct-mutex.h"
#include "ovl.h"
#include "ov.h"
#include "ov-classdef.h"
#include "parse.h"
#include "pt-classdef.h"
#include "pt-eval.h"
#include "pt-jump.h"
#include "pt-stmt.h"
#include "settings.h"
#include "sighandlers.h"
#include "sysdep.h"
#include "unwind-prot.h"
#include "utils.h"
#include "variables.h"
#include "version.h"

// TRUE means the quit() call is allowed.
bool quit_allowed = true;

// TRUE means we are ready to interpret commands, but not everything
// is ready for interactive use.
bool octave_interpreter_ready = false;

// TRUE means we've processed all the init code and we are good to go.
bool octave_initialized = false;

DEFUN (__version_info__, args, ,
       doc: /* -*- texinfo -*-
@deftypefn {} {retval =} __version_info__ (@var{name}, @var{version}, @var{release}, @var{date})
Undocumented internal function.
@end deftypefn */)
{
  static octave_map vinfo;

  int nargin = args.length ();

  if (nargin != 0 && nargin != 4)
    print_usage ();

  octave_value retval;

  if (nargin == 0)
    retval = vinfo;
  else if (nargin == 4)
    {
      if (vinfo.nfields () == 0)
        {
          vinfo.assign ("Name", args(0));
          vinfo.assign ("Version", args(1));
          vinfo.assign ("Release", args(2));
          vinfo.assign ("Date", args(3));
        }
      else
        {
          octave_idx_type n = vinfo.numel () + 1;

          vinfo.resize (dim_vector (n, 1));

          octave_value idx (n);

          vinfo.assign (idx, "Name", Cell (octave_value (args(0))));
          vinfo.assign (idx, "Version", Cell (octave_value (args(1))));
          vinfo.assign (idx, "Release", Cell (octave_value (args(2))));
          vinfo.assign (idx, "Date", Cell (octave_value (args(3))));
        }
    }

  return retval;
}

DEFMETHOD (quit, interp, args, ,
           doc: /* -*- texinfo -*-
@deftypefn  {} {} quit
@deftypefnx {} {} quit cancel
@deftypefnx {} {} quit force
@deftypefnx {} {} quit ("cancel")
@deftypefnx {} {} quit ("force")
@deftypefnx {} {} quit (@var{status})
@deftypefnx {} {} quit (@var{status}, "force")
Quit the current Octave session.

The @code{exit} function is an alias for @code{quit}.

If the optional integer value @var{status} is supplied, pass that value to
the operating system as Octave's exit status.  The default value is zero.

When exiting, Octave will attempt to run the m-file @file{finish.m} if it
exists.  User commands to save the workspace or clean up temporary files
may be placed in that file.  Alternatively, another m-file may be scheduled
to run using @code{atexit}.  If an error occurs while executing the
@file{finish.m} file, Octave does not exit and control is returned to
the command prompt.

If the optional argument @qcode{"cancel"} is provided, Octave does not
exit and control is returned to the command prompt.  This feature allows
the @code{finish.m} file to cancel the quit process.

If the user preference to request confirmation before exiting, Octave
will display a dialog and give the user an option to cancel the exit
process.

If the optional argument @qcode{"force"} is provided, no confirmation is
requested, and the execution of the @file{finish.m} file is skipped.
@seealso{atexit}
@end deftypefn */)
{
  int numel = args.length ();

  if (numel > 2)
    print_usage ();

  int exit_status = 0;

  bool force = false;
  bool cancel = false;

  if (numel == 2)
    {
      exit_status = args(0).xnint_value ("quit: STATUS must be an integer");
      std::string frc
        = args(1).xstring_value ("quit: second argument must be a string");

      if (frc == "force")
        force = true;
      else
        error (R"(quit: second argument must be string "force")");
    }
  else if (numel == 1)
    {
      if (args(0).is_string ())
        {
          const char *msg
            = R"(quit: option must be string "cancel" or "force")";

          std::string opt = args(0).xstring_value (msg);

          if (opt == "cancel")
            cancel = true;
          else if (opt == "force")
            force = true;
          else
            error ("%s", msg);
        }
      else
        exit_status = args(0).xnint_value ("quit: STATUS must be an integer");
    }

  if (cancel)
    {
      // No effect if "quit cancel" appears outside of finish script.

      if (interp.executing_finish_script ())
        interp.cancel_quit (true);

      return ovl ();
    }

  interp.quit (exit_status, force);

  return ovl ();
}

DEFALIAS (exit, quit);

DEFMETHOD (atexit, interp, args, nargout,
           doc: /* -*- texinfo -*-
@deftypefn  {} {} atexit (@var{fcn})
@deftypefnx {} {} atexit (@var{fcn}, @var{flag})
Register a function to be called when Octave exits.

For example,

@example
@group
function last_words ()
  disp ("Bye bye");
endfunction
atexit ("last_words");
@end group
@end example

@noindent
will print the message @qcode{"Bye bye"} when Octave exits.

The additional argument @var{flag} will register or unregister @var{fcn}
from the list of functions to be called when Octave exits.  If @var{flag} is
true, the function is registered, and if @var{flag} is false, it is
unregistered.  For example, after registering the function @code{last_words}
above,

@example
atexit ("last_words", false);
@end example

@noindent
will remove the function from the list and Octave will not call
@code{last_words} when it exits.

Note that @code{atexit} only removes the first occurrence of a function
from the list, so if a function was placed in the list multiple times with
@code{atexit}, it must also be removed from the list multiple times.
@seealso{quit}
@end deftypefn */)
{
  int nargin = args.length ();

  if (nargin < 1 || nargin > 2)
    print_usage ();

  std::string arg = args(0).xstring_value ("atexit: FCN argument must be a string");

  bool add_mode = (nargin == 2)
    ? args(1).xbool_value ("atexit: FLAG argument must be a logical value")
    : true;

  octave_value_list retval;

  if (add_mode)
    interp.add_atexit_fcn (arg);
  else
    {
      bool found = interp.remove_atexit_fcn (arg);

      if (nargout > 0)
        retval = ovl (found);
    }

  return retval;
}

DEFMETHOD (__traditional__, interp, , ,
           doc: /* -*- texinfo -*-
@deftypefn {} {} __traditional__ ()
Undocumented internal function.
@end deftypefn */)
{
  return ovl (interp.traditional ());
}

namespace octave
{
  temporary_file_list::~temporary_file_list (void)
  {
    cleanup ();
  }

  void temporary_file_list::insert (const std::string& file)
  {
    m_files.insert (file);
  }

  void temporary_file_list::cleanup (void)
  {
    while (! m_files.empty ())
      {
        auto it = m_files.begin ();

        octave_unlink_wrapper (it->c_str ());

        m_files.erase (it);
      }
  }

  // The time we last time we changed directories.
  sys::time Vlast_chdir_time = 0.0;

  // Execute commands from a file and catch potential exceptions in a consistent
  // way.  This function should be called anywhere we might parse and execute
  // commands from a file before we have entered the main loop in
  // toplev.cc.

  static int safe_source_file (const std::string& file_name,
                               const std::string& context = "",
                               bool verbose = false,
                               bool require_file = true)
  {
    interpreter& interp = __get_interpreter__ ("safe_source_file");

    try
      {
        source_file (file_name, context, verbose, require_file);
      }
    catch (const interrupt_exception&)
      {
        interp.recover_from_exception ();

        return 1;
      }
    catch (const execution_exception& ee)
      {
        interp.handle_exception (ee);

        return 1;
      }

    return 0;
  }

  static void initialize_version_info (void)
  {
    octave_value_list args;

    args(3) = OCTAVE_RELEASE_DATE;
    args(2) = config::release ();
    args(1) = OCTAVE_VERSION;
    args(0) = "GNU Octave";

    F__version_info__ (args, 0);
  }

  static void xerbla_abort (void)
  {
    error ("Fortran procedure terminated by call to XERBLA");
  }

  static void initialize_xerbla_error_handler (void)
  {
    // The idea here is to force xerbla to be referenced so that we will
    // link to our own version instead of the one provided by the BLAS
    // library.  But numeric_limits<double>::NaN () should never be -1, so
    // we should never actually call xerbla.  FIXME (again!): If this
    // becomes a constant expression the test might be optimized away and
    // then the reference to the function might also disappear.

    if (numeric_limits<double>::NaN () == -1)
      F77_FUNC (xerbla, XERBLA) ("octave", 13 F77_CHAR_ARG_LEN (6));

    typedef void (*xerbla_handler_ptr) (void);

    typedef void (*octave_set_xerbla_handler_ptr) (xerbla_handler_ptr);

    dynamic_library libs ("");

    if (libs)
      {
        octave_set_xerbla_handler_ptr octave_set_xerbla_handler
          = reinterpret_cast<octave_set_xerbla_handler_ptr>
              (libs.search ("octave_set_xerbla_handler"));

        if (octave_set_xerbla_handler)
          octave_set_xerbla_handler (xerbla_abort);
      }
  }

  OCTAVE_NORETURN static void
  lo_error_handler (const char *fmt, ...)
  {
    va_list args;
    va_start (args, fmt);
    verror_with_cfn (fmt, args);
    va_end (args);

    throw execution_exception ();
  }

  OCTAVE_NORETURN static void
  lo_error_with_id_handler (const char *id, const char *fmt, ...)
  {
    va_list args;
    va_start (args, fmt);
    verror_with_id_cfn (id, fmt, args);
    va_end (args);

    throw execution_exception ();
  }

  static void initialize_error_handlers (void)
  {
    set_liboctave_error_handler (lo_error_handler);
    set_liboctave_error_with_id_handler (lo_error_with_id_handler);
    set_liboctave_warning_handler (warning);
    set_liboctave_warning_with_id_handler (warning_with_id);
  }

  // Create an interpreter object and perform initialization up to the
  // point of setting reading command history and setting the load
  // path.

  interpreter::interpreter (application *app_context)
    : m_app_context (app_context),
      m_tmp_files (),
      m_atexit_fcns (),
      m_display_info (),
      m_environment (),
      m_settings (),
      m_error_system (*this),
      m_help_system (*this),
      m_input_system (*this),
      m_output_system (*this),
      m_history_system (*this),
      m_dynamic_loader (*this),
      m_load_path (*this),
      m_load_save_system (*this),
      m_type_info (),
      m_symbol_table (*this),
      m_evaluator (*this),
      m_stream_list (*this),
      m_child_list (),
      m_url_handle_manager (),
      m_cdef_manager (*this),
      m_gtk_manager (),
      m_event_manager (*this),
      m_gh_manager (nullptr),
      m_interactive (false),
      m_read_site_files (true),
      m_read_init_files (m_app_context != nullptr),
      m_verbose (false),
      m_traditional (false),
      m_inhibit_startup_message (false),
      m_load_path_initialized (false),
      m_history_initialized (false),
      m_interrupt_all_in_process_group (true),
      m_cancel_quit (false),
      m_executing_finish_script (false),
      m_executing_atexit (false),
      m_initialized (false)
  {
    // FIXME: When thread_local storage is used by default, this message
    // should change to say something like
    //
    //   only one Octave interpreter may be active in any given thread

    if (instance)
      throw std::runtime_error
        ("only one Octave interpreter may be active");

    instance = this;

    // Matlab uses "C" locale for LC_NUMERIC class regardless of local setting
    setlocale (LC_ALL, "");
    setlocale (LC_NUMERIC, "C");
    setlocale (LC_TIME, "C");
    sys::env::putenv ("LC_NUMERIC", "C");
    sys::env::putenv ("LC_TIME", "C");

    // Initialize the default floating point unit control state.
    octave_set_default_fpucw ();

    thread::init ();

    octave_ieee_init ();

    initialize_xerbla_error_handler ();

    initialize_error_handlers ();

    if (m_app_context)
      {
        install_signal_handlers ();
        octave_unblock_signal_by_name ("SIGTSTP");
      }
    else
      quit_allowed = false;

    if (! m_app_context)
      m_display_info.initialize ();

    bool line_editing = false;

    if (m_app_context)
      {
        // Embedded interpreters don't execute command line options.
        const cmdline_options& options = m_app_context->options ();

        // Make all command-line arguments available to startup files,
        // including PKG_ADD files.

        string_vector args = options.all_args ();

        m_app_context->intern_argv (args);
        intern_nargin (args.numel () - 1);

        bool is_octave_program = m_app_context->is_octave_program ();

        std::list<std::string> command_line_path = options.command_line_path ();

        for (const auto& pth : command_line_path)
          m_load_path.set_command_line_path (pth);

        std::string exec_path = options.exec_path ();
        if (! exec_path.empty ())
          m_environment.exec_path (exec_path);

        std::string image_path = options.image_path ();
        if (! image_path.empty ())
          m_environment.image_path (image_path);

        if (! options.no_window_system ())
          m_display_info.initialize ();

        // Is input coming from a terminal?  If so, we are probably
        // interactive.

        // If stdin is not a tty, then we are reading commands from a
        // pipe or a redirected file.
        bool stdin_is_tty = octave_isatty_wrapper (fileno (stdin));

        m_interactive = (! is_octave_program && stdin_is_tty
                         && octave_isatty_wrapper (fileno (stdout)));

        // Don't force interactive if we're already interactive (bug #60696).
        bool forced_interactive = options.forced_interactive ();
        if (m_interactive)
          {
            m_app_context->forced_interactive (false);
            forced_interactive = false;
          }

        // Check if the user forced an interactive session.
        if (forced_interactive)
          m_interactive = true;

        line_editing = options.line_editing ();
        if ((! m_interactive || forced_interactive)
            && ! options.forced_line_editing ())
          line_editing = false;

        m_traditional = options.traditional ();

        // FIXME: if possible, perform the following actions directly
        // instead of using the interpreter-level functions.

        if (options.echo_commands ())
          m_evaluator.echo
            (tree_evaluator::ECHO_SCRIPTS | tree_evaluator::ECHO_FUNCTIONS
             | tree_evaluator::ECHO_ALL);

        std::string docstrings_file = options.docstrings_file ();
        if (! docstrings_file.empty ())
          Fbuilt_in_docstrings_file (*this, octave_value (docstrings_file));

        std::string doc_cache_file = options.doc_cache_file ();
        if (! doc_cache_file.empty ())
          Fdoc_cache_file (*this, octave_value (doc_cache_file));

        std::string info_file = options.info_file ();
        if (! info_file.empty ())
          Finfo_file (*this, octave_value (info_file));

        std::string info_program = options.info_program ();
        if (! info_program.empty ())
          Finfo_program (*this, octave_value (info_program));

        std::string texi_macros_file = options.texi_macros_file ();
        if (! texi_macros_file.empty ())
          Ftexi_macros_file (*this, octave_value (texi_macros_file));
      }

    // FIXME: we defer creation of the gh_manager object because it
    // creates a root_figure object that requires the display_info
    // object, but that is currently only accessible through the global
    // interpreter object and that is not available until after the
    // interpreter::instance pointer is set (above).  It would be better
    // if m_gh_manager could be an object value instead of a pointer and
    // created as part of the interpreter initialization.  To do that,
    // we should either make the display_info object independent of the
    // interpreter object (does it really need to cache any
    // information?) or defer creation of the root_figure object until
    // it is actually needed.
    m_gh_manager = new gh_manager (*this);

    m_input_system.initialize (line_editing);

    // These can come after command line args since none of them set any
    // defaults that might be changed by command line options.

    initialize_version_info ();

    // This should be done before initializing the load path because
    // some PKG_ADD files might need --traditional behavior.

    if (m_traditional)
      maximum_braindamage ();

    octave_interpreter_ready = true;
  }

  OCTAVE_THREAD_LOCAL interpreter *interpreter::instance = nullptr;

  interpreter::~interpreter (void)
  {
    shutdown ();

    delete m_gh_manager;
  }

  void interpreter::intern_nargin (octave_idx_type nargs)
  {
    m_evaluator.set_auto_fcn_var (stack_frame::NARGIN, nargs);
  }

  // Read the history file unless a command-line option inhibits that.

  void interpreter::initialize_history (bool read_history_file)
  {
    if (! m_history_initialized)
      {
        // Allow command-line option to override.

        if (m_app_context)
          {
            const cmdline_options& options = m_app_context->options ();

            read_history_file = options.read_history_file ();

            if (! read_history_file)
              command_history::ignore_entries ();
          }

        m_history_system.initialize (read_history_file);

        if (! m_app_context)
          command_history::ignore_entries ();

        m_history_initialized = true;
      }
  }

  // Set the initial path to the system default unless command-line
  // option says to leave it empty.

  void interpreter::initialize_load_path (bool set_initial_path)
  {
    if (! m_load_path_initialized)
      {
        // Allow command-line option to override.

        if (m_app_context)
          {
            const cmdline_options& options = m_app_context->options ();

            set_initial_path = options.set_initial_path ();
          }

        // Temporarily set the execute_pkg_add function to one that
        // catches exceptions.  This is better than wrapping
        // load_path::initialize in a try-catch block because it will
        // not stop executing PKG_ADD files at the first exception.
        // It's also better than changing the default execute_pkg_add
        // function to use safe_source file because that will normally
        // be evaluated from the normal interpreter loop where exceptions
        // are already handled.

        unwind_action restore_add_hook (&load_path::set_add_hook, &m_load_path,
                                        m_load_path.get_add_hook ());

        m_load_path.set_add_hook ([=] (const std::string& dir)
                                  { this->execute_pkg_add (dir); });

        m_load_path.initialize (set_initial_path);

        m_load_path_initialized = true;
      }
  }

  // This may be called separately from execute

  void interpreter::initialize (void)
  {
    if (m_initialized)
      return;

    if (m_app_context)
      {
        const cmdline_options& options = m_app_context->options ();

        if (options.experimental_terminal_widget ())
          {
            if (! options.gui ())
              display_startup_message ();
          }
        else
          display_startup_message ();
      }
    else
      display_startup_message ();

    // Wait to read the history file until the interpreter reads input
    // files and begins evaluating commands.

    initialize_history ();

    // Initializing the load path may execute PKG_ADD files, so can't be
    // done until the interpreter is ready to execute commands.

    // Deferring it to the execute step also allows the path to be
    // initialized between creating and execute the interpreter, for
    // example, to set a custom path for an embedded interpreter.

    initialize_load_path ();

    octave_save_signal_mask ();

    can_interrupt = true;

    octave_signal_hook = respond_to_pending_signals;
    octave_interrupt_hook = nullptr;

    catch_interrupts ();

    // FIXME: could we eliminate this variable or make it not be global?
    // Global used to communicate with signal handler.
    octave_initialized = true;

    m_initialized = true;
  }

  // Note: this function is currently only used with the new
  // experimental terminal widget.

  void interpreter::get_line_and_eval (void)
  {
    m_evaluator.get_line_and_eval ();
  }

  // Note: the following class is currently only used with the new
  // experimental terminal widget.

  class cli_input_reader
  {
  public:

    cli_input_reader (interpreter& interp)
      : m_interpreter (interp), m_thread () { }

    cli_input_reader (const cli_input_reader&) = delete;

    cli_input_reader& operator = (const cli_input_reader&) = delete;

    ~cli_input_reader (void)
    {
      // FIXME: Would it be better to ensure that
      // interpreter::get_line_and_eval exits and then call
      // m_thread.join () here?

      m_thread.detach ();
    }

    void start (void)
    {
      m_thread = std::thread (&interpreter::get_line_and_eval, &m_interpreter);
    }

  private:

    interpreter& m_interpreter;

    std::thread m_thread;
  };

  void interpreter::parse_and_execute (const std::string& input,
                                       bool& incomplete_parse)
  {
    m_evaluator.parse_and_execute (input, incomplete_parse);
  }

  // FIXME: this function is intended to be executed only once.  Should
  // we enforce that restriction?

  int interpreter::execute (void)
  {
    int exit_status = 0;

    try
      {
        initialize ();

        execute_startup_files ();

        if (m_app_context)
          {
            const cmdline_options& options = m_app_context->options ();

            if (m_app_context->have_eval_option_code ())
              {
                int status = execute_eval_option_code ();

                if (status )
                  exit_status = status;

                if (! options.persist ())
                  return exit_status;
              }

            // If there is an extra argument, see if it names a file to
            // read.  Additional arguments are taken as command line options
            // for the script.

            if (m_app_context->have_script_file ())
              {
                int status = execute_command_line_file ();

                if (status)
                  exit_status = status;

                if (! options.persist ())
                  return exit_status;
              }

            if (options.forced_interactive ())
              command_editor::blink_matching_paren (false);

            if (options.server ())
              exit_status = server_loop ();
            else if (options.experimental_terminal_widget ())
              {
                if (options.gui ())
                  {
                    m_event_manager.start_gui (true);

                    exit_status = server_loop ();
                  }
                else
                  {
                    // Use an object so that the thread started for the
                    // reader will be cleaned up no matter how we exit
                    // this function.

                    cli_input_reader reader (*this);

                    reader.start ();

                    exit_status = server_loop ();
                  }
              }
            else
              exit_status = main_loop ();
          }
      }
    catch (const exit_exception& xe)
      {
        exit_status = xe.exit_status ();
      }

    return exit_status;
  }

  // Call a function with exceptions handled to avoid problems with
  // errors while shutting down.

#define OCTAVE_IGNORE_EXCEPTION(E)                                      \
  catch (E)                                                             \
    {                                                                   \
      recover_from_exception ();                                        \
                                                                        \
      std::cerr << "error: ignoring " #E " while preparing to exit"     \
                << std::endl;                                           \
    }

#define OCTAVE_SAFE_CALL(F, ARGS)                                       \
  do                                                                    \
    {                                                                   \
      try                                                               \
        {                                                               \
          unwind_action restore_debug_on_error                          \
            (&error_system::set_debug_on_error, &m_error_system,        \
             m_error_system.debug_on_error ());                        \
                                                                        \
          unwind_action restore_debug_on_warning                        \
            (&error_system::set_debug_on_warning, &m_error_system,      \
             m_error_system.debug_on_warning ());                       \
                                                                        \
          m_error_system.debug_on_error (false);                        \
          m_error_system.debug_on_warning (false);                      \
                                                                        \
          F ARGS;                                                       \
        }                                                               \
      OCTAVE_IGNORE_EXCEPTION (const exit_exception&)                   \
      OCTAVE_IGNORE_EXCEPTION (const interrupt_exception&)              \
      OCTAVE_IGNORE_EXCEPTION (const execution_exception&)              \
      OCTAVE_IGNORE_EXCEPTION (const std::bad_alloc&)                   \
    }                                                                   \
  while (0)

  void interpreter::shutdown (void)
  {
    // Attempt to prevent more than one call to shutdown.

    if (! m_initialized)
      return;

    m_initialized = false;

    OCTAVE_SAFE_CALL (feval, ("close", ovl ("all"), 0));

    // If we are attached to a GUI, process pending events and
    // disable the link.

    OCTAVE_SAFE_CALL (m_event_manager.process_events, (true));
    OCTAVE_SAFE_CALL (m_event_manager.disable, ());

    OCTAVE_SAFE_CALL (m_input_system.clear_input_event_hooks, ());

    // Any atexit functions added after this function call won't be
    // executed.  Each atexit function is executed with
    // OCTAVE_SAFE_CALL, so we don't need that here.

    execute_atexit_fcns ();

    // Clear all functions and variables.

    // Note that we don't force symbols to be cleared, so we will
    // respect mlock at this point.  Later, we'll force all variables
    // and functions to be cleared.

    OCTAVE_SAFE_CALL (clear_all, ());

    // We may still have some figures.  Close them.

    OCTAVE_SAFE_CALL (feval, ("close", ovl ("all"), 0));

    // What is supposed to happen if a figure has a closerequestfcn or
    // deletefcn callback registered that creates other figures or
    // variables?  What if those variables are classdef objects with
    // destructors that can create figures?  The possibilities are
    // endless.  At some point, we have to give up and force execution
    // to end.

    // Note that we again don't force symbols to be cleared, so we
    // continue to respect mlock here.  Later, we'll force all variables
    // and functions to be cleared.

    OCTAVE_SAFE_CALL (clear_all, ());

    // Do this explicitly so that destructors for mex file objects
    // are called, so that functions registered with mexAtExit are
    // called.

    OCTAVE_SAFE_CALL (m_symbol_table.clear_mex_functions, ());

    OCTAVE_SAFE_CALL (command_editor::restore_terminal_state, ());

    OCTAVE_SAFE_CALL (m_history_system.write_timestamp, ());

    if (! command_history::ignoring_entries ())
      OCTAVE_SAFE_CALL (command_history::clean_up_and_save, ());

    OCTAVE_SAFE_CALL (m_gtk_manager.unload_all_toolkits, ());

    // Now that the graphics toolkits have been unloaded, force all
    // symbols to be cleared.

    OCTAVE_SAFE_CALL (clear_all, (true));

    // FIXME:  May still need something like this to ensure that
    // destructors for class objects will run properly.  Should that be
    // done earlier?  Before or after atexit functions are executed?
    // What will happen if the destructor for an obect attempts to
    // display a figure?

    OCTAVE_SAFE_CALL (m_symbol_table.cleanup, ());

    OCTAVE_SAFE_CALL (sysdep_cleanup, ());

    OCTAVE_SAFE_CALL (flush_stdout, ());

    // Don't call singleton_cleanup_list::cleanup until we have the
    // problems with registering/unregistering types worked out.  For
    // example, uncomment the following line, then use the make_int
    // function from the examples directory to create an integer
    // object and then exit Octave.  Octave should crash with a
    // segfault when cleaning up the typinfo singleton.  We need some
    // way to force new octave_value_X types that are created in
    // .oct files to be unregistered when the .oct file shared library
    // is unloaded.
    //
    // OCTAVE_SAFE_CALL (singleton_cleanup_list::cleanup, ());
  }

  void interpreter::execute_atexit_fcns (void)
  {
    // Prevent atexit functions from adding new functions to the list.
    m_executing_atexit = true;

    while (! m_atexit_fcns.empty ())
      {
        std::string fcn = m_atexit_fcns.front ();

        m_atexit_fcns.pop_front ();

        OCTAVE_SAFE_CALL (feval, (fcn, octave_value_list (), 0));

        OCTAVE_SAFE_CALL (flush_stdout, ());
      }
  }

  void interpreter::display_startup_message (void) const
  {
    bool inhibit_startup_message = false;

    if (m_app_context)
      {
        const cmdline_options& options = m_app_context->options ();

        inhibit_startup_message = options.inhibit_startup_message ();
      }

    if (m_interactive && ! inhibit_startup_message)
      std::cout << octave_startup_message () << "\n" << std::endl;
  }

  // Initialize by reading startup files.  Return non-zero if an exception
  // occurs when reading any of them, but don't exit early because of an
  // exception.

  int interpreter::execute_startup_files (void)
  {
    bool read_site_files = m_read_site_files;
    bool read_init_files = m_read_init_files;
    bool verbose = m_verbose;
    bool inhibit_startup_message = m_inhibit_startup_message;

    if (m_app_context)
      {
        const cmdline_options& options = m_app_context->options ();

        read_site_files = options.read_site_files ();
        read_init_files = options.read_init_files ();
        verbose = options.verbose_flag ();
        inhibit_startup_message = options.inhibit_startup_message ();
      }

    verbose = (verbose && ! inhibit_startup_message);

    bool require_file = false;

    std::string context;

    int exit_status = 0;

    if (read_site_files)
      {
        // Execute commands from the site-wide configuration file.
        // First from the file $(prefix)/lib/octave/site/m/octaverc
        // (if it exists), then from the file
        // $(prefix)/share/octave/$(version)/m/octaverc (if it exists).

        int status = safe_source_file (config::local_site_defaults_file (),
                                       context, verbose, require_file);

        if (status)
          exit_status = status;

        status = safe_source_file (config::site_defaults_file (),
                                   context, verbose, require_file);

        if (status)
          exit_status = status;
      }

    if (read_init_files)
      {
        // Try to execute commands from the Matlab compatible startup.m file
        // if it exists anywhere in the load path when starting Octave.
        std::string ff_startup_m = file_in_path ("startup.m", "");

        if (! ff_startup_m.empty ())
          {
            int parse_status = 0;

            try
              {
                eval_string (std::string ("startup"), false, parse_status, 0);
              }
            catch (const interrupt_exception&)
              {
                recover_from_exception ();
              }
            catch (const execution_exception& ee)
              {
                handle_exception (ee);
              }
          }

        // Try to execute commands from $CONFIG/octave/octaverc, where
        // $CONFIG is the platform-dependent location for user local
        // configuration files.

        std::string user_config_dir = sys::env::get_user_config_directory ();

        std::string cfg_dir = user_config_dir + sys::file_ops::dir_sep_str ()
                              + "octave";

        std::string cfg_rc = sys::env::make_absolute ("octaverc", cfg_dir);

        if (! cfg_rc.empty ())
          {
            int status = safe_source_file (cfg_rc, context, verbose,
                                           require_file);

            if (status)
              exit_status = status;
          }

        // Try to execute commands from $HOME/$OCTAVE_INITFILE and
        // $OCTAVE_INITFILE.  If $OCTAVE_INITFILE is not set,
        // .octaverc is assumed.

        bool home_rc_already_executed = false;

        std::string initfile = sys::env::getenv ("OCTAVE_INITFILE");

        if (initfile.empty ())
          initfile = ".octaverc";

        std::string home_dir = sys::env::get_home_directory ();

        std::string home_rc = sys::env::make_absolute (initfile, home_dir);

        std::string local_rc;

        if (! home_rc.empty ())
          {
            int status = safe_source_file (home_rc, context, verbose,
                                           require_file);

            if (status)
              exit_status = status;

            // Names alone are not enough.

            sys::file_stat fs_home_rc (home_rc);

            if (fs_home_rc)
              {
                // We want to check for curr_dir after executing home_rc
                // because doing that may change the working directory.

                local_rc = sys::env::make_absolute (initfile);

                home_rc_already_executed = same_file (home_rc, local_rc);
              }
          }

        if (! home_rc_already_executed)
          {
            if (local_rc.empty ())
              local_rc = sys::env::make_absolute (initfile);

            int status = safe_source_file (local_rc, context, verbose,
                                           require_file);

            if (status)
              exit_status = status;
          }
      }

    if (m_interactive && verbose)
      std::cout << std::endl;

    return exit_status;
  }

  // Execute any code specified with --eval 'CODE'

  int interpreter::execute_eval_option_code (void)
  {
    if (! m_app_context)
      return 0;

    const cmdline_options& options = m_app_context->options ();

    std::string code_to_eval = options.code_to_eval ();

    unwind_protect_var<bool> upv (m_interactive, false);

    int parse_status = 0;

    try
      {
        eval_string (code_to_eval, false, parse_status, 0);
      }
    catch (const interrupt_exception&)
      {
        recover_from_exception ();

        return 1;
      }
    catch (const execution_exception& ee)
      {
        handle_exception (ee);

        return 1;
      }

    return parse_status;
  }

  int interpreter::execute_command_line_file (void)
  {
    if (! m_app_context)
      return 0;

    const cmdline_options& options = m_app_context->options ();

    string_vector args = options.all_args ();

    void (interpreter::*interactive_fptr) (bool) = &interpreter::interactive;
    unwind_action restore_interactive (interactive_fptr, this, m_interactive);

    unwind_action restore_argv (&application::intern_argv, m_app_context, args);

    unwind_action restore_nargin (&interpreter::intern_nargin, this,
                                  args.numel () - 1);

    void (application::*program_invocation_name_fptr) (const std::string&)
      = &application::program_invocation_name;
    unwind_action restore_program_invocation_name
      (program_invocation_name_fptr, m_app_context,
       application::program_invocation_name ());

    void (application::*program_name_fptr) (const std::string&)
      = &application::program_name;
    unwind_action restore_program_name
      (program_name_fptr, m_app_context, application::program_name ());

    m_interactive = false;

    // If we are running an executable script (#! /bin/octave) then
    // we should only see the args passed to the script.

    string_vector script_args = options.remaining_args ();

    m_app_context->intern_argv (script_args);
    intern_nargin (script_args.numel () - 1);

    std::string fname = script_args[0];

    m_app_context->set_program_names (fname);

    std::string context;
    bool verbose = false;
    bool require_file = true;

    return safe_source_file (fname, context, verbose, require_file);
  }

  int interpreter::main_loop (void)
  {
    return m_evaluator.repl ();
  }

  int interpreter::server_loop (void)
  {
    return m_evaluator.server_loop ();
  }

  tree_evaluator& interpreter::get_evaluator (void)
  {
    return m_evaluator;
  }

  stream_list& interpreter::get_stream_list (void)
  {
    return m_stream_list;
  }

  url_handle_manager& interpreter::get_url_handle_manager (void)
  {
    return m_url_handle_manager;
  }

  symbol_scope
  interpreter::get_top_scope (void) const
  {
    return m_evaluator.get_top_scope ();
  }

  symbol_scope
  interpreter::get_current_scope (void) const
  {
    return m_evaluator.get_current_scope ();
  }

  symbol_scope
  interpreter::require_current_scope (const std::string& who) const
  {
    symbol_scope scope = get_current_scope ();

    if (! scope)
      error ("%s: symbol table scope missing", who.c_str ());

    return scope;
  }

  profiler& interpreter::get_profiler (void)
  {
    return m_evaluator.get_profiler ();
  }

  int interpreter::chdir (const std::string& dir)
  {
    std::string xdir = sys::file_ops::tilde_expand (dir);

    int cd_ok = sys::env::chdir (xdir);

    if (! cd_ok)
      error ("%s: %s", dir.c_str (), std::strerror (errno));

    Vlast_chdir_time.stamp ();

    // FIXME: should these actions be handled as a list of functions
    // to call so users can add their own chdir handlers?

    m_load_path.read_dir_config (".");
    m_load_path.update ();

    m_event_manager.directory_changed (sys::env::get_current_directory ());

    return cd_ok;
  }

  void interpreter::mlock (bool skip_first) const
  {
    m_evaluator.mlock (skip_first);
  }

  void interpreter::munlock (bool skip_first) const
  {
    m_evaluator.munlock (skip_first);
  }

  bool interpreter::mislocked (bool skip_first) const
  {
    return m_evaluator.mislocked (skip_first);
  }

  void interpreter::munlock (const char *nm)
  {
    if (! nm)
      error ("munlock: invalid value for NAME");

    munlock (std::string (nm));
  }

  void interpreter::munlock (const std::string& nm)
  {
    octave_value val = m_symbol_table.find_function (nm);

    if (val.is_defined ())
      {
        octave_function *fcn = val.function_value ();

        if (fcn)
          fcn->unlock ();
      }
  }

  bool interpreter::mislocked (const char *nm)
  {
    if (! nm)
      error ("mislocked: invalid value for NAME");

    return mislocked (std::string (nm));
  }

  bool interpreter::mislocked (const std::string& nm)
  {
    bool retval = false;

    octave_value val = m_symbol_table.find_function (nm);

    if (val.is_defined ())
      {
        octave_function *fcn = val.function_value ();

        if (fcn)
          retval = fcn->islocked ();
      }

    return retval;
  }

  std::string interpreter::mfilename (const std::string& opt) const
  {
    return m_evaluator.mfilename (opt);
  }

  octave_value_list interpreter::eval_string (const std::string& eval_str,
                                              bool silent, int& parse_status,
                                              int nargout)
  {
    return m_evaluator.eval_string (eval_str, silent, parse_status, nargout);
  }

  octave_value interpreter::eval_string (const std::string& eval_str,
                                         bool silent, int& parse_status)
  {
    return m_evaluator.eval_string (eval_str, silent, parse_status);
  }

  octave_value_list interpreter::eval_string (const octave_value& arg,
                                              bool silent, int& parse_status,
                                              int nargout)
  {
    return m_evaluator.eval_string (arg, silent, parse_status, nargout);
  }

  octave_value_list interpreter::eval (const std::string& try_code,
                                       int nargout)
  {
    return m_evaluator.eval (try_code, nargout);
  }

  octave_value_list interpreter::eval (const std::string& try_code,
                                       const std::string& catch_code,
                                       int nargout)
  {
    return m_evaluator.eval (try_code, catch_code, nargout);
  }

  octave_value_list interpreter::evalin (const std::string& context,
                                         const std::string& try_code,
                                         int nargout)
  {
    return m_evaluator.evalin (context, try_code, nargout);
  }

  octave_value_list interpreter::evalin (const std::string& context,
                                         const std::string& try_code,
                                         const std::string& catch_code,
                                         int nargout)
  {
    return m_evaluator.evalin (context, try_code, catch_code, nargout);
  }

  //! Evaluate an Octave function (built-in or interpreted) and return
  //! the list of result values.
  //!
  //! @param name The name of the function to call.
  //! @param args The arguments to the function.
  //! @param nargout The number of output arguments expected.
  //! @return A list of output values.  The length of the list is not
  //!         necessarily the same as @c nargout.

  octave_value_list interpreter::feval (const char *name,
                                        const octave_value_list& args,
                                        int nargout)
  {
    return feval (std::string (name), args, nargout);
  }

  octave_value_list interpreter::feval (const std::string& name,
                                        const octave_value_list& args,
                                        int nargout)
  {
    octave_value fcn = m_symbol_table.find_function (name, args);

    if (fcn.is_undefined ())
      error ("feval: function '%s' not found", name.c_str ());

    octave_function *of = fcn.function_value ();

    return of->call (m_evaluator, nargout, args);
  }

  octave_value_list interpreter::feval (octave_function *fcn,
                                        const octave_value_list& args,
                                        int nargout)
  {
    if (fcn)
      return fcn->call (m_evaluator, nargout, args);

    return octave_value_list ();
  }

  octave_value_list interpreter::feval (const octave_value& val,
                                        const octave_value_list& args,
                                        int nargout)
  {
    // FIXME: do we really want to silently return an empty ovl if
    // the function object is undefined?  It's essentially what the
    // version above that accepts a pointer to an octave_function
    // object does and some code was apparently written to rely on it
    // (for example, __ode15__).

    if (val.is_undefined ())
      return ovl ();

    if (val.is_function ())
      {
        return feval (val.function_value (), args, nargout);
      }
    else if (val.is_function_handle () || val.is_inline_function ())
      {
        // This covers function handles, inline functions, and anonymous
        //  functions.

        std::list<octave_value_list> arg_list;
        arg_list.push_back (args);

        // FIXME: could we make octave_value::subsref a const method?
        // It would be difficult because there are instances of
        // incrementing the reference count inside subsref methods,
        // which means they can't be const with the current way of
        // handling reference counting.

        octave_value xval = val;
        return xval.subsref ("(", arg_list, nargout);
      }
    else if (val.is_string ())
      {
        return feval (val.string_value (), args, nargout);
      }
    else
      error ("feval: first argument must be a string, inline function, or a function handle");

    return ovl ();
  }

  //! Evaluate an Octave function (built-in or interpreted) and return
  //! the list of result values.
  //!
  //! @param args The first element of @c args is the function to call.
  //!             It may be the name of the function as a string, a function
  //!             handle, or an inline function.  The remaining arguments are
  //!             passed to the function.
  //! @param nargout The number of output arguments expected.
  //! @return A list of output values.  The length of the list is not
  //!         necessarily the same as @c nargout.

  octave_value_list interpreter::feval (const octave_value_list& args,
                                        int nargout)
  {
    if (args.length () == 0)
      error ("feval: first argument must be a string, inline function, or a function handle");

    octave_value f_arg = args(0);

    octave_value_list tmp_args = args.slice (1, args.length () - 1, true);

    return feval (f_arg, tmp_args, nargout);
  }

  octave_value interpreter::make_function_handle (const std::string& name)
  {
    return m_evaluator.make_fcn_handle (name);
  }

  void interpreter::install_variable (const std::string& name,
                                      const octave_value& value, bool global)
  {
    m_evaluator.install_variable (name, value, global);
  }

  octave_value interpreter::global_varval (const std::string& name) const
  {
    return m_evaluator.global_varval (name);
  }

  void interpreter::global_assign (const std::string& name,
                                   const octave_value& val)
  {
    m_evaluator.global_assign (name, val);
  }

  octave_value interpreter::top_level_varval (const std::string& name) const
  {
    return m_evaluator.top_level_varval (name);
  }

  void interpreter::top_level_assign (const std::string& name,
                                      const octave_value& val)
  {
    m_evaluator.top_level_assign (name, val);
  }

  bool interpreter::is_variable (const std::string& name) const
  {
    return m_evaluator.is_variable (name);
  }

  bool interpreter::is_local_variable (const std::string& name) const
  {
    return m_evaluator.is_local_variable (name);
  }

  octave_value interpreter::varval (const std::string& name) const
  {
    return m_evaluator.varval (name);
  }

  void interpreter::assign (const std::string& name,
                            const octave_value& val)
  {
    m_evaluator.assign (name, val);
  }

  void interpreter::assignin (const std::string& context,
                              const std::string& name,
                              const octave_value& val)
  {
    m_evaluator.assignin (context, name, val);
  }

  void interpreter::source_file (const std::string& file_name,
                                 const std::string& context, bool verbose,
                                 bool require_file)
  {
    m_evaluator.source_file (file_name, context, verbose, require_file);
  }

  bool interpreter::at_top_level (void) const
  {
    return m_evaluator.at_top_level ();
  }

  bool interpreter::isglobal (const std::string& name) const
  {
    return m_evaluator.is_global (name);
  }

  octave_value interpreter::find (const std::string& name)
  {
    return m_evaluator.find (name);
  }

  void interpreter::clear_all (bool force)
  {
    m_evaluator.clear_all (force);
  }

  void interpreter::clear_objects (void)
  {
    m_evaluator.clear_objects ();
  }

  void interpreter::clear_variable (const std::string& name)
  {
    m_evaluator.clear_variable (name);
  }

  void interpreter::clear_variable_pattern (const std::string& pattern)
  {
    m_evaluator.clear_variable_pattern (pattern);
  }

  void interpreter::clear_variable_regexp (const std::string& pattern)
  {
    m_evaluator.clear_variable_regexp (pattern);
  }

  void interpreter::clear_variables (void)
  {
    m_evaluator.clear_variables ();
  }

  void interpreter::clear_global_variable (const std::string& name)
  {
    m_evaluator.clear_global_variable (name);
  }

  void interpreter::clear_global_variable_pattern (const std::string& pattern)
  {
    m_evaluator.clear_global_variable_pattern (pattern);
  }

  void interpreter::clear_global_variable_regexp (const std::string& pattern)
  {
    m_evaluator.clear_global_variable_regexp (pattern);
  }

  void interpreter::clear_global_variables (void)
  {
    m_evaluator.clear_global_variables ();
  }

  void interpreter::clear_functions (bool force)
  {
    m_symbol_table.clear_functions (force);
  }

  void interpreter::clear_function (const std::string& name)
  {
    m_symbol_table.clear_function (name);
  }

  void interpreter::clear_symbol (const std::string& name)
  {
    m_evaluator.clear_symbol (name);
  }

  void interpreter::clear_function_pattern (const std::string& pat)
  {
    m_symbol_table.clear_function_pattern (pat);
  }

  void interpreter::clear_function_regexp (const std::string& pat)
  {
    m_symbol_table.clear_function_regexp (pat);
  }

  void interpreter::clear_symbol_pattern (const std::string& pat)
  {
    return m_evaluator.clear_symbol_pattern (pat);
  }

  void interpreter::clear_symbol_regexp (const std::string& pat)
  {
    return m_evaluator.clear_symbol_regexp (pat);
  }

  std::list<std::string> interpreter::global_variable_names (void)
  {
    return m_evaluator.global_variable_names ();
  }

  std::list<std::string> interpreter::top_level_variable_names (void)
  {
    return m_evaluator.top_level_variable_names ();
  }

  std::list<std::string> interpreter::variable_names (void)
  {
    return m_evaluator.variable_names ();
  }

  std::list<std::string> interpreter::user_function_names (void)
  {
    return m_symbol_table.user_function_names ();
  }

  std::list<std::string> interpreter::autoloaded_functions (void) const
  {
    return m_evaluator.autoloaded_functions ();
  }

  // May be used to send an interrupt signal to the the interpreter from
  // another thread (for example, the GUI).

  void interpreter::interrupt (void)
  {
    static int sigint = 0;
    static bool first = true;

    if (first)
      {
        octave_get_sig_number ("SIGINT", &sigint);
        first = false;
      }

    // Send SIGINT to Octave and (optionally) all other processes in its
    // process group.  The signal handler for SIGINT will set a global
    // variable indicating an interrupt has happened.  That variable is
    // checked in many places in the Octave interpreter and eventually
    // results in an interrupt_exception being thrown.  Finally, that
    // exception is caught and returns control to one of the
    // read-eval-print loops or to the server loop.  We use a signal
    // instead of just setting the global variables here so that we will
    // probably send interrupt signals to any subprocesses as well as
    // interrupt execution of the interpreter.

    pid_t pid
      = m_interrupt_all_in_process_group ? 0 : octave_getpid_wrapper ();

    octave_kill_wrapper (pid, sigint);
  }

  void interpreter::pause (void)
  {
    // FIXME: To be reliable, these tree_evaluator functions must be
    // made thread safe.

    m_evaluator.break_on_next_statement (true);
    m_evaluator.reset_debug_state ();
  }

  void interpreter::stop (void)
  {
    // FIXME: To be reliable, these tree_evaluator functions must be
    // made thread safe.

    if (m_evaluator.in_debug_repl ())
      m_evaluator.dbquit (true);
    else
      interrupt ();
  }

  void interpreter::resume (void)
  {
    // FIXME: To be reliable, these tree_evaluator functions must be
    // made thread safe.

    // FIXME: Should there be any feeback about not doing anything if
    // not in debug mode?

    if (m_evaluator.in_debug_repl ())
      m_evaluator.dbcont ();
  }

  // Provided for convenience.  Will be removed once we eliminate the
  // old terminal widget.
  bool interpreter::experimental_terminal_widget (void) const
  {
    if (! m_app_context)
      return false;

    // Embedded interpreters don't execute command line options.
    const cmdline_options& options = m_app_context->options ();

    return options.experimental_terminal_widget ();
  }

  void interpreter::add_debug_watch_expression (const std::string& expr)
  {
    m_evaluator.add_debug_watch_expression (expr);
  }

  void interpreter::remove_debug_watch_expression (const std::string& expr)
  {
    m_evaluator.remove_debug_watch_expression (expr);
  }

  void interpreter::clear_debug_watch_expressions (void)
  {
    m_evaluator.clear_debug_watch_expressions ();
  }

  std::set<std::string> interpreter::debug_watch_expressions (void) const
  {
    return m_evaluator.debug_watch_expressions ();
  }

  void interpreter::handle_exception (const execution_exception& ee)
  {
    m_error_system.save_exception (ee);

    // FIXME: use a separate stream instead of std::cerr directly so that
    // error messages can be redirected more easily?  Pass the message
    // to an event manager function?
    m_error_system.display_exception (ee);

    recover_from_exception ();
  }

  void interpreter::recover_from_exception (void)
  {
    if (octave_interrupt_state)
      m_event_manager.interpreter_interrupted ();

    can_interrupt = true;
    octave_interrupt_state = 0;
    octave_signal_caught = 0;
    octave_restore_signal_mask ();
    catch_interrupts ();
  }

  void interpreter::mark_for_deletion (const std::string& file)
  {
    m_tmp_files.insert (file);
  }

  void interpreter::cleanup_tmp_files (void)
  {
    m_tmp_files.cleanup ();
  }

  void interpreter::quit (int exit_status, bool force, bool confirm)
  {
    if (! force)
      {
        try
          {
            bool cancel = false;

            if (symbol_exist ("finish.m", "file"))
              {
                unwind_protect_var<bool> upv1 (m_executing_finish_script, true);
                unwind_protect_var<bool> upv2 (m_cancel_quit);

                evalin ("base", "finish", 0);

                cancel = m_cancel_quit;
              }

            if (cancel)
              return;

            // Check for confirmation.

            if (confirm && ! m_event_manager.confirm_shutdown ())
              return;
          }
        catch (const execution_exception&)
          {
            // Catch execution_exceptions so we don't throw an
            // exit_exception if there is an in finish.m.  But throw it
            // again so that will be handled as any other
            // execution_exception by the evaluator.  This way, errors
            // will be ignored properly and we won't exit if quit is
            // called recursively from finish.m.

            throw;
          }
      }

    throw exit_exception (exit_status);
  }

  void interpreter::add_atexit_fcn (const std::string& fname)
  {
    if (m_executing_atexit)
      return;

    m_atexit_fcns.push_front (fname);
  }

  bool interpreter::remove_atexit_fcn (const std::string& fname)
  {
    bool found = false;

    for (auto it = m_atexit_fcns.begin ();
         it != m_atexit_fcns.end (); it++)
      {
        if (*it == fname)
          {
            m_atexit_fcns.erase (it);
            found = true;
            break;
          }
      }

    return found;
  }

  void interpreter::add_atexit_function (const std::string& fname)
  {
    interpreter& interp
      = __get_interpreter__ ("interpreter::add_atexit_function");

    interp.add_atexit_fcn (fname);
  }

  bool interpreter::remove_atexit_function (const std::string& fname)
  {
    interpreter& interp
      = __get_interpreter__ ("interpreter::remove_atexit_function");

    return interp.remove_atexit_fcn (fname);
  }

  // What internal options get configured by --traditional.

  void interpreter::maximum_braindamage (void)
  {
    m_input_system.PS1 (">> ");
    m_input_system.PS2 ("");

    m_evaluator.PS4 ("");

    m_load_save_system.crash_dumps_octave_core (false);
    m_load_save_system.save_default_options ("-mat-binary");

    m_history_system.timestamp_format_string ("%%-- %D %I:%M %p --%%");

    m_error_system.beep_on_error (true);

    Fconfirm_recursive_rmdir (octave_value (false));
    Fdisable_diagonal_matrix (octave_value (true));
    Fdisable_permutation_matrix (octave_value (true));
    Fdisable_range (octave_value (true));
    Ffixed_point_format (octave_value (true));
    Fprint_empty_dimensions (octave_value (false));
    Fprint_struct_array_contents (octave_value (true));
    Fstruct_levels_to_print (octave_value (0));

    disable_warning ("Octave:abbreviated-property-match");
    disable_warning ("Octave:colon-nonscalar-argument");
    disable_warning ("Octave:data-file-in-path");
    disable_warning ("Octave:empty-index");
    disable_warning ("Octave:function-name-clash");
    disable_warning ("Octave:possible-matlab-short-circuit-operator");
  }

  void interpreter::execute_pkg_add (const std::string& dir)
  {
    try
      {
        m_load_path.execute_pkg_add (dir);
      }
    catch (const interrupt_exception&)
      {
        recover_from_exception ();
      }
    catch (const execution_exception& ee)
      {
        handle_exception (ee);
      }
  }
}