aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/cmdline.h2
-rw-r--r--src/qregisters.cpp74
-rw-r--r--src/qregisters.h47
-rw-r--r--src/spawn.h2
4 files changed, 95 insertions, 30 deletions
diff --git a/src/cmdline.h b/src/cmdline.h
index 9a78605..05506fa 100644
--- a/src/cmdline.h
+++ b/src/cmdline.h
@@ -104,7 +104,7 @@ extern bool quit_requested;
class StateSaveCmdline : public StateExpectQReg {
public:
- StateSaveCmdline() : StateExpectQReg(true) {}
+ StateSaveCmdline() : StateExpectQReg(QREG_OPTIONAL_INIT) {}
private:
State *got_register(QRegister *reg);
diff --git a/src/qregisters.cpp b/src/qregisters.cpp
index a82e219..e47415b 100644
--- a/src/qregisters.cpp
+++ b/src/qregisters.cpp
@@ -849,6 +849,11 @@ StateString:
done:
if (mode > MODE_NORMAL) {
+ /*
+ * StateExpectQRegs with type != OPTIONAL
+ * will never see this NULL pointer beyond
+ * BEGIN_EXEC()
+ */
result = NULL;
return true;
}
@@ -856,12 +861,24 @@ done:
QRegisterTable &table = is_local ? *QRegisters::locals
: QRegisters::globals;
- result = table[name];
- if (!result) {
- if (!initialize)
- throw InvalidQRegError(name, is_local);
- result = table.insert(name);
- table.undo_remove(result);
+ switch (type) {
+ case QREG_REQUIRED:
+ result = table[name];
+ if (!result)
+ fail();
+ break;
+
+ case QREG_OPTIONAL:
+ result = table[name];
+ break;
+
+ case QREG_OPTIONAL_INIT:
+ result = table[name];
+ if (!result) {
+ result = table.insert(name);
+ table.undo_remove(result);
+ }
+ break;
}
return true;
@@ -871,7 +888,7 @@ done:
* Command states
*/
-StateExpectQReg::StateExpectQReg(bool initialize) : State(), machine(initialize)
+StateExpectQReg::StateExpectQReg(QRegSpecType type) : machine(type)
{
transitions['\0'] = this;
}
@@ -1000,9 +1017,9 @@ StateSaveQReg::got_file(const gchar *filename)
}
/*$
- * Qq -> n -- Query Q-Register integer or string
+ * Qq -> n -- Query Q-Register existence, its integer or string characters
* <position>Qq -> character
- * :Qq -> size
+ * :Qq -> -1 | size
*
* Without any arguments, get and return the integer-part of
* Q-Register <q>.
@@ -1012,13 +1029,26 @@ StateSaveQReg::got_file(const gchar *filename)
* Positions are handled like buffer positions \(em they
* begin at 0 up to the length of the string minus 1.
* An error is thrown for invalid positions.
+ * Both non-colon-modified forms of Q require register <q>
+ * to be defined and fail otherwise.
*
* When colon-modified, Q does not pop any arguments from
* the expression stack and returns the <size> of the string
- * in Q-Register <q>.
+ * in Q-Register <q> if register <q> exists (i.e. is defined).
* Naturally, for empty strings, 0 is returned.
- *
- * The command fails for undefined registers.
+ * When colon-modified and Q-Register <q> is undefined,
+ * -1 is returned instead.
+ * Therefore checking the return value \fB:Q\fP for values smaller
+ * 0 allows checking the existence of a register.
+ * Note that if <q> exists, its string part is not initialized,
+ * so \fB:Q\fP may be used to handle purely numeric data structures
+ * without creating Scintilla documents by accident.
+ * These semantics allow the useful idiom \(lq:Q\fIq\fP">\(rq for
+ * checking whether a Q-Register exists and has a non-empty string.
+ * Note also that the return value of \fB:Q\fP may be interpreted
+ * as a condition boolean that represents the non-existence of <q>.
+ * If <q> is undefined, it returns \fIsuccess\fP, else a \fIfailure\fP
+ * boolean.
*/
State *
StateQueryQReg::got_register(QRegister *reg)
@@ -1028,9 +1058,23 @@ StateQueryQReg::got_register(QRegister *reg)
expressions.eval();
if (eval_colon()) {
- /* Query Q-Register string size */
- expressions.push(reg->get_string_size());
- } else if (expressions.args() > 0) {
+ /* Query Q-Register's existence or string size */
+ expressions.push(reg ? reg->get_string_size()
+ : (tecoInt)-1);
+ return &States::start;
+ }
+
+ /*
+ * NOTE: This command is special since the QRegister is required
+ * without colon and otherwise optional.
+ * While it may be clearer to model this as two States,
+ * we cannot currently let parsing depend on the colon-modifier.
+ * That's why we have to delegate exception throwing to QRegSpecMachine.
+ */
+ if (!reg)
+ machine.fail();
+
+ if (expressions.args() > 0) {
/* Query character from Q-Register string */
gint c = reg->get_character(expressions.pop_num_calc());
if (c < 0)
diff --git a/src/qregisters.h b/src/qregisters.h
index f4f944d..1c55066 100644
--- a/src/qregisters.h
+++ b/src/qregisters.h
@@ -368,18 +368,30 @@ public:
bool pop(QRegister &reg);
};
+enum QRegSpecType {
+ /** Register must exist, else fail */
+ QREG_REQUIRED,
+ /**
+ * Return NULL if register does not exist.
+ * You can still call QRegSpecMachine::fail() to require it.
+ */
+ QREG_OPTIONAL,
+ /** Initialize register if it does not already exist */
+ QREG_OPTIONAL_INIT
+};
+
class QRegSpecMachine : public MicroStateMachine<QRegister *> {
StringBuildingMachine string_machine;
- bool initialize;
+ QRegSpecType type;
bool is_local;
gint nesting;
gchar *name;
public:
- QRegSpecMachine(bool _init = false)
+ QRegSpecMachine(QRegSpecType _type = QREG_REQUIRED)
: MicroStateMachine<QRegister *>(),
- initialize(_init),
+ type(_type),
is_local(false), nesting(0), name(NULL) {}
~QRegSpecMachine()
@@ -390,6 +402,12 @@ public:
void reset(void);
bool input(gchar chr, QRegister *&result);
+
+ inline void
+ fail(void) G_GNUC_NORETURN
+ {
+ throw InvalidQRegError(name, is_local);
+ }
};
/*
@@ -400,15 +418,15 @@ public:
* Super class for states accepting Q-Register specifications
*/
class StateExpectQReg : public State {
- QRegSpecMachine machine;
-
public:
- StateExpectQReg(bool initialize = false);
+ StateExpectQReg(QRegSpecType type = QREG_REQUIRED);
private:
State *custom(gchar chr);
protected:
+ QRegSpecMachine machine;
+
virtual State *got_register(QRegister *reg) = 0;
};
@@ -419,7 +437,7 @@ private:
class StatePopQReg : public StateExpectQReg {
public:
- StatePopQReg() : StateExpectQReg(true) {}
+ StatePopQReg() : StateExpectQReg(QREG_OPTIONAL_INIT) {}
private:
State *got_register(QRegister *reg);
@@ -427,7 +445,7 @@ private:
class StateEQCommand : public StateExpectQReg {
public:
- StateEQCommand() : StateExpectQReg(true) {}
+ StateEQCommand() : StateExpectQReg(QREG_OPTIONAL_INIT) {}
private:
State *got_register(QRegister *reg);
@@ -449,13 +467,16 @@ private:
};
class StateQueryQReg : public StateExpectQReg {
+public:
+ StateQueryQReg() : StateExpectQReg(QREG_OPTIONAL) {}
+
private:
State *got_register(QRegister *reg);
};
class StateCtlUCommand : public StateExpectQReg {
public:
- StateCtlUCommand() : StateExpectQReg(true) {}
+ StateCtlUCommand() : StateExpectQReg(QREG_OPTIONAL_INIT) {}
private:
State *got_register(QRegister *reg);
@@ -463,7 +484,7 @@ private:
class StateEUCommand : public StateExpectQReg {
public:
- StateEUCommand() : StateExpectQReg(true) {}
+ StateEUCommand() : StateExpectQReg(QREG_OPTIONAL_INIT) {}
private:
State *got_register(QRegister *reg);
@@ -488,7 +509,7 @@ private:
class StateSetQRegInteger : public StateExpectQReg {
public:
- StateSetQRegInteger() : StateExpectQReg(true) {}
+ StateSetQRegInteger() : StateExpectQReg(QREG_OPTIONAL_INIT) {}
private:
State *got_register(QRegister *reg);
@@ -496,7 +517,7 @@ private:
class StateIncreaseQReg : public StateExpectQReg {
public:
- StateIncreaseQReg() : StateExpectQReg(true) {}
+ StateIncreaseQReg() : StateExpectQReg(QREG_OPTIONAL_INIT) {}
private:
State *got_register(QRegister *reg);
@@ -514,7 +535,7 @@ private:
class StateCopyToQReg : public StateExpectQReg {
public:
- StateCopyToQReg() : StateExpectQReg(true) {}
+ StateCopyToQReg() : StateExpectQReg(QREG_OPTIONAL_INIT) {}
private:
State *got_register(QRegister *reg);
diff --git a/src/spawn.h b/src/spawn.h
index e65f18e..61c073f 100644
--- a/src/spawn.h
+++ b/src/spawn.h
@@ -57,7 +57,7 @@ private:
class StateEGCommand : public StateExpectQReg {
public:
- StateEGCommand() : StateExpectQReg(true) {}
+ StateEGCommand() : StateExpectQReg(QREG_OPTIONAL_INIT) {}
private:
State *got_register(QRegister *reg);