changeset 9:822a2fe5bb51

move command window to separate file and other refactoring
author John W. Eaton <jwe@octave.org>
date Thu, 23 May 2019 12:36:26 -0400
parents 7c4b04a6346d
children 69f5d5e42d2b
files NOTES calc.pro command-window.cpp command-window.h gui-main.cpp gui-main.h interpreter.cpp main.cpp main.h parser.yy tty-main.cpp tty-main.h
diffstat 12 files changed, 570 insertions(+), 592 deletions(-) [+]
line wrap: on
line diff
--- a/NOTES	Thu May 23 11:26:44 2019 -0400
+++ b/NOTES	Thu May 23 12:36:26 2019 -0400
@@ -77,3 +77,72 @@
 
   * Do we need text position markers to keep track of the prompt position
     (beginning of current line) when inserting or clearing text?
+
+
+readline callback functions:
+
+// -- Variable: rl_getc_func_t * rl_getc_function
+//     If non-zero, Readline will call indirectly through this pointer to
+//     get a character from the input stream.  By default, it is set to
+//     `rl_getc', the default Readline character input function (*note
+//     Character Input::).
+
+// -- Variable: rl_voidfunc_t * rl_redisplay_function
+//     If non-zero, Readline will call indirectly through this pointer to
+//     update the display with the current contents of the editing buffer.
+//     By default, it is set to `rl_redisplay', the default Readline
+//     redisplay function (*note Redisplay::).
+
+// -- Variable: rl_vintfunc_t * rl_prep_term_function
+//     If non-zero, Readline will call indirectly through this pointer to
+//     initialize the terminal.  The function takes a single argument, an
+//     `int' flag that says whether or not to use eight-bit characters.
+//     By default, this is set to `rl_prep_terminal' (*note Terminal
+//     Management::).
+
+// -- Variable: rl_voidfunc_t * rl_deprep_term_function
+//     If non-zero, Readline will call indirectly through this pointer to
+//     reset the terminal.  This function should undo the effects of
+//     `rl_prep_term_function'.  By default, this is set to
+//     `rl_deprep_terminal' (*note Terminal Management::).
+
+// -- Function: void rl_callback_handler_install (const char *prompt,
+//          rl_vcpfunc_t *lhandler)
+//     Set up the terminal for readline I/O and display the initial
+//     expanded value of PROMPT.  Save the value of LHANDLER to use as a
+//     function to call when a complete line of input has been entered.
+//     The function takes the text of the line as an argument.
+
+// -- Function: void rl_callback_read_char (void)
+//     Whenever an application determines that keyboard input is
+//     available, it should call `rl_callback_read_char()', which will
+//     read the next character from the current input source.  If that
+//     character completes the line, `rl_callback_read_char' will invoke
+//     the LHANDLER function saved by `rl_callback_handler_install' to
+//     process the line.  Before calling the LHANDLER function, the
+//     terminal settings are reset to the values they had before calling
+//     `rl_callback_handler_install'.  If the LHANDLER function returns,
+//     the terminal settings are modified for Readline's use again.
+//     `EOF' is  indicated by calling LHANDLER with a `NULL' line.
+
+// -- Function: void rl_callback_handler_remove (void)
+//     Restore the terminal to its initial state and remove the line
+//     handler.  This may be called from within a callback as well as
+//     independently.  If the LHANDLER installed by
+//     `rl_callback_handler_install' does not exit the program, either
+//     this function or the function referred to by the value of
+//     `rl_deprep_term_function' should be called before the program
+//     exits to reset the terminal settings.
+
+// -- Variable: rl_compdisp_func_t * rl_completion_display_matches_hook
+//     If non-zero, then this is the address of a function to call when
+//     completing a word would normally display the list of possible
+//     matches.  This function is called in lieu of Readline displaying
+//     the list.  It takes three arguments: (`char **'MATCHES, `int'
+//     NUM_MATCHES, `int' MAX_LENGTH) where MATCHES is the array of
+//     matching strings, NUM_MATCHES is the number of strings in that
+//     array, and MAX_LENGTH is the length of the longest string in that
+//     array.  Readline provides a convenience function,
+//     `rl_display_match_list', that takes care of doing the display to
+//     Readline's output stream.  That function may be called from this
+//     hook.
--- a/calc.pro	Thu May 23 11:26:44 2019 -0400
+++ b/calc.pro	Thu May 23 12:36:26 2019 -0400
@@ -1,20 +1,21 @@
 MAKEFILE = calc.mk
 
 SOURCES = \
-  parser.cpp \
+  command-window.cpp \
+  gui-main.cpp \
   interpreter.cpp \
   main.cpp \
-  gui-main.cpp \
-  tty-main.cpp \
-  qt-interpreter.cpp
+  parser.cpp \
+  qt-interpreter.cpp \
+  tty-main.cpp
 
 HEADERS = \
-  parser.h \
+  command-window.h \
+  gui-main.h \
   interpreter.h \
-  main.h \
-  gui-main.h \
-  tty-main.h \
-  qt-interpreter.h
+  parser.h \
+  qt-interpreter.h \
+  tty-main.h
 
 LIBS += -lreadline
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/command-window.cpp	Thu May 23 12:36:26 2019 -0400
@@ -0,0 +1,379 @@
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <cstdlib>
+#include <cstring>
+
+#include <QApplication>
+#include <QKeyEvent>
+#include <QTextDocument>
+#include <QTextEdit>
+#include <QTimer>
+
+#include "command-window.h"
+#include "interpreter.h"
+#include "parser.h"
+
+#include <readline/readline.h>
+#include <readline/history.h>
+
+namespace calc
+{
+  static int available_char = 0;
+
+  // vvvvv readline vvvvv
+
+  static int getc (FILE *)
+  {
+    int tmp = available_char;
+    available_char = 0;
+    return tmp;
+  }
+
+  static void redisplay (void)
+  {
+    if (command_window::the_command_window)
+      emit command_window::the_command_window->redisplay_signal ();
+  }
+
+  static void prep_term (int)
+  {
+  }
+
+  static void deprep_term (void)
+  {
+  }
+
+  static void accept_line (char *line)
+  {
+    if (command_window::the_command_window)
+      command_window::the_command_window->accept_line (line ? line : "");
+  }
+
+  static void display_completion_matches (char **matches, int num_matches,
+                                          int /* max_length */)
+  {
+    if (command_window::the_command_window)
+      {
+        std::ostringstream buf;
+
+        if (num_matches > 1)
+          buf << "\n";
+
+        for (int i = 1; i < num_matches; i++)
+          buf << matches[i] << "\n";
+
+        command_window::the_command_window->insert_at_end (buf.str ());
+
+        emit command_window::the_command_window->redisplay_signal ();
+      }
+  }
+
+  void readline_init (void)
+  {
+    rl_initialize ();
+
+    rl_getc_function = getc;
+    rl_redisplay_function = redisplay;
+    rl_prep_term_function = prep_term;
+    rl_deprep_term_function = deprep_term;
+    rl_completion_display_matches_hook = display_completion_matches;
+
+    rl_callback_handler_install (">> ", accept_line);
+  }
+
+  void readline_fini (void)
+  {
+    rl_callback_handler_remove ();
+  }
+
+  // ^^^^^ readline ^^^^^
+
+  command_window *command_window::the_command_window = nullptr;
+
+  command_window::command_window (QWidget *p)
+    : QTextEdit (p),
+      m_buffer (new QTextDocument ()),
+      m_interpreter (new calc::qt_interpreter ()),
+      beg_mark (), prompt_mark ()
+  {
+    if (the_command_window)
+      {
+        std::cerr << "multiple command_windows are not possible!" << std::endl;
+        exit (1);
+      }
+
+    the_command_window = this;
+
+    setWindowTitle ("Qt::TextEdit example");
+
+    setMinimumSize (QSize (600, 400));
+
+    setDocument (m_buffer);
+
+    connect (m_interpreter, SIGNAL (result_ready (double)),
+             this, SLOT (handle_result (double)));
+
+    connect (m_interpreter, SIGNAL (error_signal (const QString&)),
+             this, SLOT (handle_error (const QString&)));
+
+    connect (this, SIGNAL (input_char_available (int)),
+             this, SLOT (handle_input_char (int)));
+
+    connect (this, SIGNAL (redisplay_signal (void)),
+             this, SLOT (redisplay (void)));
+
+    connect (this, SIGNAL (accept_line_signal (const QString&)),
+             m_interpreter, SLOT (accept_input_line (const QString&)));
+
+    insert_at_end
+      ("Qt Example Calculator.\n"
+       "Available operations: + - * / ^ ()\n"
+       "Semicolon terminates statement.\n"
+       "Up Arrow key moves to previous line in the command history.\n"
+       "Down Arrow key moves to next line in the comand history.\n\n");
+
+    beg_mark = set_mark ();
+
+    // Defer initializing and executing the interpreter until after the main
+    // window and QApplication are running to prevent race conditions
+    QTimer::singleShot (0, m_interpreter, SLOT (execute (void)));
+  }
+
+  // Accept an input line, parse and possibly execute it.
+
+  void command_window::accept_line (const std::string& line)
+  {
+    insert_at_end ("\n");
+
+    if (! line.empty ())
+      {
+        add_history (line.c_str ());
+        using_history ();
+
+        emit accept_line_signal (QString::fromStdString (line));
+      }
+  }
+
+  void command_window::insert_at_end (const std::string& text)
+  {
+    scroll_to_bottom ();
+
+    insert_at_cursor (text);
+  }
+
+  void command_window::handle_error (const QString& msg)
+  {
+    insert_at_end ("parse error: " + msg.toStdString () + "\n");
+
+    rl_abort (0, 0);
+  }
+
+  // FIXME: do we really need this extra function?
+  void command_window::handle_result (double value)
+  {
+    insert_result (value);
+  }
+
+  // Redisplay current command line.
+
+  void command_window::redisplay (void)
+  {
+    erase_line ();
+
+    std::string line = rl_line_buffer ? rl_line_buffer : "";
+    std::string prompt = (rl_prompt && parser::beg_of_stmt) ? rl_prompt : "";
+
+    insert_line (prompt, line);
+
+    scroll_to_bottom ();
+
+    QTextCursor cursor = textCursor ();
+
+    cursor.setPosition (prompt_mark + rl_point, QTextCursor::MoveAnchor);
+
+    setTextCursor (cursor);
+  }
+
+  void command_window::keyPressEvent (QKeyEvent *event)
+  {
+    if (! event)
+      return;
+
+    if (event->type () == QEvent::KeyPress)
+      {
+        int key = event->key ();
+
+        switch (key)
+          {
+          case Qt::Key_Return:
+            key = 0x0A;
+            break;
+
+          case Qt::Key_Backspace:
+            key = 0x08;
+            break;
+
+          case Qt::Key_Tab:
+            key = 0x09;
+            break;
+
+          case Qt::Key_Escape:
+            key = 0x1b;
+            break;
+
+          case Qt::Key_Up:
+          case Qt::Key_Down:
+          case Qt::Key_Right:
+          case Qt::Key_Left:
+            key = do_arrow_key (key);
+            break;
+
+          default:
+            {
+              switch (event->modifiers ())
+                {
+                case Qt::ControlModifier:
+                  if (key > 0x3f && key < 0x7b)
+                    key &= 0x1f;
+                  else
+                    key = -1;
+                  break;
+
+                default:
+                  {
+                    // Don't shoot me, this is just a demo...
+                    QString text = event->text ();
+                    QByteArray latin_text = text.toLatin1 ();
+                    key = latin_text[0];
+                  }
+                  break;
+                }
+            }
+            break;
+          }
+
+        if (key >= 0)
+          emit input_char_available (key);
+      }
+  }
+
+  void command_window::handle_input_char (int key)
+  {
+    available_char = key;
+    rl_callback_read_char ();
+  }
+
+  int command_window::do_arrow_key (int arrow_key)
+  {
+    int retval = 0;
+
+    available_char = 0x1b;
+    rl_callback_read_char ();
+
+    available_char = '[';
+    rl_callback_read_char ();
+
+    switch (arrow_key)
+      {
+      case Qt::Key_Up:
+        retval = 'A';
+        break;
+
+      case Qt::Key_Down:
+        retval = 'B';
+        break;
+
+      case Qt::Key_Right:
+        retval = 'C';
+        break;
+
+      case Qt::Key_Left:
+        retval = 'D';
+        break;
+      }
+
+    return retval;
+  }
+
+  // Retrieve a command from the history list and insert it on the
+  // current command.
+
+  void command_window::history (bool up)
+  {
+    HIST_ENTRY *entry = up ? previous_history () : next_history ();
+
+    if (entry)
+      {
+        erase_line ();
+
+        std::string prompt = rl_prompt ? rl_prompt : "";
+
+        insert_line (prompt, entry->line);
+      }
+    else if (! up)
+      erase_line ();
+
+    scroll_to_bottom ();
+  }
+
+  void command_window::erase_line (void)
+  {
+    QTextCursor cursor = textCursor ();
+
+    cursor.movePosition (QTextCursor::End);
+    cursor.select (QTextCursor::LineUnderCursor);
+    cursor.removeSelectedText ();
+
+    setTextCursor (cursor);
+  }
+
+  void command_window::insert_at_cursor (const std::string& text)
+  {
+    QTextCursor cursor = textCursor ();
+
+    cursor.insertText (QString::fromStdString (text));
+
+    setTextCursor (cursor);
+  }
+
+  void command_window::insert_line (const std::string& prompt,
+                                    const std::string& line)
+  {
+    beg_mark = set_mark ();
+
+    insert_at_cursor (prompt);
+
+    prompt_mark = set_mark ();
+
+    insert_at_cursor (line);
+  }
+
+  int command_window::set_mark (void)
+  {
+    return textCursor ().position ();
+  }
+
+  void command_window::insert_result (double value)
+  {
+    std::ostringstream buf;
+
+    buf << "ans = " << value << "\n";
+
+    insert_at_cursor (buf.str ());
+
+    beg_mark = set_mark ();
+
+    scroll_to_bottom ();
+  }
+
+  void command_window::scroll_to_bottom (void)
+  {
+    QTextCursor cursor = textCursor ();
+
+    cursor.movePosition (QTextCursor::End);
+
+    setTextCursor (cursor);
+  }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/command-window.h	Thu May 23 12:36:26 2019 -0400
@@ -0,0 +1,93 @@
+#if ! defined (calc_command_window_h)
+#define calc_command_window_h 1
+
+#include <string>
+
+#include <QTextDocument>
+#include <QTextEdit>
+
+#include "qt-interpreter.h"
+
+namespace calc
+{
+  class command_window : public QTextEdit
+  {
+    Q_OBJECT
+
+  public:
+
+    static command_window *the_command_window;
+
+    command_window (QWidget *p = nullptr);
+
+    virtual ~command_window (void) = default;
+
+    // Accept an input line, parse and possibly execute it.
+
+    void accept_line (const std::string& line);
+
+    // Redisplay current command line.
+
+    void insert_at_end (const std::string& text);
+
+    void emit_error (const std::string& msg);
+
+    void emit_result (double value);
+
+  signals:
+
+    void input_char_available (int key);
+
+    void accept_line_signal (const QString& line);
+
+    void redisplay_signal (void);
+
+  public slots:
+
+    void handle_input_char (int key);
+
+    void handle_error (const QString& msg);
+
+    void handle_result (double value);
+
+    void redisplay (void);
+
+  protected:
+
+    void keyPressEvent (QKeyEvent *event);
+
+    int do_arrow_key (int arrow_key);
+
+    // Retrieve a command from the history list and insert it on the
+    // current command.
+
+    void history (bool up);
+
+    void erase_line (void);
+
+    void insert_at_cursor (const std::string& text);
+
+    void insert_line (const std::string& prompt, const std::string& line);
+
+    int set_mark (void);
+
+    void insert_result (double value);
+
+    void scroll_to_bottom (void);
+
+    // Child widgets:
+
+    QTextDocument *m_buffer;
+
+    calc::qt_interpreter *m_interpreter;
+
+    int beg_mark;
+    int prompt_mark;
+  };
+
+  extern void readline_init (void);
+
+  extern void readline_fini (void);
+}
+
+#endif
--- a/gui-main.cpp	Thu May 23 11:26:44 2019 -0400
+++ b/gui-main.cpp	Thu May 23 12:36:26 2019 -0400
@@ -6,383 +6,19 @@
 #include <cstring>
 
 #include <QApplication>
-#include <QKeyEvent>
-#include <QTextDocument>
-#include <QTextEdit>
-#include <QTimer>
 
-#include "gui-main.h"
-#include "main.h"
-
+#include "command-window.h"
 #include "gui-main.h"
-#include "interpreter.h"
-#include "parser.h"
-
-#include <readline/readline.h>
-#include <readline/history.h>
-
-namespace gui
-{
-  static int available_char = 0;
-
-  static command_window *calc_interaction_window = 0;
-
-  static inline int ctrl (int c)
-  {
-    return c & 0x1f;
-  }
-
-  static int getc (FILE *)
-  {
-    int tmp = available_char;
-    available_char = 0;
-    return tmp;
-  }
-
-  static void redisplay (void)
-  {
-    if (calc_interaction_window)
-      emit calc_interaction_window->redisplay_signal ();
-  }
-
-  static void prep_term (int)
-  {
-  }
-
-  static void deprep_term (void)
-  {
-  }
-
-  static void accept_line (char *line)
-  {
-    if (calc_interaction_window)
-      calc_interaction_window->accept_line (line ? line : "");
-  }
-
-  static void display_completion_matches (char **matches, int num_matches,
-                                          int /* max_length */)
-  {
-    if (calc_interaction_window)
-      {
-        std::ostringstream buf;
-
-        if (num_matches > 1)
-          buf << "\n";
-
-        for (int i = 1; i < num_matches; i++)
-          buf << matches[i] << "\n";
-
-        calc_interaction_window->insert_at_end (buf.str ());
-
-        emit calc_interaction_window->redisplay_signal ();
-      }
-  }
-
-  static void readline_init (void)
-  {
-    rl_initialize ();
-
-    rl_getc_function = getc;
-    rl_redisplay_function = redisplay;
-    rl_prep_term_function = prep_term;
-    rl_deprep_term_function = deprep_term;
-    rl_completion_display_matches_hook = display_completion_matches;
-
-    rl_callback_handler_install (">> ", accept_line);
-  }
-
-  static void readline_fini (void)
-  {
-    rl_callback_handler_remove ();
-  }
-
-  command_window::command_window (QWidget *p)
-    : QTextEdit (p),
-      m_buffer (new QTextDocument ()),
-      m_interpreter (new calc::qt_interpreter ()),
-      beg_mark (), prompt_mark ()
-  {
-    setWindowTitle ("Qt::TextEdit example");
-
-    setMinimumSize (QSize (600, 400));
-
-    setDocument (m_buffer);
-
-    connect (m_interpreter, SIGNAL (result_ready (double)),
-             this, SLOT (handle_result (double)));
-
-    connect (m_interpreter, SIGNAL (error_signal (const QString&)),
-             this, SLOT (handle_error (const QString&)));
-
-    connect (this, SIGNAL (input_char_available (int)),
-             this, SLOT (handle_input_char (int)));
-
-    connect (this, SIGNAL (redisplay_signal (void)),
-             this, SLOT (redisplay (void)));
-
-    connect (this, SIGNAL (accept_line_signal (const QString&)),
-             m_interpreter, SLOT (accept_input_line (const QString&)));
-
-    insert_at_end
-      ("Qt Example Calculator.\n"
-       "Available operations: + - * / ^ ()\n"
-       "Semicolon terminates statement.\n"
-       "Up Arrow key moves to previous line in the command history.\n"
-       "Down Arrow key moves to next line in the comand history.\n\n");
-
-    beg_mark = set_mark ();
-
-    // Defer initializing and executing the interpreter until after the main
-    // window and QApplication are running to prevent race conditions
-    QTimer::singleShot (0, m_interpreter, SLOT (execute (void)));
-  }
-
-  // Accept an input line, parse and possibly execute it.
-
-  void command_window::accept_line (const std::string& line)
-  {
-    if (calc::debug_mode)
-      std::cerr << "accept: " << line << std::endl;
-
-    insert_at_end ("\n");
-
-    if (! line.empty ())
-      {
-        add_history (line.c_str ());
-        using_history ();
-
-        emit accept_line_signal (QString::fromStdString (line));
-      }
-  }
-
-  void command_window::insert_at_end (const std::string& text)
-  {
-    scroll_to_bottom ();
-
-    insert_at_cursor (text);
-  }
-
-  void command_window::handle_error (const QString& msg)
-  {
-    insert_at_end ("parse error: " + msg.toStdString () + "\n");
-
-    rl_abort (0, 0);
-  }
-
-  // FIXME: do we really need this extra function?
-  void command_window::handle_result (double value)
-  {
-    insert_result (value);
-  }
-
-  // Redisplay current command line.
-
-  void command_window::redisplay (void)
-  {
-    erase_line ();
-
-    std::string line = rl_line_buffer ? rl_line_buffer : "";
-    std::string prompt = (rl_prompt && parser::beg_of_stmt) ? rl_prompt : "";
-
-    insert_line (prompt, line);
-
-    scroll_to_bottom ();
-
-    QTextCursor cursor = textCursor ();
-
-    cursor.setPosition (prompt_mark + rl_point, QTextCursor::MoveAnchor);
 
-    setTextCursor (cursor);
-  }
-
-  void command_window::keyPressEvent (QKeyEvent *event)
-  {
-    if (! event)
-      return;
-
-    if (event->type () == QEvent::KeyPress)
-      {
-        int key = event->key ();
-
-        switch (key)
-          {
-          case Qt::Key_Return:
-            key = 0x0A;
-            break;
-
-          case Qt::Key_Backspace:
-            key = 0x08;
-            break;
-
-          case Qt::Key_Tab:
-            key = 0x09;
-            break;
-
-          case Qt::Key_Escape:
-            key = 0x1b;
-            break;
-
-          case Qt::Key_Up:
-          case Qt::Key_Down:
-          case Qt::Key_Right:
-          case Qt::Key_Left:
-            key = do_arrow_key (key);
-            break;
-
-          default:
-            {
-              switch (event->modifiers ())
-                {
-                case Qt::ControlModifier:
-                  if (key > 0x3f && key < 0x7b)
-                    key &= 0x1f;
-                  else
-                    key = -1;
-                  break;
-
-                default:
-                  {
-                    // Don't shoot me, this is just a demo...
-                    QString text = event->text ();
-                    QByteArray latin_text = text.toLatin1 ();
-                    key = latin_text[0];
-                  }
-                  break;
-                }
-            }
-            break;
-          }
-
-        if (key >= 0)
-          emit input_char_available (key);
-      }
-  }
-
-  void command_window::handle_input_char (int key)
-  {
-    available_char = key;
-    rl_callback_read_char ();
-  }
-
-  int command_window::do_arrow_key (int arrow_key)
-  {
-    int retval = 0;
-
-    available_char = 0x1b;
-    rl_callback_read_char ();
-
-    available_char = '[';
-    rl_callback_read_char ();
-
-    switch (arrow_key)
-      {
-      case Qt::Key_Up:
-        retval = 'A';
-        break;
-
-      case Qt::Key_Down:
-        retval = 'B';
-        break;
-
-      case Qt::Key_Right:
-        retval = 'C';
-        break;
-
-      case Qt::Key_Left:
-        retval = 'D';
-        break;
-      }
-
-    return retval;
-  }
-
-  // Retrieve a command from the history list and insert it on the
-  // current command.
-
-  void command_window::history (bool up)
-  {
-    HIST_ENTRY *entry = up ? previous_history () : next_history ();
-
-    if (entry)
-      {
-        erase_line ();
-
-        std::string prompt = rl_prompt ? rl_prompt : "";
-
-        insert_line (prompt, entry->line);
-      }
-    else if (! up)
-      erase_line ();
-
-    scroll_to_bottom ();
-  }
-
-  void command_window::erase_line (void)
-  {
-    QTextCursor cursor = textCursor ();
-
-    cursor.movePosition (QTextCursor::End);
-    cursor.select (QTextCursor::LineUnderCursor);
-    cursor.removeSelectedText ();
-
-    setTextCursor (cursor);
-  }
-
-  void command_window::insert_at_cursor (const std::string& text)
-  {
-    QTextCursor cursor = textCursor ();
-
-    cursor.insertText (QString::fromStdString (text));
-
-    setTextCursor (cursor);
-  }
-
-  void command_window::insert_line (const std::string& prompt,
-                                    const std::string& line)
-  {
-    beg_mark = set_mark ();
-
-    insert_at_cursor (prompt);
-
-    prompt_mark = set_mark ();
-
-    insert_at_cursor (line);
-  }
-
-  int command_window::set_mark (void)
-  {
-    return textCursor ().position ();
-  }
-
-  void command_window::insert_result (double value)
-  {
-    std::ostringstream buf;
-
-    buf << "ans = " << value << "\n";
-
-    insert_at_cursor (buf.str ());
-
-    beg_mark = set_mark ();
-
-    scroll_to_bottom ();
-  }
-
-  void command_window::scroll_to_bottom (void)
-  {
-    QTextCursor cursor = textCursor ();
-
-    cursor.movePosition (QTextCursor::End);
-
-    setTextCursor (cursor);
-  }
-
-  int main (int argc, char *argv[])
+namespace calc
+{
+  int gui_main (int argc, char *argv[])
   {
     QApplication app (argc, argv);
 
-    calc_interaction_window = new command_window ();
+    command_window command_window;
 
-    calc_interaction_window->show ();
+    command_window.show ();
 
     readline_init ();
 
@@ -393,69 +29,3 @@
     return status;
   }
 }
-
-// -- Variable: rl_getc_func_t * rl_getc_function
-//     If non-zero, Readline will call indirectly through this pointer to
-//     get a character from the input stream.  By default, it is set to
-//     `rl_getc', the default Readline character input function (*note
-//     Character Input::).
-
-// -- Variable: rl_voidfunc_t * rl_redisplay_function
-//     If non-zero, Readline will call indirectly through this pointer to
-//     update the display with the current contents of the editing buffer.
-//     By default, it is set to `rl_redisplay', the default Readline
-//     redisplay function (*note Redisplay::).
-
-// -- Variable: rl_vintfunc_t * rl_prep_term_function
-//     If non-zero, Readline will call indirectly through this pointer to
-//     initialize the terminal.  The function takes a single argument, an
-//     `int' flag that says whether or not to use eight-bit characters.
-//     By default, this is set to `rl_prep_terminal' (*note Terminal
-//     Management::).
-
-// -- Variable: rl_voidfunc_t * rl_deprep_term_function
-//     If non-zero, Readline will call indirectly through this pointer to
-//     reset the terminal.  This function should undo the effects of
-//     `rl_prep_term_function'.  By default, this is set to
-//     `rl_deprep_terminal' (*note Terminal Management::).
-
-// -- Function: void rl_callback_handler_install (const char *prompt,
-//          rl_vcpfunc_t *lhandler)
-//     Set up the terminal for readline I/O and display the initial
-//     expanded value of PROMPT.  Save the value of LHANDLER to use as a
-//     function to call when a complete line of input has been entered.
-//     The function takes the text of the line as an argument.
-
-// -- Function: void rl_callback_read_char (void)
-//     Whenever an application determines that keyboard input is
-//     available, it should call `rl_callback_read_char()', which will
-//     read the next character from the current input source.  If that
-//     character completes the line, `rl_callback_read_char' will invoke
-//     the LHANDLER function saved by `rl_callback_handler_install' to
-//     process the line.  Before calling the LHANDLER function, the
-//     terminal settings are reset to the values they had before calling
-//     `rl_callback_handler_install'.  If the LHANDLER function returns,
-//     the terminal settings are modified for Readline's use again.
-//     `EOF' is  indicated by calling LHANDLER with a `NULL' line.
-
-// -- Function: void rl_callback_handler_remove (void)
-//     Restore the terminal to its initial state and remove the line
-//     handler.  This may be called from within a callback as well as
-//     independently.  If the LHANDLER installed by
-//     `rl_callback_handler_install' does not exit the program, either
-//     this function or the function referred to by the value of
-//     `rl_deprep_term_function' should be called before the program
-//     exits to reset the terminal settings.
-
-// -- Variable: rl_compdisp_func_t * rl_completion_display_matches_hook
-//     If non-zero, then this is the address of a function to call when
-//     completing a word would normally display the list of possible
-//     matches.  This function is called in lieu of Readline displaying
-//     the list.  It takes three arguments: (`char **'MATCHES, `int'
-//     NUM_MATCHES, `int' MAX_LENGTH) where MATCHES is the array of
-//     matching strings, NUM_MATCHES is the number of strings in that
-//     array, and MAX_LENGTH is the length of the longest string in that
-//     array.  Readline provides a convenience function,
-//     `rl_display_match_list', that takes care of doing the display to
-//     Readline's output stream.  That function may be called from this
-//     hook.
--- a/gui-main.h	Thu May 23 11:26:44 2019 -0400
+++ b/gui-main.h	Thu May 23 12:36:26 2019 -0400
@@ -1,89 +1,9 @@
 #if ! defined (calc_gui_main_h)
 #define calc_gui_main_h 1
 
-#include <string>
-
-#include <QTextDocument>
-#include <QTextEdit>
-
-#include "qt-interpreter.h"
-
-namespace gui
+namespace calc
 {
-  class command_window : public QTextEdit
-  {
-    Q_OBJECT
-
-  public:
-
-    command_window (QWidget *p = nullptr);
-
-    virtual ~command_window (void) = default;
-
-    // Accept an input line, parse and possibly execute it.
-
-    void accept_line (const std::string& line);
-
-    // Redisplay current command line.
-
-    void insert_at_end (const std::string& text);
-
-    void emit_error (const std::string& msg);
-
-    void emit_result (double value);
-
-  signals:
-
-    void input_char_available (int key);
-
-    void accept_line_signal (const QString& line);
-
-    void redisplay_signal (void);
-
-  public slots:
-
-    void handle_input_char (int key);
-
-    void handle_error (const QString& msg);
-
-    void handle_result (double value);
-
-    void redisplay (void);
-
-  protected:
-
-    void keyPressEvent (QKeyEvent *event);
-
-    int do_arrow_key (int arrow_key);
-
-    // Retrieve a command from the history list and insert it on the
-    // current command.
-
-    void history (bool up);
-
-    void erase_line (void);
-
-    void insert_at_cursor (const std::string& text);
-
-    void insert_line (const std::string& prompt, const std::string& line);
-
-    int set_mark (void);
-
-    void insert_result (double value);
-
-    void scroll_to_bottom (void);
-
-    // Child widgets:
-
-    QTextDocument *m_buffer;
-
-    calc::qt_interpreter *m_interpreter;
-
-    int beg_mark;
-    int prompt_mark;
-  };
-
-  extern int main (int argc, char **argv);
+  extern int gui_main (int argc, char **argv);
 }
 
 #endif
--- a/interpreter.cpp	Thu May 23 11:26:44 2019 -0400
+++ b/interpreter.cpp	Thu May 23 12:36:26 2019 -0400
@@ -6,7 +6,6 @@
 #include "interpreter.h"
 #include "parser.h"
 
-#include "main.h"
 #include "gui-main.h"
 #include "tty-main.h"
 
@@ -18,7 +17,7 @@
     : m_error_handler ([] (const char *msg)
                        { std::cerr << "error: " << msg << std::endl; }),
       m_result_handler ([] (double value)
-                        { std::cout << value << std::endl; })
+                        { std::cout << "ans = " << value << std::endl; })
   {
     if (the_interpreter)
       {
--- a/main.cpp	Thu May 23 11:26:44 2019 -0400
+++ b/main.cpp	Thu May 23 12:36:26 2019 -0400
@@ -5,15 +5,11 @@
 #include "gui-main.h"
 #include "tty-main.h"
 
-namespace calc
-{
-  bool tty_mode = false;
-  bool debug_mode = false;
-}
-
 int
 main (int argc, char **argv)
 {
+  bool tty_mode = false;
+
   int new_argc = 0;
 
   char **new_argv = new char* [argc + 1];
@@ -23,19 +19,14 @@
   for (int i = 1; i < argc; i++)
     {
       if (argv[i] == std::string ("--tty"))
-        calc::tty_mode = true;
-      else
-        new_argv[new_argc++] = argv[i];
-
-      if (argv[i] == std::string ("--debug"))
-        calc::debug_mode = true;
+        tty_mode = true;
       else
         new_argv[new_argc++] = argv[i];
     }
 
-  int status = calc::tty_mode
-    ? tty::main (new_argc, new_argv)
-    : gui::main (new_argc, new_argv);
+  int status = tty_mode
+    ? calc::tty_main (new_argc, new_argv)
+    : calc::gui_main (new_argc, new_argv);
 
   delete [] new_argv;
 
--- a/main.h	Thu May 23 11:26:44 2019 -0400
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,10 +0,0 @@
-#if ! defined (calc_main_h)
-#define calc_main_h 1
-
-namespace calc
-{
-  extern bool tty_mode;
-  extern bool debug_mode;
-}
-
-#endif
--- a/parser.yy	Thu May 23 11:26:44 2019 -0400
+++ b/parser.yy	Thu May 23 12:36:26 2019 -0400
@@ -12,9 +12,6 @@
 #include <cstdio>
 
 #include "interpreter.h"
-#include "main.h"
-#include "gui-main.h"
-#include "tty-main.h"
 #include "parser.h"
 
 namespace parser
@@ -27,8 +24,6 @@
   bool beg_of_stmt = true;
 
   static int yylex (void);
-
-  static void debug_trace (const char *);
 }
 
 static void yyerror (char const *);
@@ -54,7 +49,6 @@
         | input line
         | error
           {
-            parser::debug_trace ("ABORT");
             YYABORT;
           }
         ;
@@ -70,49 +64,41 @@
 
 exp     : NUM
           {
-            parser::debug_trace ("NUM");
             $$ = $1;
             parser::beg_of_stmt = false;
           }
         | exp '+' exp
           {
-            parser::debug_trace ("ADD");
             $$ = $1 + $3;
             parser::beg_of_stmt = false;
           }
         | exp '-' exp
           {
-            parser::debug_trace ("SUB");
             $$ = $1 - $3;
             parser::beg_of_stmt = false;
           }
         | exp '*' exp
           {
-            parser::debug_trace ("MUL");
             $$ = $1 * $3;
             parser::beg_of_stmt = false;
           }
         | exp '/' exp
           {
-            parser::debug_trace ("DIV");
             $$ = $1 / $3;
             parser::beg_of_stmt = false;
           }
         | '-' exp  %prec NEG
           {
-            parser::debug_trace ("NEG");
             $$ = -$2;
             parser::beg_of_stmt = false;
           }
         | exp '^' exp
           {
-            parser::debug_trace ("EXP");
             $$ = std::pow ($1, $3);
             parser::beg_of_stmt = false;
           }
         | '(' exp ')'
           {
-            parser::debug_trace ("PAREN");
             $$ = $2;
             parser::beg_of_stmt = false;
           }
@@ -190,12 +176,6 @@
 
     return -2;
   }
-
-  void debug_trace (const char *msg)
-  {
-    if (calc::debug_mode)
-      std::cerr << msg << std::endl;
-  }
 }
 
 static void yyerror (char const *msg)
--- a/tty-main.cpp	Thu May 23 11:26:44 2019 -0400
+++ b/tty-main.cpp	Thu May 23 12:36:26 2019 -0400
@@ -7,15 +7,14 @@
 #include <readline/readline.h>
 #include <readline/history.h>
 
-#include "gui-main.h"
 #include "tty-main.h"
 
 #include "interpreter.h"
 #include "parser.h"
 
-namespace tty
+namespace calc
 {
-  int main (int, char **)
+  int tty_main (int, char **)
   {
     std::cout
       << "Example Calculator.\n"
@@ -50,14 +49,4 @@
 
     return 0;
   }
-
-  void emit_result (double value)
-  {
-    std::cout << "ans = " << value << std::endl;
-  }
-
-  void emit_error (const char *msg)
-  {
-    std::cerr << "parse error: " << msg << std::endl;
-  }
 }
--- a/tty-main.h	Thu May 23 11:26:44 2019 -0400
+++ b/tty-main.h	Thu May 23 12:36:26 2019 -0400
@@ -1,12 +1,9 @@
 #if ! defined (calc_tty_main_h)
 #define calc_tty_main_h 1
 
-namespace tty
+namespace calc
 {
-  extern int main (int argc, char **argv);
-
-  extern void emit_result (double value);
-  extern void emit_error (const char *msg);
+  extern int tty_main (int argc, char **argv);
 }
 
 #endif