aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/parser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser.cpp')
-rw-r--r--src/parser.cpp136
1 files changed, 106 insertions, 30 deletions
diff --git a/src/parser.cpp b/src/parser.cpp
index 4ae13d3..4cefb28 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -53,6 +53,7 @@ namespace States {
StateStart start;
StateControl control;
StateASCII ascii;
+ StateEscape escape;
StateFCommand fcommand;
StateChangeDir changedir;
StateCondCommand condcommand;
@@ -139,7 +140,7 @@ Execute::macro(const gchar *macro, bool locals)
GotoTable *parent_goto_table = Goto::table;
GotoTable macro_goto_table(false);
- QRegisterTable *parent_locals;
+ QRegisterTable *parent_locals = NULL;
State *parent_state = States::current;
gint parent_pc = macro_pc;
@@ -160,7 +161,20 @@ Execute::macro(const gchar *macro, bool locals)
}
try {
- step(macro, strlen(macro));
+ try {
+ step(macro, strlen(macro));
+ } catch (Return) {
+ /*
+ * Macro returned - handle like regular
+ * end of macro, even though some checks
+ * are unnecessary here.
+ * macro_pc will still point to the return PC.
+ * We are still in the "escape" state and must
+ * reset it here, so it is not confused
+ * with a trailing ^[ in macro.
+ */
+ States::current = &States::start;
+ }
/*
* Subsequent errors must still be
@@ -172,12 +186,22 @@ Execute::macro(const gchar *macro, bool locals)
throw Error("Label \"%s\" not found",
Goto::skip_label);
- if (States::current != &States::start)
+ if (States::current == &States::escape) {
+ /*
+ * Due to the deferred nature of ^[,
+ * it is valid to end in the "escape" state.
+ * FIXME: This could be avoided by signalling
+ * the state the end of macro but State::refresh()
+ * cannot be used :-(.
+ */
+ expressions.discard_args();
+ } else if (States::current != &States::start) {
/*
* can only happen if we returned because
* of macro end
*/
throw Error("Unterminated command");
+ }
/*
* This handles the problem of Q-Registers
@@ -190,7 +214,7 @@ Execute::macro(const gchar *macro, bool locals)
if (locals)
QRegisters::locals->clear();
} catch (Error &error) {
- error.set_coord(macro, strlen(macro));
+ error.set_coord(macro, macro_pc);
throw; /* forward */
}
} catch (...) {
@@ -726,7 +750,7 @@ StateStart::custom(gchar chr)
tecoBool rc;
/*
- * <CTRL/x> commands implemented in StateCtrlCmd
+ * <CTRL/x> commands implemented in StateControl
*/
if (IS_CTL(chr))
return States::control.get_next_state(CTL_ECHO(chr));
@@ -1801,6 +1825,7 @@ StateControl::StateControl() : State()
transitions['I'] = &States::insert_indent;
transitions['U'] = &States::ctlucommand;
transitions['^'] = &States::ascii;
+ transitions['['] = &States::escape;
}
State *
@@ -1841,31 +1866,6 @@ StateControl::custom(gchar chr)
break;
/*
- * Alternatives: ^[, <CTRL/[>, <ESC>
- */
- /*$
- * ^[ -- Discard all arguments
- * $
- *
- * Pops and discards all values from the stack that
- * might otherwise be used as arguments to following
- * commands.
- * Therefore it stops popping on stack boundaries like
- * they are introduced by arithmetic brackets or loops.
- *
- * Note that ^[ is usually typed using the Escape key.
- * CTRL+[ however is possible as well and equivalent to
- * Escape in every manner.
- * The Caret-[ notation however is processed like any
- * ordinary command and only works as the discard-arguments
- * command.
- */
- case '[':
- BEGIN_EXEC(&States::start);
- expressions.discard_args();
- break;
-
- /*
* Additional numeric operations
*/
/*$
@@ -1930,6 +1930,82 @@ StateASCII::custom(gchar chr)
return &States::start;
}
+/*
+ * The Escape state is special, as it implements
+ * a kind of "lookahead" for the ^[ command (dicard all
+ * arguments).
+ * It is not executed immediately as usual in SciTECO
+ * but only if not followed by an escape character.
+ * This is necessary since $$ is the macro return
+ * and command-line termination command and it must not
+ * discard arguments.
+ * Deferred execution of ^[ is possible since it does
+ * not have any visible side-effects - its effects can
+ * only be seen when executing the following command.
+ */
+StateEscape::StateEscape()
+{
+ transitions['\0'] = this;
+}
+
+State *
+StateEscape::custom(gchar chr)
+{
+ /*$
+ * [a1,a2,...]$$ -- Terminate command line or return from macro
+ * [a1,a2,...]^[$
+ *
+ * Returns from the current macro invocation.
+ * The numeric stack is not modified, giving the
+ * effect of returning the arguments or stack contents
+ * preceding the command to the macro caller.
+ * It is generally semantically equivalent to reaching
+ * the end of the current macro but is executed faster.
+ *
+ * Therefore returning from the top-level macro in batch mode
+ * will exit the program or start up interactive mode depending
+ * on whether program exit has been requested.
+ * \(lqEX\fB$$\fP\(rq is thus a common idiom to exit
+ * prematurely.
+ *
+ * In interactive mode, returning from the top-level macro
+ * (i.e. typing \fB$$\fP at the command line) has the
+ * effect of command line termination.
+ *
+ * Only the first \fIescape\fP of \fB$$\fP may be typed
+ * in up-arrow mode as \fB^[$\fP \(em the second character
+ * must be a real escape character.
+ */
+ if (chr == CTL_KEY_ESC) {
+ BEGIN_EXEC(&States::start);
+ throw Return();
+ }
+
+ /*
+ * Alternatives: ^[, <CTRL/[>, <ESC>
+ */
+ /*$
+ * ^[ -- Discard all arguments
+ * $
+ *
+ * Pops and discards all values from the stack that
+ * might otherwise be used as arguments to following
+ * commands.
+ * Therefore it stops popping on stack boundaries like
+ * they are introduced by arithmetic brackets or loops.
+ *
+ * Note that ^[ is usually typed using the Escape key.
+ * CTRL+[ however is possible as well and equivalent to
+ * Escape in every manner.
+ * The Caret-[ notation however is processed like any
+ * ordinary command and only works as the discard-arguments
+ * command.
+ */
+ if (mode == MODE_NORMAL)
+ expressions.discard_args();
+ return States::start.get_next_state(chr);
+}
+
StateECommand::StateECommand() : State()
{
transitions['\0'] = this;