diff --git a/SYMBOLS_MANIFEST.txt b/SYMBOLS_MANIFEST.txt
index d5af2742a..86ee80766 100644
--- a/SYMBOLS_MANIFEST.txt
+++ b/SYMBOLS_MANIFEST.txt
@@ -325,6 +325,7 @@ System`DesignMatrix
System`Det
System`Diagonal
System`DiagonalMatrix
+System`Dialog
System`Diamond
System`DiamondMatrix
System`DiceDissimilarity
diff --git a/mathics/__main__.py b/mathics/__main__.py
index 9f2222afa..97db6787f 100755
--- a/mathics/__main__.py
+++ b/mathics/__main__.py
@@ -11,33 +11,21 @@
import argparse
import atexit
import cProfile
-import locale
-import os
-import os.path as osp
-import re
-import subprocess
import sys
-from typing import List, Optional
+from typing import Optional
-import mathics_scanner.location
-from mathics_scanner.location import ContainerKind
-
-import mathics.core as mathics_core
+import mathics.session
from mathics import __version__, license_string, settings, version_string
-from mathics.builtin.trace import TraceBuiltins, traced_apply_function
-from mathics.core.atoms import String
-from mathics.core.definitions import Definitions, Symbol
-from mathics.core.evaluation import Evaluation, Output
-from mathics.core.expression import Expression
+from mathics.builtin.tuning_debug.trace import TraceBuiltins, traced_apply_function
+from mathics.core.definitions import Definitions
+from mathics.core.evaluation import Evaluation
from mathics.core.load_builtin import import_and_load_builtins
-from mathics.core.parser import MathicsFileLineFeeder, MathicsLineFeeder
+from mathics.core.parser import MathicsFileLineFeeder
from mathics.core.rules import FunctionApplyRule
-from mathics.core.streams import stream_manager
-from mathics.core.symbols import SymbolNull, strip_context
+from mathics.core.symbols import SymbolNull
+from mathics.core.systemsymbols import SymbolAborted, SymbolOverflow
from mathics.eval.files_io.files import set_input_var
-from mathics.eval.files_io.read import channel_to_stream
-from mathics.interrupt import setup_signal_handler
-from mathics.session import autoload_files
+from mathics.repl import TerminalOutput, TerminalShell, eval_loop, interactive_eval_loop
from mathics.settings import DATA_DIR, USER_PACKAGE_DIR, ensure_directory
from mathics.timing import show_lru_cache_statistics
@@ -47,342 +35,6 @@
import_and_load_builtins()
-try:
- import readline
-except ImportError:
-
- def user_write_history_file():
- return
-
- pass
-else:
- # Set up mathics3 configuration directory
- CONFIGHOME = os.environ.get("XDG_CONFIG_HOME", osp.expanduser("~/.config"))
- CONFIGDIR = osp.join(CONFIGHOME, "Mathics3")
- os.makedirs(CONFIGDIR, exist_ok=True)
- HISTFILE = os.environ.get("MATHICS3_HISTFILE", osp.join(CONFIGDIR, "history"))
-
- def user_write_history_file():
- try:
- # print(f"Writing {HISTFILE}")
- readline.write_history_file(HISTFILE)
- except: # noqa
- pass
-
-
-def get_srcdir():
- filename = osp.normcase(osp.dirname(osp.abspath(__file__)))
- return osp.realpath(filename)
-
-
-def show_echo(query, evaluation):
- echovar = evaluation.definitions.get_ownvalue("System`$Echo")
- if not isinstance(echovar, Expression) or not echovar.has_form("List", None):
- return
-
- for element in echovar.elements:
- if isinstance(element, String) and element.value == "stdout":
- stream = stream_manager.lookup_stream(1)
- else:
- strm = channel_to_stream(element, mode="w")
- if strm is None:
- continue
- stream = stream_manager.lookup_stream(strm.elements[1].value)
- if stream is None or stream.io is None or stream.io.closed:
- continue
- if stream is not None and stream.io is not None:
- stream.io.write(query + "\n")
-
-
-class TerminalShell(MathicsLineFeeder):
- def __init__(
- self,
- definitions,
- colors,
- want_readline,
- want_completion,
- autoload=False,
- in_prefix: str = "In",
- out_prefix: str = "Out",
- ):
- super(TerminalShell, self).__init__([], ContainerKind.STREAM)
- self.input_encoding = locale.getpreferredencoding()
-
- # is_inside_interrupt is set True when shell has been
- # interrupted via an interrupt handler.
- self.is_inside_interrupt = False
-
- self.lineno = 0
- self.in_prefix = in_prefix
- self.out_prefix = out_prefix
-
- # Try importing readline to enable arrow keys support etc.
- self.using_readline = False
- try:
- if want_readline:
- try:
- # Load history from file
- readline.read_history_file(HISTFILE)
- atexit.register(user_write_history_file)
- except FileNotFoundError:
- # Create an empty history file.
- with open(HISTFILE, "w"):
- pass
- atexit.register(user_write_history_file)
- except IOError:
- pass
- except: # noqa
- # PyPy read_history_file fails
- pass
- self.using_readline = sys.stdin.isatty() and sys.stdout.isatty()
- self.ansi_color_re = re.compile("\033\\[[0-9;]+m")
- if want_completion:
- readline.set_completer(
- lambda text, state: self.complete_symbol_name(text, state)
- )
-
- # Make _ a delimiter, but not $ or `
- readline.set_completer_delims(
- " \t\n_~!@#%^&*()-=+[{]}\\|;:'\",<>/?"
- )
-
- readline.parse_and_bind("tab: complete")
- self.completion_candidates: List[str] = []
-
- except ImportError:
- pass
-
- # Try importing colorama to escape ansi sequences for cross platform
- # colors
- try:
- from colorama import init as colorama_init
- except ImportError:
- colors = "NoColor"
- else:
- colorama_init()
- if colors is None:
- terminal_supports_color = (
- sys.stdout.isatty() and os.getenv("TERM") != "dumb"
- )
- colors = "Linux" if terminal_supports_color else "NoColor"
-
- color_schemes = {
- "NOCOLOR": (["", "", "", ""], ["", "", "", ""]),
- "NONE": (["", "", "", ""], ["", "", "", ""]),
- "LINUX": (
- ["\033[32m", "\033[1m", "\033[0m\033[32m", "\033[39m"],
- ["\033[31m", "\033[1m", "\033[0m\033[31m", "\033[39m"],
- ),
- "LIGHTBG": (
- ["\033[34m", "\033[1m", "\033[22m", "\033[39m"],
- ["\033[31m", "\033[1m", "\033[22m", "\033[39m"],
- ),
- }
-
- # Handle any case by using .upper()
- term_colors = color_schemes.get(colors.upper())
- if term_colors is None:
- out_msg = "The 'colors' argument must be {0} or None"
- print(out_msg.format(repr(list(color_schemes.keys()))))
- sys.exit()
-
- self.incolors, self.outcolors = term_colors
- self.definitions = definitions
- if autoload:
- autoload_files(definitions, get_srcdir(), "autoload-cli")
-
- def get_last_line_number(self):
- return self.definitions.get_line_no()
-
- def get_in_prompt(self):
- next_line_number = self.get_last_line_number() + 1
- if self.lineno > 0:
- return " " * len("{0}[{1}]:= ".format(self.in_prefix, next_line_number))
- else:
- return "{2}{0}[{3}{1}{4}]:= {5}".format(
- self.in_prefix, next_line_number, *self.incolors
- )
-
- def get_out_prompt(self, form=None):
- line_number = self.get_last_line_number()
- if form:
- return "{3}{0}[{4}{1}{5}]//{2}= {6}".format(
- self.out_prefix, line_number, form, *self.outcolors
- )
- return "{2}{0}[{3}{1}{4}]= {5}".format(
- self.out_prefix, line_number, *self.outcolors
- )
-
- def to_output(self, text, form=None):
- line_number = self.get_last_line_number()
- newline = "\n" + " " * len("Out[{0}]= ".format(line_number))
- if form:
- newline += (len(form) + 2) * " "
- return newline.join(text.splitlines())
-
- def out_callback(self, out, fmt=None):
- print(self.to_output(str(out), fmt))
-
- def read_line(self, prompt):
- if self.using_readline:
- return self.rl_read_line(prompt)
- return input(prompt)
-
- def print_result(self, result, no_out_prompt=False, strict_wl_output=False):
- if result is None or result.last_eval is SymbolNull:
- # Following WMA CLI, if the result is `SymbolNull`, just print an empty line.
- print("")
- return
-
- form = result.form
- last_eval = result.last_eval
-
- eval_type = None
- if last_eval is not None:
- try:
- eval_type = last_eval.get_head_name()
- except Exception:
- print(sys.exc_info()[1])
- return
-
- out_str = str(result.result)
- if eval_type == "System`String" and not strict_wl_output:
- out_str = '"' + out_str.replace('"', r"\"") + '"'
- if eval_type == "System`Graph":
- out_str = "-Graph-"
-
- output = self.to_output(out_str, form)
- mess = self.get_out_prompt(form) if not no_out_prompt else ""
- print(mess + output + "\n")
-
- def rl_read_line(self, prompt):
- # Wrap ANSI colour sequences in \001 and \002, so readline
- # knows that they're nonprinting.
- prompt = self.ansi_color_re.sub(lambda m: "\001" + m.group(0) + "\002", prompt)
-
- return input(prompt)
-
- def complete_symbol_name(self, text, state):
- try:
- return self._complete_symbol_name(text, state)
- except Exception:
- # any exception thrown inside the completer gets silently
- # thrown away otherwise
- print("Unhandled error in readline completion")
-
- def _complete_symbol_name(self, text, state):
- # The readline module calls this function repeatedly,
- # increasing 'state' each time and expecting one string to be
- # returned per call.
-
- if state == 0:
- self.completion_candidates = self.get_completion_candidates(text)
-
- try:
- return self.completion_candidates[state]
- except IndexError:
- return None
-
- def get_completion_candidates(self, text):
- matches = self.definitions.get_matching_names(text + "*")
- if "`" not in text:
- matches = [strip_context(m) for m in matches]
- return matches
-
- def reset_lineno(self):
- self.lineno = 0
-
- def feed(self):
- result = self.read_line(self.get_in_prompt()) + "\n"
- if mathics_scanner.location.TRACK_LOCATIONS:
- self.container.append(self.source_text)
- if result == "\n":
- return "" # end of input
- self.lineno += 1
- return result
-
- def empty(self):
- return False
-
-
-class TerminalOutput(Output):
- def max_stored_size(self, output_settings):
- return None
-
- def __init__(self, shell):
- self.shell = shell
-
- def out(self, out):
- return self.shell.out_callback(out)
-
-
-def eval_loop(feeder: MathicsFileLineFeeder, shell: TerminalShell):
- """
- A read eval/loop for things having file input `feeder`.
- `shell` is a shell session
- """
- try:
- while not feeder.empty():
- evaluation = Evaluation(
- shell.definitions,
- output=TerminalOutput(shell),
- catch_interrupt=False,
- )
-
- query = evaluation.parse_feeder(feeder)
- if query is None:
- continue
- evaluation.evaluate(query, timeout=settings.TIMEOUT)
- except KeyboardInterrupt:
- print("\nKeyboardInterrupt")
-
-
-def interactive_eval_loop(
- shell: TerminalShell, full_form: bool, strict_wl_output: bool
-):
- """
- A read eval/loop for an interactive session.
- `shell` is a shell session
- """
- setup_signal_handler()
- while True:
- try:
- evaluation = Evaluation(shell.definitions, output=TerminalOutput(shell))
-
- # Store shell into the evaluation so that an interrupt handler
- # has access to this
- evaluation.shell = shell
-
- query, source_code = evaluation.parse_feeder_returning_code(shell)
- if mathics_core.PRE_EVALUATION_HOOK is not None:
- mathics_core.PRE_EVALUATION_HOOK(query, evaluation)
-
- show_echo(source_code, evaluation)
- if len(source_code) and source_code[0] == "!":
- if not settings.ENABLE_SYSTEM_COMMANDS:
- evaluation.message("Run", "dis")
- else:
- subprocess.run(source_code[1:], shell=True)
- shell.definitions.increment_line_no(1)
- continue
- if query is None:
- continue
- if full_form:
- print(query)
- result = evaluation.evaluate(query, timeout=settings.TIMEOUT)
- if result is not None:
- shell.print_result(result, strict_wl_output=strict_wl_output)
- except KeyboardInterrupt:
- print("\nKeyboardInterrupt")
- except EOFError:
- print("\n\nGoodbye!\n")
- break
- except SystemExit:
- # raise to pass the error code on, e.g. Quit[1]
- raise
- finally:
- shell.reset_lineno()
-
class VersionAction(argparse.Action):
def __init__(self, option_strings, version=Optional[str], **kwargs):
@@ -558,7 +210,7 @@ def dump_tracing_stats():
)
definitions.set_line_no(0)
- shell = TerminalShell(
+ shell = mathics.session.shell_session = TerminalShell(
definitions,
args.colors,
want_readline=not (args.no_readline),
@@ -612,9 +264,9 @@ def run_it():
)
if evaluation.exc_result is SymbolNull:
exit_rc = 0
- elif evaluation.exc_result is Symbol("$Aborted"):
+ elif evaluation.exc_result is SymbolAborted:
exit_rc = -1
- elif evaluation.exc_result is Symbol("Overflow"):
+ elif evaluation.exc_result is SymbolOverflow:
exit_rc = -2
else:
exit_rc = -3
diff --git a/mathics/builtin/tuning_debug/dialog.py b/mathics/builtin/tuning_debug/dialog.py
new file mode 100644
index 000000000..baf874232
--- /dev/null
+++ b/mathics/builtin/tuning_debug/dialog.py
@@ -0,0 +1,31 @@
+"""
+Debugging
+"""
+
+from mathics.core.attributes import A_HOLD_ALL, A_PROTECTED
+from mathics.core.builtin import Builtin
+from mathics.core.element import BaseElement
+from mathics.core.evaluation import Evaluation
+from mathics.eval.debug_tuning.dialog import eval_Dialog
+
+
+class Dialog(Builtin):
+ r"""
+