aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--TODO6
-rw-r--r--src/parser.cpp96
-rw-r--r--src/parser.h15
3 files changed, 78 insertions, 39 deletions
diff --git a/TODO b/TODO
index 46f4592..1349d4d 100644
--- a/TODO
+++ b/TODO
@@ -222,12 +222,6 @@ Optimizations:
* refactor search commands (create proper base class)
* refactor match-char state machine using MicroStateMachine class
* simplify parser (static jump tables are unnecessary!)
- * search and other commands can be significantly optimized:
- in batch mode, or in loops/macros there is no need to rematch every character!
- -> immediate vs. non-immediate command execution
- * can be handled automatically in StateExpectString
- * but it requires to be able to determine whether we're in the
- command line macro stack frame and are executing the end of it.
Documentation:
* Code docs (Doxygen). It's slowly getting better...
diff --git a/src/parser.cpp b/src/parser.cpp
index 5748c78..4ae13d3 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -90,31 +90,42 @@ gchar escape_char = CTL_KEY_ESC;
void
Execute::step(const gchar *macro, gint stop_pos)
{
- while (macro_pc < stop_pos) {
+ try {
+ /*
+ * convert bad_alloc and other C++ standard
+ * library exceptions
+ */
+ try {
+ while (macro_pc < stop_pos) {
#ifdef DEBUG
- g_printf("EXEC(%d): input='%c'/%x, state=%p, mode=%d\n",
- macro_pc, macro[macro_pc], macro[macro_pc],
- States::current, mode);
+ g_printf("EXEC(%d): input='%c'/%x, state=%p, mode=%d\n",
+ macro_pc, macro[macro_pc], macro[macro_pc],
+ States::current, mode);
#endif
- try {
- /*
- * convert bad_alloc and other C++ standard
- * library exceptions
- */
- try {
if (interface.is_interrupted())
throw Error("Interrupted");
State::input(macro[macro_pc]);
- } catch (std::exception &error) {
- throw StdError(error);
+ macro_pc++;
}
- } catch (Error &error) {
- error.set_coord(macro, macro_pc);
- throw; /* forward */
+
+ /*
+ * Provide interactive feedback when the
+ * PC is at the end of the command line.
+ * This will actually be called in other situations,
+ * like at the end of macros but that does not hurt.
+ * It should perhaps be in Cmdline::insert(),
+ * but doing it here ensures that exceptions get
+ * normalized.
+ */
+ States::current->refresh();
+ } catch (std::exception &error) {
+ throw StdError(error);
}
- macro_pc++;
+ } catch (Error &error) {
+ error.set_coord(macro, macro_pc);
+ throw; /* forward */
}
}
@@ -430,8 +441,6 @@ StringBuildingMachine::~StringBuildingMachine()
State *
StateExpectString::custom(gchar chr)
{
- gchar *insert;
-
if (chr == '\0') {
BEGIN_EXEC(this);
initial();
@@ -479,6 +488,15 @@ StateExpectString::custom(gchar chr)
machine.reset();
try {
+ /*
+ * Call process() even if interactive feedback
+ * has not been requested using refresh().
+ * This is necessary since commands are either
+ * written for interactive execution or not,
+ * so they may do their main activity in process().
+ */
+ if (insert_len)
+ process(string ? : "", insert_len);
next = done(string ? : "");
} catch (...) {
g_free(string);
@@ -486,38 +504,52 @@ StateExpectString::custom(gchar chr)
}
g_free(string);
+ insert_len = 0;
return next;
}
BEGIN_EXEC(this);
/*
- * String building characters
+ * String building characters and
+ * string argument accumulation.
+ *
+ * NOTE: As an optimization insert_len is not
+ * restored on undo since that is only
+ * necessary in interactive mode and we get
+ * called once per character when this is necessary.
+ * If this gets too confusing, just undo changes
+ * to insert_len.
*/
if (string_building) {
+ gchar *insert;
+
if (!machine.input(chr, insert))
return this;
- } else {
- insert = String::chrdup(chr);
- }
- /*
- * String accumulation
- */
- undo.push_str(strings[0]);
- String::append(strings[0], insert);
+ undo.push_str(strings[0]);
+ String::append(strings[0], insert);
+ insert_len += strlen(insert);
- try {
- process(strings[0], strlen(insert));
- } catch (...) {
g_free(insert);
- throw;
+ } else {
+ undo.push_str(strings[0]);
+ String::append(strings[0], chr);
+ insert_len++;
}
- g_free(insert);
return this;
}
+void
+StateExpectString::refresh(void)
+{
+ /* never calls process() in parse-only mode */
+ if (insert_len)
+ process(strings[0], insert_len);
+ insert_len = 0;
+}
+
State *
StateExpectFile::done(const gchar *str)
{
diff --git a/src/parser.h b/src/parser.h
index 1cd51c7..a1bfd75 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -54,9 +54,20 @@ public:
static void input(gchar chr);
State *get_next_state(gchar chr);
+ /**
+ * Provide interactive feedback.
+ *
+ * This gets called whenever a state with
+ * immediate interactive feedback should provide that
+ * feedback; allowing them to optimize batch mode,
+ * macro and many other cases.
+ */
+ virtual void refresh(void) {}
+
protected:
static bool eval_colon(void);
+ /** Get next state given an input character */
virtual State *
custom(gchar chr)
{
@@ -136,6 +147,7 @@ public:
*/
class StateExpectString : public State {
StringBuildingMachine machine;
+ gsize insert_len;
gint nesting;
@@ -144,11 +156,12 @@ class StateExpectString : public State {
public:
StateExpectString(bool _building = true, bool _last = true)
- : State(), nesting(1),
+ : insert_len(0), nesting(1),
string_building(_building), last(_last) {}
private:
State *custom(gchar chr);
+ void refresh(void);
protected:
virtual void initial(void) {}