aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2013-02-13 23:10:44 +0100
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2013-02-14 05:21:02 +0100
commit91ae87c7ab0c26a387eb42243e50baf0ded30250 (patch)
tree80029def56294772dec508d6c2f7e88096547572
parentcd132baf66da7ede169a9718e7473fa7bbb472ba (diff)
downloadsciteco-91ae87c7ab0c26a387eb42243e50baf0ded30250.tar.gz
rewritten string building state machine using a common MicroStateMachine base class
* uses label pointers instead of state-enum and switch case (both faster and shorter to write) * common interface for all micro state machines: makes them reusable
-rw-r--r--src/parser.cpp189
-rw-r--r--src/parser.h76
2 files changed, 140 insertions, 125 deletions
diff --git a/src/parser.cpp b/src/parser.cpp
index 15fcbdf..140a0a7 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -241,127 +241,124 @@ State::get_next_state(gchar chr) throw (Error, ReplaceCmdline)
}
gchar *
-StateExpectString::machine_input(gchar chr) throw (Error)
+StringBuildingMachine::input(gchar chr) throw (State::Error)
{
- switch (machine.mode) {
- case Machine::MODE_UPPER:
+ switch (mode) {
+ case MODE_UPPER:
chr = g_ascii_toupper(chr);
break;
- case Machine::MODE_LOWER:
+ case MODE_LOWER:
chr = g_ascii_tolower(chr);
break;
default:
break;
}
- if (machine.toctl) {
- chr = CTL_KEY(g_ascii_toupper(chr));
- machine.toctl = false;
+ if (toctl) {
+ if (chr != '^')
+ chr = CTL_KEY(g_ascii_toupper(chr));
+ undo.push_var(toctl) = false;
+ } else if (chr == '^') {
+ undo.push_var(toctl) = true;
+ return NULL;
}
- if (machine.state == Machine::STATE_ESCAPED) {
- machine.state = Machine::STATE_START;
- goto original;
- }
+ if (state)
+ goto *state;
- if (chr == '^') {
- machine.toctl = true;
- return NULL;
+ /* NULL state */
+ switch (chr) {
+ case CTL_KEY('Q'):
+ case CTL_KEY('R'): set(&&StateEscaped); break;
+ case CTL_KEY('V'): set(&&StateLower); break;
+ case CTL_KEY('W'): set(&&StateUpper); break;
+ case CTL_KEY('E'): set(&&StateCtlE); break;
+ default:
+ goto StateEscaped;
}
- switch (machine.state) {
- case Machine::STATE_START:
- switch (chr) {
- case CTL_KEY('Q'):
- case CTL_KEY('R'): machine.state = Machine::STATE_ESCAPED; break;
- case CTL_KEY('V'): machine.state = Machine::STATE_LOWER; break;
- case CTL_KEY('W'): machine.state = Machine::STATE_UPPER; break;
- case CTL_KEY('E'): machine.state = Machine::STATE_CTL_E; break;
- default:
- goto original;
- }
- break;
+ return NULL;
- case Machine::STATE_LOWER:
- machine.state = Machine::STATE_START;
- if (chr != CTL_KEY('V'))
- return g_strdup((gchar []){g_ascii_tolower(chr), '\0'});
- machine.mode = Machine::MODE_LOWER;
- break;
+StateLower:
+ set(NULL);
- case Machine::STATE_UPPER:
- machine.state = Machine::STATE_START;
- if (chr != CTL_KEY('W'))
- return g_strdup((gchar []){g_ascii_toupper(chr), '\0'});
- machine.mode = Machine::MODE_UPPER;
- break;
+ if (chr != CTL_KEY('V'))
+ return g_strdup(CHR2STR(g_ascii_tolower(chr)));
- case Machine::STATE_CTL_E:
- switch (g_ascii_toupper(chr)) {
- case 'Q': machine.state = Machine::STATE_CTL_EQ; break;
- case 'U': machine.state = Machine::STATE_CTL_EU; break;
- default:
- machine.state = Machine::STATE_START;
- return g_strdup((gchar []){CTL_KEY('E'), chr, '\0'});
- }
- break;
+ undo.push_var(mode) = MODE_LOWER;
+ return NULL;
- case Machine::STATE_CTL_EU:
- if (chr == '.') {
- machine.state = Machine::STATE_CTL_EU_LOCAL;
- } else {
- QRegister *reg;
+StateUpper:
+ set(NULL);
- machine.state = Machine::STATE_START;
- reg = QRegisters::globals[g_ascii_toupper(chr)];
- if (!reg)
- throw InvalidQRegError(chr);
- return g_strdup(CHR2STR(reg->get_integer()));
- }
- break;
+ if (chr != CTL_KEY('W'))
+ return g_strdup(CHR2STR(g_ascii_toupper(chr)));
+
+ undo.push_var(mode) = MODE_UPPER;
+ return NULL;
- case Machine::STATE_CTL_EU_LOCAL: {
+StateCtlE:
+ switch (g_ascii_toupper(chr)) {
+ case 'Q': set(&&StateCtlEQ); break;
+ case 'U': set(&&StateCtlEU); break;
+ default:
+ set(NULL);
+ return g_strdup((gchar []){CTL_KEY('E'), chr, '\0'});
+ }
+
+ return NULL;
+
+StateCtlEU:
+ if (chr == '.') {
+ set(&&StateCtlEULocal);
+ return NULL;
+ } else {
QRegister *reg;
- machine.state = Machine::STATE_START;
- reg = (*QRegisters::locals)[g_ascii_toupper(chr)];
+ reg = QRegisters::globals[g_ascii_toupper(chr)];
if (!reg)
- throw InvalidQRegError(chr, true);
+ throw State::InvalidQRegError(chr);
+ set(NULL);
return g_strdup(CHR2STR(reg->get_integer()));
}
- case Machine::STATE_CTL_EQ:
- if (chr == '.') {
- machine.state = Machine::STATE_CTL_EQ_LOCAL;
- } else {
- QRegister *reg;
+StateCtlEULocal: {
+ QRegister *reg;
- machine.state = Machine::STATE_START;
- reg = QRegisters::globals[g_ascii_toupper(chr)];
- if (!reg)
- throw InvalidQRegError(chr);
- return reg->get_string();
- }
- break;
+ reg = (*QRegisters::locals)[g_ascii_toupper(chr)];
+ if (!reg)
+ throw State::InvalidQRegError(chr, true);
+ set(NULL);
+ return g_strdup(CHR2STR(reg->get_integer()));
+}
- case Machine::STATE_CTL_EQ_LOCAL: {
+StateCtlEQ:
+ if (chr == '.') {
+ set(&&StateCtlEQLocal);
+ return NULL;
+ } else {
QRegister *reg;
- machine.state = Machine::STATE_START;
- reg = (*QRegisters::locals)[g_ascii_toupper(chr)];
+ reg = QRegisters::globals[g_ascii_toupper(chr)];
if (!reg)
- throw InvalidQRegError(chr, true);
+ throw State::InvalidQRegError(chr);
+ set(NULL);
return reg->get_string();
}
- default:
- g_assert(TRUE);
- }
+StateCtlEQLocal: {
+ QRegister *reg;
- return NULL;
+ reg = (*QRegisters::locals)[g_ascii_toupper(chr)];
+ if (!reg)
+ throw State::InvalidQRegError(chr, true);
+ set(NULL);
+ return reg->get_string();
+}
-original:
- return g_strdup((gchar []){chr, '\0'});
+StateEscaped:
+ set(NULL);
+ return g_strdup(CHR2STR(chr));
}
State *
@@ -393,17 +390,14 @@ StateExpectString::custom(gchar chr) throw (Error)
if (escape_char == '{') {
switch (chr) {
case '{':
- undo.push_var(nesting);
- nesting++;
+ undo.push_var(nesting)++;
break;
case '}':
- undo.push_var(nesting);
- nesting--;
+ undo.push_var(nesting)--;
break;
}
} else if (g_ascii_toupper(chr) == escape_char) {
- undo.push_var(nesting);
- nesting--;
+ undo.push_var(nesting)--;
}
if (!nesting) {
@@ -415,12 +409,8 @@ StateExpectString::custom(gchar chr) throw (Error)
undo.push_var(escape_char) = '\x1B';
nesting = 1;
- if (string_building) {
- undo.push_var(machine);
- machine.state = Machine::STATE_START;
- machine.mode = Machine::MODE_NORMAL;
- machine.toctl = false;
- }
+ if (string_building)
+ machine.reset();
next = done(string ? : "");
g_free(string);
@@ -433,12 +423,11 @@ StateExpectString::custom(gchar chr) throw (Error)
* String building characters
*/
if (string_building) {
- undo.push_var(machine);
- insert = machine_input(chr);
+ insert = machine.input(chr);
if (!insert)
return this;
} else {
- insert = g_strdup((gchar []){chr, '\0'});
+ insert = g_strdup(CHR2STR(chr));
}
/*
diff --git a/src/parser.h b/src/parser.h
index 13b99d4..26cd064 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -21,6 +21,7 @@
#include <glib.h>
#include <glib/gprintf.h>
+#include "undo.h"
#include "sciteco.h"
/* TECO uses only lower 7 bits for commands */
@@ -109,36 +110,62 @@ protected:
}
};
+template <typename Type>
+class MicroStateMachine {
+protected:
+ typedef void *MicroState;
+ MicroState state;
+
+public:
+ MicroStateMachine() : state(NULL) {}
+
+ inline void
+ set(MicroState next)
+ {
+ if (next != state)
+ undo.push_var(state) = next;
+ }
+
+ virtual inline void
+ reset(void)
+ {
+ set(NULL);
+ }
+
+ virtual Type input(gchar chr) throw (State::Error) = 0;
+};
+
+class StringBuildingMachine : public MicroStateMachine<gchar *> {
+ enum Mode {
+ MODE_NORMAL,
+ MODE_UPPER,
+ MODE_LOWER
+ } mode;
+
+ bool toctl;
+
+public:
+ StringBuildingMachine() : MicroStateMachine(),
+ mode(MODE_NORMAL), toctl(false) {}
+
+ void
+ reset(void)
+ {
+ MicroStateMachine::reset();
+ undo.push_var(mode) = MODE_NORMAL;
+ undo.push_var(toctl) = false;
+ }
+
+ gchar *input(gchar chr) throw (State::Error);
+};
+
/*
* Super-class for states accepting string arguments
* Opaquely cares about alternative-escape characters,
* string building commands and accumulation into a string
*/
class StateExpectString : public State {
- struct Machine {
- enum State {
- STATE_START,
- STATE_ESCAPED,
- STATE_LOWER,
- STATE_UPPER,
- STATE_CTL_E,
- STATE_CTL_EQ,
- STATE_CTL_EQ_LOCAL,
- STATE_CTL_EU,
- STATE_CTL_EU_LOCAL
- } state;
-
- enum Mode {
- MODE_NORMAL,
- MODE_UPPER,
- MODE_LOWER
- } mode;
-
- bool toctl;
-
- Machine() : state(STATE_START),
- mode(MODE_NORMAL), toctl(false) {}
- } machine;
+ StringBuildingMachine machine;
gint nesting;
@@ -151,7 +178,6 @@ public:
string_building(_building), last(_last) {}
private:
- gchar *machine_input(gchar key) throw (Error);
State *custom(gchar chr) throw (Error);
protected: