aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/expressions.h
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2016-02-15 14:00:32 +0100
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2016-02-15 15:00:55 +0100
commitde7394372c77b695d4779606ea7587dfe61a35de (patch)
treedb10983d46c0b4b3b5a7b67eeb457e58abb07fff /src/expressions.h
parentf08187e454f56954b41d95615ca2e370ba19667e (diff)
downloadsciteco-de7394372c77b695d4779606ea7587dfe61a35de.tar.gz
revised looping implementation, aggregating loops, sane $$ semantics, some optimizationa and additional checks
* undo tokens emitted by the expression stack no longer waste memory by pointing to the stack implementation. This uses some ugly C++ constant template arguments but saves 4 or 8 byte per undo token depending on the architecture. * Round braces are counted now and the return command $$ will use this information to discard all non-relevant brace levels. * It is an error to close a brace when none have been opened. * The bracing rules are still very liberal, allowing you to close braces in macros belonging to a higher call frame or leave them open at the end of a macro. While this is technically possible, it is perhaps a good idea to stricten these rules in some future release. * Loops no longer (ab)use the expression stack to store program counters and loop counters. This removes flow control from the responsibility of the expression stack which is much safer now since we can control where we jump to. This also eased implemented proper semantics for $$. * It is an error to leave loops open at the end of a macro or trying to close a loop opened in the caller of the macro. Similarily it is only possible to close a loop from the current invocation frame. This means it is now impossible to accidentally jump to invalid PCs. * Even though loop context stacks could be attached directly to the macro invocation frame, this would be inefficient. Instead there's a loop frame pointer now that is part of the invocation frame. All frames will reuse the same stack structure. * Loops are automatically discarded when returning using $$. * Special aggregating forms of the loop start (":<") and loop end (":>") commands are possible now and have been implemented. This improves SciTECO's capability as a stack-oriented language. It is no longer necessary to write recursive macros to generate stack values of arbitrary length dynamically or to process them. * All expression and loop stacks are still fixed-size. It may be a good idea to implement dynamic resizing (TODO). * Added some G_UNLIKELYs to Execute::macro(). Should improve the branch prediction of modern CPUs. * Local Q-Register tables are allocated on the stack now instead of on the heap (the bulk of a table is stored on the heap anyway). Should improve performance of macro invocations. * Document that "F<" will jump to the beginning of the macro if there is no loop. This is not in standard TECO, but I consider it a useful feature.
Diffstat (limited to 'src/expressions.h')
-rw-r--r--src/expressions.h88
1 files changed, 60 insertions, 28 deletions
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);