aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--src/cmdline.cpp3
-rw-r--r--src/error.h7
-rw-r--r--src/expressions.cpp46
-rw-r--r--src/expressions.h88
-rw-r--r--src/parser.cpp291
-rw-r--r--src/parser.h20
-rw-r--r--src/qregisters.cpp4
-rw-r--r--src/search.cpp2
8 files changed, 311 insertions, 150 deletions
diff --git a/src/cmdline.cpp b/src/cmdline.cpp
index 34a5df3..271e2be 100644
--- a/src/cmdline.cpp
+++ b/src/cmdline.cpp
@@ -235,6 +235,8 @@ Cmdline::keypress(gchar key)
/*
* Return from top-level macro, results
* in command line termination.
+ * The return "arguments" are currently
+ * ignored.
*/
interface.popup_clear();
@@ -248,6 +250,7 @@ Cmdline::keypress(gchar key)
QRegisters::view.set_scintilla_undo(true);
Goto::table->clear();
expressions.clear();
+ loop_stack.clear();
last_cmdline = *this;
str = NULL;
diff --git a/src/error.h b/src/error.h
index a4f4660..f4dbfbf 100644
--- a/src/error.h
+++ b/src/error.h
@@ -39,7 +39,12 @@ class Quit {};
* Thrown as exception to cause a macro to
* return or a command-line termination.
*/
-class Return {};
+class Return {
+public:
+ guint args;
+
+ Return(guint _args = 0) : args(_args) {}
+};
class Error {
gchar *description;
diff --git a/src/expressions.cpp b/src/expressions.cpp
index a82914e..922c212 100644
--- a/src/expressions.cpp
+++ b/src/expressions.cpp
@@ -27,7 +27,9 @@
namespace SciTECO {
-Expressions expressions;
+Expressions expressions;
+Expressions::NumberStack Expressions::numbers;
+Expressions::OperatorStack Expressions::operators;
tecoInt
Expressions::push(tecoInt number)
@@ -42,7 +44,7 @@ Expressions::push(tecoInt number)
number *= -1;
}
- numbers.undo_pop();
+ NumberStack::undo_pop<numbers>();
return numbers.push(number);
}
@@ -56,7 +58,7 @@ Expressions::pop_num(guint index)
if (numbers.items()) {
n = numbers.pop(index);
- numbers.undo_push(n, index);
+ NumberStack::undo_push<numbers>(n, index);
}
return n;
@@ -83,7 +85,7 @@ Expressions::add_digit(gchar digit)
Expressions::Operator
Expressions::push(Expressions::Operator op)
{
- operators.undo_pop();
+ OperatorStack::undo_pop<operators>();
return operators.push(op);
}
@@ -107,7 +109,7 @@ Expressions::pop_op(guint index)
if (operators.items()) {
op = operators.pop(index);
- operators.undo_push(op, index);
+ OperatorStack::undo_push<operators>(op, index);
}
return op;
@@ -181,8 +183,6 @@ Expressions::eval(bool pop_brace)
break;
op = operators.peek(n);
- if (op == OP_LOOP)
- break;
if (op == OP_BRACE) {
if (pop_brace)
pop_op(n);
@@ -208,18 +208,6 @@ Expressions::args(void)
}
gint
-Expressions::find_op(Operator op)
-{
- guint items = operators.items();
-
- for (guint i = 0; i < items; i++)
- if (operators.peek(i) == op)
- return i;
-
- return -1; /* not found */
-}
-
-gint
Expressions::first_op(void)
{
guint items = operators.items();
@@ -245,6 +233,26 @@ Expressions::discard_args(void)
pop_num_calc();
}
+void
+Expressions::brace_return(guint keep_braces, guint args)
+{
+ tecoInt return_numbers[args];
+
+ for (guint i = args; i; i--)
+ return_numbers[i-1] = pop_num();
+
+ undo.push_var(brace_level);
+
+ while (brace_level > keep_braces) {
+ discard_args();
+ eval(true);
+ brace_level--;
+ }
+
+ for (guint i = 0; i < args; i++)
+ push(return_numbers[i]);
+}
+
const gchar *
Expressions::format(tecoInt number)
{
diff --git a/src/expressions.h b/src/expressions.h
index 216ec7a..cdb2d3e 100644
--- a/src/expressions.h
+++ b/src/expressions.h
@@ -27,41 +27,39 @@ namespace SciTECO {
template <typename Type>
class ValueStack {
- class UndoTokenPush : public UndoTokenWithSize<UndoTokenPush> {
- /*
- * FIXME: saving the UndoStack for each undo taken
- * wastes a lot of memory
- */
- ValueStack<Type> *stack;
-
+ /*
+ * NOTE: Since value stacks are usually singleton,
+ * we pass them as a template parameter, saving space
+ * in the undo token.
+ */
+ template <ValueStack<Type> &stack>
+ class UndoTokenPush : public UndoTokenWithSize<UndoTokenPush<stack>> {
Type value;
guint index;
public:
- UndoTokenPush(ValueStack<Type> *_stack,
- Type _value, guint _index = 0)
- : stack(_stack), value(_value), index(_index) {}
+ UndoTokenPush(Type _value, guint _index = 0)
+ : value(_value), index(_index) {}
void
run(void)
{
- stack->push(value, index);
+ stack.push(value, index);
}
};
- class UndoTokenPop : public UndoTokenWithSize<UndoTokenPop> {
- ValueStack<Type> *stack;
-
+ template <ValueStack<Type> &stack>
+ class UndoTokenPop : public UndoTokenWithSize<UndoTokenPop<stack>> {
guint index;
public:
- UndoTokenPop(ValueStack<Type> *_stack, guint _index = 0)
- : stack(_stack), index(_index) {}
+ UndoTokenPop(guint _index = 0)
+ : index(_index) {}
void
run(void)
{
- stack->pop(index);
+ stack.pop(index);
}
};
@@ -108,10 +106,12 @@ public:
return sp[index] = value;
}
- inline void
+
+ template <ValueStack<Type> &stack>
+ static inline void
undo_push(Type value, guint index = 0)
{
- undo.push<UndoTokenPush>(this, value, index);
+ undo.push<UndoTokenPush<stack>>(value, index);
}
inline Type
@@ -129,10 +129,12 @@ public:
return v;
}
- inline void
+
+ template <ValueStack<Type> &stack>
+ static inline void
undo_pop(guint index = 0)
{
- undo.push<UndoTokenPop>(this, index);
+ undo.push<UndoTokenPop<stack>>(index);
}
inline Type &
@@ -143,10 +145,13 @@ public:
return sp[index];
}
+ /** Clear all but `keep_items` items. */
inline void
- clear(void)
+ clear(guint keep_items = 0)
{
- sp = stack_top;
+ g_assert(keep_items <= items());
+
+ sp = stack_top - keep_items;
}
};
@@ -170,7 +175,6 @@ public:
OP_NIL = 0x00,
OP_NEW,
OP_BRACE,
- OP_LOOP,
OP_NUMBER,
/*
* Real operators
@@ -194,11 +198,19 @@ private:
return op >> 4;
}
- ValueStack<tecoInt> numbers;
- ValueStack<Operator> operators;
+ /*
+ * Number and operator stacks are static, so
+ * they can be passed to the undo token constructors.
+ * This is OK since Expression is singleton.
+ */
+ typedef ValueStack<tecoInt> NumberStack;
+ static NumberStack numbers;
+
+ typedef ValueStack<Operator> OperatorStack;
+ static OperatorStack operators;
public:
- Expressions() : num_sign(1), radix(10) {}
+ Expressions() : num_sign(1), radix(10), brace_level(0) {}
gint num_sign;
inline void
@@ -258,13 +270,33 @@ public:
void discard_args(void);
- gint find_op(Operator op);
+ /** The nesting level of braces */
+ guint brace_level;
+
+ inline void
+ brace_open(void)
+ {
+ push(OP_BRACE);
+ undo.push_var(brace_level)++;
+ }
+
+ void brace_return(guint keep_braces, guint args = 0);
+
+ inline void
+ brace_close(void)
+ {
+ if (!brace_level)
+ throw Error("Missing opening brace");
+ undo.push_var(brace_level)--;
+ eval(true);
+ }
inline void
clear(void)
{
numbers.clear();
operators.clear();
+ brace_level = 0;
}
const gchar *format(tecoInt number);
diff --git a/src/parser.cpp b/src/parser.cpp
index 4cefb28..3541db1 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -21,6 +21,7 @@
#include <string.h>
#include <exception>
+#include <new>
#include <glib.h>
#include <glib/gprintf.h>
@@ -82,6 +83,18 @@ static gint nest_level = 0;
gchar *strings[2] = {NULL, NULL};
gchar escape_char = CTL_KEY_ESC;
+LoopStack loop_stack;
+
+/**
+ * Loop frame pointer: The number of elements on
+ * the loop stack when a macro invocation frame is
+ * created.
+ * This is used to perform checks for flow control
+ * commands to avoid jumping with invalid PCs while
+ * not creating a new stack per macro frame.
+ */
+static guint loop_stack_fp = 0;
+
/**
* Handles all expected exceptions, converting them to
* SciTECO::Error and preparing them for stack frame insertion.
@@ -144,6 +157,9 @@ Execute::macro(const gchar *macro, bool locals)
State *parent_state = States::current;
gint parent_pc = macro_pc;
+ guint parent_loop_fp = loop_stack_fp;
+
+ guint parent_brace_level = expressions.brace_level;
/*
* need this to fixup state on rubout: state machine emits undo token
@@ -152,18 +168,26 @@ Execute::macro(const gchar *macro, bool locals)
*/
undo.push_var(States::current) = &States::start;
macro_pc = 0;
+ loop_stack_fp = loop_stack.items();
Goto::table = &macro_goto_table;
- /* locals are allocated so that we do not waste call stack space */
if (locals) {
+ /*
+ * Locals are only allocated when needed to save
+ * space on the call stack and to improve the speed
+ * of local macro calls.
+ * However since the QRegisterTable object is rather
+ * small we can allocate it using alloca() on the stack.
+ */
parent_locals = QRegisters::locals;
- QRegisters::locals = new QRegisterTable(false);
+ QRegisters::locals = g_newa(QRegisterTable, 1);
+ new (QRegisters::locals) QRegisterTable(false);
}
try {
try {
step(macro, strlen(macro));
- } catch (Return) {
+ } catch (Return &info) {
/*
* Macro returned - handle like regular
* end of macro, even though some checks
@@ -174,6 +198,24 @@ Execute::macro(const gchar *macro, bool locals)
* with a trailing ^[ in macro.
*/
States::current = &States::start;
+
+ /*
+ * Discard all braces, except the current one.
+ */
+ expressions.brace_return(parent_brace_level, info.args);
+
+ /*
+ * Clean up the loop stack.
+ * We are allowed to return in loops.
+ * NOTE: This does not have to be undone.
+ */
+ loop_stack.clear(loop_stack_fp);
+ }
+
+ if (G_UNLIKELY(loop_stack.items() > loop_stack_fp)) {
+ Error error("Unterminated loop");
+ error.set_coord(macro, loop_stack.peek().pc);
+ throw error;
}
/*
@@ -182,7 +224,7 @@ Execute::macro(const gchar *macro, bool locals)
* via Error::set_coord()
*/
try {
- if (Goto::skip_label)
+ if (G_UNLIKELY(Goto::skip_label))
throw Error("Label \"%s\" not found",
Goto::skip_label);
@@ -195,7 +237,7 @@ Execute::macro(const gchar *macro, bool locals)
* cannot be used :-(.
*/
expressions.discard_args();
- } else if (States::current != &States::start) {
+ } else if (G_UNLIKELY(States::current != &States::start)) {
/*
* can only happen if we returned because
* of macro end
@@ -222,11 +264,13 @@ Execute::macro(const gchar *macro, bool locals)
Goto::skip_label = NULL;
if (locals) {
- delete QRegisters::locals;
+ /* memory is reclaimed on return */
+ QRegisters::locals->~QRegisterTable();
QRegisters::locals = parent_locals;
}
Goto::table = parent_goto_table;
+ loop_stack_fp = parent_loop_fp;
macro_pc = parent_pc;
States::current = parent_state;
@@ -234,11 +278,13 @@ Execute::macro(const gchar *macro, bool locals)
}
if (locals) {
- delete QRegisters::locals;
+ /* memory is reclaimed on return */
+ QRegisters::locals->~QRegisterTable();
QRegisters::locals = parent_locals;
}
Goto::table = parent_goto_table;
+ loop_stack_fp = parent_loop_fp;
macro_pc = parent_pc;
States::current = parent_state;
}
@@ -827,12 +873,12 @@ StateStart::custom(gchar chr)
expressions.push(-1);
expressions.push_calc(Expressions::OP_MUL);
}
- expressions.push(Expressions::OP_BRACE);
+ expressions.brace_open();
break;
case ')':
BEGIN_EXEC(this);
- expressions.eval(true);
+ expressions.brace_close();
break;
case ',':
@@ -912,55 +958,75 @@ StateStart::custom(gchar chr)
*/
case '<':
if (mode == MODE_PARSE_ONLY_LOOP) {
- undo.push_var<gint>(nest_level);
- nest_level++;
- return this;
- }
- BEGIN_EXEC(this);
+ undo.push_var(nest_level)++;
+ } else {
+ LoopContext ctx;
- expressions.eval();
- if (!expressions.args())
- /* infinite loop */
- expressions.push(-1);
+ BEGIN_EXEC(this);
- if (!expressions.peek_num()) {
- expressions.pop_num();
+ expressions.eval();
+ ctx.pass_through = eval_colon();
+ ctx.counter = expressions.pop_num_calc(0, -1);
+ if (ctx.counter) {
+ /*
+ * Non-colon modified, we add implicit
+ * braces, so loop body won't see parameters.
+ * Colon modified, loop starts can be used
+ * to process stack elements which is symmetric
+ * to ":>".
+ */
+ if (!ctx.pass_through)
+ expressions.brace_open();
- /* skip to end of loop */
- undo.push_var<Mode>(mode);
- mode = MODE_PARSE_ONLY_LOOP;
- } else {
- expressions.push(macro_pc);
- expressions.push(Expressions::OP_LOOP);
+ ctx.pc = macro_pc;
+ loop_stack.push(ctx);
+ LoopStack::undo_pop<loop_stack>();
+ } else {
+ /* skip to end of loop */
+ undo.push_var(mode) = MODE_PARSE_ONLY_LOOP;
+ }
}
break;
case '>':
if (mode == MODE_PARSE_ONLY_LOOP) {
- if (!nest_level) {
- undo.push_var<Mode>(mode);
- mode = MODE_NORMAL;
- } else {
- undo.push_var<gint>(nest_level);
- nest_level--;
- }
+ if (!nest_level)
+ undo.push_var(mode) = MODE_NORMAL;
+ else
+ undo.push_var(nest_level)--;
} else {
BEGIN_EXEC(this);
- tecoInt loop_pc, loop_cnt;
- expressions.discard_args();
- if (expressions.pop_op() != Expressions::OP_LOOP)
+ if (loop_stack.items() <= loop_stack_fp)
throw Error("Loop end without corresponding "
"loop start command");
- loop_pc = expressions.pop_num();
- loop_cnt = expressions.pop_num();
+ LoopContext &ctx = loop_stack.peek();
+ bool colon_modified = eval_colon();
- if (loop_cnt != 1) {
+ /*
+ * Colon-modified loop ends can be used to
+ * aggregate values on the stack.
+ * A non-colon modified ">" behaves like ":>"
+ * for pass-through loop starts, though.
+ */
+ if (!ctx.pass_through) {
+ if (colon_modified) {
+ expressions.eval();
+ expressions.push(Expressions::OP_NEW);
+ } else {
+ expressions.discard_args();
+ }
+ }
+
+ if (ctx.counter == 1) {
+ /* this was the last loop iteration */
+ if (!ctx.pass_through)
+ expressions.brace_close();
+ LoopStack::undo_push<loop_stack>(loop_stack.pop());
+ } else {
/* repeat loop */
- macro_pc = loop_pc;
- expressions.push(MAX(loop_cnt - 1, -1));
- expressions.push(loop_pc);
- expressions.push(Expressions::OP_LOOP);
+ macro_pc = ctx.pc;
+ ctx.counter = MAX(ctx.counter - 1, -1);
}
}
break;
@@ -981,34 +1047,32 @@ StateStart::custom(gchar chr)
* without colon-modifying the search command (or at a
* later point).
*
- * Executing \(lq;\(rq outside of iterations yields an
- * error.
+ * Executing \(lq;\(rq outside of iterations in the current
+ * macro invocation level yields an error. It is thus not
+ * possible to let a macro break a caller's loop.
*/
case ';':
BEGIN_EXEC(this);
+ if (loop_stack.items() <= loop_stack_fp)
+ throw Error("<;> only allowed in iterations");
+
v = QRegisters::globals["_"]->get_integer();
rc = expressions.pop_num_calc(0, v);
if (eval_colon())
rc = ~rc;
if (IS_FAILURE(rc)) {
+ LoopContext ctx = loop_stack.pop();
+
expressions.discard_args();
- /*
- * FIXME: it would be better accroding to the
- * TECO standard to throw an error
- * always when we're not in a loop.
- * But this is not easy to find out without
- * modifying the expression stack.
- */
- if (expressions.pop_op() != Expressions::OP_LOOP)
- throw Error("<;> only allowed in iterations");
- expressions.pop_num(); /* pc */
- expressions.pop_num(); /* counter */
+ if (!ctx.pass_through)
+ expressions.brace_close();
+
+ LoopStack::undo_push<loop_stack>(ctx);
/* skip to end of loop */
- undo.push_var<Mode>(mode);
- mode = MODE_PARSE_ONLY_LOOP;
+ undo.push_var(mode) = MODE_PARSE_ONLY_LOOP;
}
break;
@@ -1556,60 +1620,79 @@ StateFCommand::custom(gchar chr)
* loop flow control
*/
/*$
- * F< -- Go to loop start
+ * F< -- Go to loop start or jump to beginning of macro
*
* Immediately jumps to the current loop's start.
* Also works from inside conditionals.
+ *
+ * Outside of loops \(em or in a macro without
+ * a loop \(em this jumps to the beginning of the macro.
*/
case '<':
BEGIN_EXEC(&States::start);
/* FIXME: what if in brackets? */
expressions.discard_args();
- if (expressions.peek_op() == Expressions::OP_LOOP)
- /* repeat loop */
- macro_pc = expressions.peek_num();
- else
- macro_pc = -1;
+
+ macro_pc = loop_stack.items() > loop_stack_fp
+ ? loop_stack.peek().pc : -1;
break;
/*$
* F> -- Go to loop end
+ * :F>
*
* Jumps to the current loop's end.
- * If the loop has a counter or runs idefinitely, the jump
- * is performed immediately.
- * If the loop has reached its last iteration, parsing
- * until the loop end command has been found is performed.
+ * If the loop has remaining iterations or runs indefinitely,
+ * the jump is performed immediately just as if \(lq>\(rq
+ * had been executed.
+ * If the loop has reached its last iteration, \*(ST will
+ * parse until the loop end command has been found and control
+ * resumes after the end of the loop.
*
* In interactive mode, if the loop is incomplete and must
* be exited, you can type in the loop's remaining commands
* without them being executed (but they are parsed).
*
- * Calling \fBF\>\fP outside of a loop will throw an
- * error.
+ * When colon-modified, \fB:F>\fP behaves like \fB:>\fP
+ * and allows numbers to be aggregated on the stack.
+ *
+ * Calling \fBF>\fP outside of a loop at the current
+ * macro invocation level will throw an error.
+ */
+ /*
+ * NOTE: This is almost identical to the normal
+ * loop end since we don't really want to or need to
+ * parse till the end of the loop.
*/
case '>': {
- tecoInt loop_pc, loop_cnt;
-
BEGIN_EXEC(&States::start);
- /* FIXME: what if in brackets? */
- expressions.discard_args();
- if (expressions.pop_op() != Expressions::OP_LOOP)
+
+ if (loop_stack.items() <= loop_stack_fp)
throw Error("Jump to loop end without corresponding "
"loop start command");
- loop_pc = expressions.pop_num();
- loop_cnt = expressions.pop_num();
+ LoopContext &ctx = loop_stack.peek();
+ bool colon_modified = eval_colon();
- if (loop_cnt != 1) {
- /* repeat loop */
- macro_pc = loop_pc;
- expressions.push(MAX(loop_cnt - 1, -1));
- expressions.push(loop_pc);
- expressions.push(Expressions::OP_LOOP);
- } else {
+ if (!ctx.pass_through) {
+ if (colon_modified) {
+ expressions.eval();
+ expressions.push(Expressions::OP_NEW);
+ } else {
+ expressions.discard_args();
+ }
+ }
+
+ if (ctx.counter == 1) {
+ /* this was the last loop iteration */
+ if (!ctx.pass_through)
+ expressions.brace_close();
+ LoopStack::undo_push<loop_stack>(loop_stack.pop());
/* skip to end of loop */
- undo.push_var<Mode>(mode);
- mode = MODE_PARSE_ONLY_LOOP;
+ undo.push_var(mode) = MODE_PARSE_ONLY_LOOP;
+ } else {
+ /* repeat loop */
+ macro_pc = ctx.pc;
+ ctx.counter = MAX(ctx.counter - 1, -1);
}
break;
}
@@ -1956,13 +2039,19 @@ StateEscape::custom(gchar chr)
* [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
+ * This will pass control to the calling macro immediately
+ * and is thus faster than letting control reach the macro's end.
+ * Also, direct arguments to \fB$$\fP will be left on the expression
+ * stack when the macro returns.
+ * \fB$$\fP closes loops automatically and is thus safe to call
+ * from loop bodies.
+ * Furthermore, it has defined semantics when executed
+ * from within braced expressions:
+ * All braces opened in the current macro invocation will
+ * be closed and their values discarded.
+ * Only the direct arguments to \fB$$\fP will be kept.
+ *
+ * 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
@@ -1971,6 +2060,9 @@ StateEscape::custom(gchar chr)
* 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.
+ * The arguments to \fB$$\fP are currently not used
+ * when terminating a command line \(em the new command line
+ * will always start with a clean expression stack.
*
* Only the first \fIescape\fP of \fB$$\fP may be typed
* in up-arrow mode as \fB^[$\fP \(em the second character
@@ -1978,15 +2070,16 @@ StateEscape::custom(gchar chr)
*/
if (chr == CTL_KEY_ESC) {
BEGIN_EXEC(&States::start);
- throw Return();
+ expressions.eval();
+ throw Return(expressions.args());
}
/*
* Alternatives: ^[, <CTRL/[>, <ESC>
*/
/*$
- * ^[ -- Discard all arguments
- * $
+ * $ -- Discard all arguments
+ * ^[
*
* Pops and discards all values from the stack that
* might otherwise be used as arguments to following
@@ -1997,9 +2090,9 @@ StateEscape::custom(gchar chr)
* 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.
+ * The up-arrow notation however is processed like any
+ * ordinary command and only works at the begining of
+ * a command.
*/
if (mode == MODE_NORMAL)
expressions.discard_args();
diff --git a/src/parser.h b/src/parser.h
index fe8f08b..b502f46 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -25,6 +25,7 @@
#include "sciteco.h"
#include "undo.h"
#include "error.h"
+#include "expressions.h"
namespace SciTECO {
@@ -373,6 +374,25 @@ extern gint macro_pc;
extern gchar *strings[2];
extern gchar escape_char;
+struct LoopContext {
+ /** how many iterations are left */
+ tecoInt counter;
+ /** Program counter of loop start command */
+ guint pc : sizeof(guint)*8 - 1;
+ /**
+ * Whether the loop represents an argument
+ * barrier or not (it "passes through"
+ * stack arguments).
+ *
+ * Since the program counter is usually
+ * a signed integer, it's ok steal one
+ * bit for the pass_through flag.
+ */
+ bool pass_through : 1;
+};
+typedef ValueStack<LoopContext> LoopStack;
+extern LoopStack loop_stack;
+
namespace Execute {
void step(const gchar *macro, gint stop_pos);
void macro(const gchar *macro, bool locals = true);
diff --git a/src/qregisters.cpp b/src/qregisters.cpp
index 6be0469..195521c 100644
--- a/src/qregisters.cpp
+++ b/src/qregisters.cpp
@@ -771,11 +771,11 @@ QRegisters::hook(Hook type)
* So this effectively executes:
* (typeM[ED]^[)
*/
- expressions.push(Expressions::OP_BRACE);
+ expressions.brace_open();
expressions.push(type);
reg->execute();
expressions.discard_args();
- expressions.eval(true);
+ expressions.brace_close();
} catch (Error &error) {
const gchar *type_str = type2name[type-1];
diff --git a/src/search.cpp b/src/search.cpp
index 3a493ff..d5b314b 100644
--- a/src/search.cpp
+++ b/src/search.cpp
@@ -642,7 +642,7 @@ StateSearch::done(const gchar *str)
if (eval_colon())
expressions.push(search_reg->get_integer());
else if (IS_FAILURE(search_reg->get_integer()) &&
- expressions.find_op(Expressions::OP_LOOP) < 0 /* not in loop */)
+ !loop_stack.items() /* not in loop */)
interface.msg(InterfaceCurrent::MSG_ERROR, "Search string not found!");
return &States::start;