diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2016-02-11 13:09:39 +0100 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2016-02-11 13:09:39 +0100 |
commit | b259cb68aa3f76d1e23b47be67d3fbeb6f6b62df (patch) | |
tree | 87fa2e003daa0f584d996ef28365b9b1bd39f5c0 | |
parent | 723b75534632a99228a7266d7579c9d8b3f0cb77 (diff) | |
download | sciteco-b259cb68aa3f76d1e23b47be67d3fbeb6f6b62df.tar.gz |
optimized command execution in batch mode, during macro calls, loops etc.
* SciTECO commands are implemented with immediate execution in mind.
Those commands that do need to execute immediately while a string
command is entered, can do so using StateExpectString::process().
For simplicity, the parser just assumed that every input character
should result in immediate execution (if the command supports it
of course).
* This lead to unnecessarily slow execution of commands like <I> or
<S> in batch mode. E.g. a search was always repeated for every
character of the pattern - a N character pattern could result in
N searches instead of one.
Also in interactive mode when executing a macro
or repeating commands in a loop, immediate processing of string arguments
is unnecessary and results in superfluous undo tokens.
* These cases are all optimized now by being informed about
the necessity of providing immediate feedback via State::refresh().
This is used by StateExpectString to defer calling process() as
long as possible.
* For states extending StateExpectString, there is no change since
they can already process arbitrarily long strings.
The optimization is hidden in StateExpectString.
* some allocations are now also avoided in StateExpectString::custom().
-rw-r--r-- | TODO | 6 | ||||
-rw-r--r-- | src/parser.cpp | 96 | ||||
-rw-r--r-- | src/parser.h | 15 |
3 files changed, 78 insertions, 39 deletions
@@ -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) {} |