diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2015-06-29 16:46:19 +0200 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2015-06-29 17:10:03 +0200 |
commit | 95ebb1c7d7969fa642192ce8e6c9efa8249979d9 (patch) | |
tree | 8d768c2116b6ce6d013ff1517a38827eb3fe06b2 /src | |
parent | cbcda49236665b3721ee9c3c66aa4bad08d777b8 (diff) | |
download | sciteco-95ebb1c7d7969fa642192ce8e6c9efa8249979d9.tar.gz |
<:Q> returns -1 for non-existent registers now
* added a new OPTIONAL behaviour for QRegSpecMachines
* allows you to implement commands that have an optional Q-Register
argument that should not be initialized if undefined.
* Using QRegSpecMachine::fail() you may still check for existence of
the register conditionally to emulate the QREG_REQUIRED behaviour.
* Using :Q for checking for register existence makes sense, because
usually you will want to check for both existence and non-emptyness
as in :Qq">. So in this common case, you no longer have to
keep in mind that the register may also be undefined.
* This finally allows us to create arrays in the Q-Register
tables without keeping a separate entry for the number of elements.
E.g. an array.0 to array.N can be iterated like this:
0Ui <:Q[array.^E\i]:; ! work with element i ! %i>
Diffstat (limited to 'src')
-rw-r--r-- | src/cmdline.h | 2 | ||||
-rw-r--r-- | src/qregisters.cpp | 74 | ||||
-rw-r--r-- | src/qregisters.h | 47 | ||||
-rw-r--r-- | src/spawn.h | 2 |
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 ®); }; +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); |