diff options
author | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2013-03-28 17:37:49 +0100 |
---|---|---|
committer | Robin Haberkorn <robin.haberkorn@googlemail.com> | 2014-02-15 15:21:52 +0100 |
commit | d374448af8ab690c810757f73ba44f208db96f30 (patch) | |
tree | 48e15c42d7a01b10aaef51d0113572ea53fec797 | |
parent | 6d4668bdaf393aa45d9adb640774f998c6b4aa58 (diff) | |
download | sciteco-d374448af8ab690c810757f73ba44f208db96f30.tar.gz |
added support for TECO stack tracing
* when an error is thrown, stack frames are collected on clean up, up to
the toplevel macro
* the toplevel macro decides how to display the error
* now errors in interactive and batch mode are displayed differently
* in batch mode, a backtrace is displayed as a sequence of messages
* Execute::file() forwards errors correctly
* the correct error in the file is displayed in interactive mode
* necessary to build the stack trace
-rw-r--r-- | src/cmdline.cpp | 5 | ||||
-rw-r--r-- | src/main.cpp | 37 | ||||
-rw-r--r-- | src/parser.cpp | 71 | ||||
-rw-r--r-- | src/parser.h | 83 | ||||
-rw-r--r-- | src/qregisters.cpp | 10 | ||||
-rw-r--r-- | src/qregisters.h | 2 |
6 files changed, 171 insertions, 37 deletions
diff --git a/src/cmdline.cpp b/src/cmdline.cpp index c6dcf2a..641b062 100644 --- a/src/cmdline.cpp +++ b/src/cmdline.cpp @@ -97,7 +97,10 @@ cmdline_keypress(gchar key) cmdline_pos = repl_pos = r.pos; macro_pc = r.pos-1; continue; - } catch (...) { + } catch (State::Error &error) { + error.add_frame(new State::Error::ToplevelFrame()); + error.display_short(); + if (old_cmdline) { undo.pop(repl_pos); diff --git a/src/main.cpp b/src/main.cpp index ca3a37d..d0dfd84 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -254,26 +254,33 @@ main(int argc, char **argv) /* * Execute macro or mung file */ - if (eval_macro) { - try { - Execute::macro(eval_macro, false); - } catch (...) { - exit(EXIT_FAILURE); + try { + if (eval_macro) { + try { + Execute::macro(eval_macro, false); + } catch (State::Error &error) { + error.add_frame(new State::Error::ToplevelFrame()); + throw; /* forward */ + } + exit(EXIT_SUCCESS); } - exit(EXIT_SUCCESS); - } - if (g_file_test(mung_file, G_FILE_TEST_IS_REGULAR)) { - if (!Execute::file(mung_file, false)) - exit(EXIT_FAILURE); + if (g_file_test(mung_file, G_FILE_TEST_IS_REGULAR)) { + Execute::file(mung_file, false); - /* FIXME: make quit immediate in batch/macro mode (non-UNDO)? */ - if (quit_requested) { - /* FIXME */ - exit(EXIT_SUCCESS); + /* FIXME: make quit immediate in batch/macro mode (non-UNDO)? */ + if (quit_requested) { + /* FIXME */ + exit(EXIT_SUCCESS); + } } + g_free(mung_file); + } catch (State::Error &error) { + error.display_full(); + exit(EXIT_FAILURE); + } catch (...) { + exit(EXIT_FAILURE); } - g_free(mung_file); /* * If munged file didn't quit, switch into interactive mode diff --git a/src/parser.cpp b/src/parser.cpp index 4572f02..016b8bd 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -82,10 +82,15 @@ Execute::step(const gchar *macro, gint stop_pos) States::current, mode); #endif - if (interface.is_interrupted()) - throw State::Error("Interrupted"); - - State::input(macro[macro_pc]); + try { + if (interface.is_interrupted()) + throw State::Error("Interrupted"); + + State::input(macro[macro_pc]); + } catch (State::Error &error) { + error.pos = macro_pc; + throw; /* forward */ + } macro_pc++; } } @@ -147,26 +152,34 @@ Execute::macro(const gchar *macro, bool locals) States::current = parent_state; } -bool +void Execute::file(const gchar *filename, bool locals) + throw (State::Error, ReplaceCmdline) { - gchar *macro_str, *p = NULL; + gchar *macro_str, *p; if (!g_file_get_contents(filename, ¯o_str, NULL, NULL)) - return false; + throw State::Error("Error reading file \"%s\"", filename); /* only when executing files, ignore Hash-Bang line */ if (*macro_str == '#') - p = MAX(strchr(macro_str, '\r'), strchr(macro_str, '\n')); + p = MAX(strchr(macro_str, '\r'), strchr(macro_str, '\n'))+1; + else + p = macro_str; try { - macro(p ? p+1 : macro_str, locals); + macro(p, locals); + } catch (State::Error &error) { + error.pos += p - macro_str; + error.add_frame(new State::Error::FileFrame(filename)); + + g_free(macro_str); + throw; /* forward */ } catch (...) { g_free(macro_str); - return false; + throw; /* forward */ } g_free(macro_str); - return true; } ReplaceCmdline::ReplaceCmdline() @@ -178,15 +191,45 @@ ReplaceCmdline::ReplaceCmdline() pos++; } -State::Error::Error(const gchar *fmt, ...) +State::Error::Error(const gchar *fmt, ...) : frames(NULL), pos(0) { va_list ap; va_start(ap, fmt); - interface.vmsg(Interface::MSG_ERROR, fmt, ap); + description = g_strdup_vprintf(fmt, ap); va_end(ap); } +void +State::Error::display_short(void) +{ + interface.msg(Interface::MSG_ERROR, + "%s (at %d)", description, pos); +} + +void +State::Error::display_full(void) +{ + gint nr = 0; + + interface.msg(Interface::MSG_ERROR, "%s", description); + + frames = g_slist_reverse(frames); + for (GSList *cur = frames; cur; cur = g_slist_next(cur)) { + Frame *frame = (Frame *)cur->data; + + frame->display(nr++); + } +} + +State::Error::~Error() +{ + g_free(description); + for (GSList *cur = frames; cur; cur = g_slist_next(cur)) + delete (Frame *)cur->data; + g_slist_free(frames); +} + State::State() { for (guint i = 0; i < G_N_ELEMENTS(transitions); i++) @@ -366,7 +409,7 @@ StringBuildingMachine::~StringBuildingMachine() } State * -StateExpectString::custom(gchar chr) throw (Error) +StateExpectString::custom(gchar chr) throw (Error, ReplaceCmdline) { gchar *insert; diff --git a/src/parser.h b/src/parser.h index b96e8d4..8aa7ef5 100644 --- a/src/parser.h +++ b/src/parser.h @@ -39,8 +39,84 @@ public: class State { public: class Error { + gchar *description; + GSList *frames; + public: + gint pos; + + class Frame { + public: + gint pos; + virtual ~Frame() {} + + virtual void display(gint nr) = 0; + }; + + class QRegFrame : public Frame { + gchar *name; + + public: + QRegFrame(const gchar *_name) + : name(g_strdup(_name)) {} + + ~QRegFrame() + { + g_free(name); + } + + void + display(gint nr) + { + interface.msg(Interface::MSG_INFO, + "#%d in Q-Register \"%s\" at %d", + nr, name, pos); + } + }; + + class FileFrame : public Frame { + gchar *name; + + public: + FileFrame(const gchar *_name) + : name(g_strdup(_name)) {} + + ~FileFrame() + { + g_free(name); + } + + void + display(gint nr) + { + interface.msg(Interface::MSG_INFO, + "#%d in file \"%s\" at %d", + nr, name, pos); + } + }; + + class ToplevelFrame : public Frame { + public: + void + display(gint nr) + { + interface.msg(Interface::MSG_INFO, + "#%d in toplevel macro at %d", nr, pos); + } + }; + Error(const gchar *fmt, ...) G_GNUC_PRINTF(2, 3); + ~Error(); + + inline void + add_frame(Frame *frame) + { + frame->pos = pos; + frames = g_slist_prepend(frames, frame); + } + + void display_short(void); + void display_full(void); }; class SyntaxError : public Error { @@ -193,12 +269,12 @@ public: string_building(_building), last(_last) {} private: - State *custom(gchar chr) throw (Error); + State *custom(gchar chr) throw (Error, ReplaceCmdline); protected: virtual void initial(void) throw (Error) {} virtual void process(const gchar *str, gint new_chars) throw (Error) {} - virtual State *done(const gchar *str) throw (Error) = 0; + virtual State *done(const gchar *str) throw (Error, ReplaceCmdline) = 0; }; class StateExpectFile : public StateExpectString { @@ -334,7 +410,8 @@ namespace Execute { throw (State::Error, ReplaceCmdline); void macro(const gchar *macro, bool locals = true) throw (State::Error, ReplaceCmdline); - bool file(const gchar *filename, bool locals = true); + void file(const gchar *filename, bool locals = true) + throw (State::Error, ReplaceCmdline); } #endif diff --git a/src/qregisters.cpp b/src/qregisters.cpp index 02a779a..a0dc5f5 100644 --- a/src/qregisters.cpp +++ b/src/qregisters.cpp @@ -185,6 +185,11 @@ QRegister::execute(bool locals) throw (State::Error, ReplaceCmdline) try { Execute::macro(str, locals); + } catch (State::Error &error) { + error.add_frame(new State::Error::QRegFrame(name)); + + g_free(str); + throw; /* forward */ } catch (...) { g_free(str); throw; /* forward */ @@ -698,13 +703,12 @@ StateMacro::got_register(QRegister ®) throw (Error, ReplaceCmdline) * If <file> could not be read, the command yields an error. */ State * -StateMacroFile::done(const gchar *str) throw (Error) +StateMacroFile::done(const gchar *str) throw (Error, ReplaceCmdline) { BEGIN_EXEC(&States::start); /* don't create new local Q-Registers if colon modifier is given */ - if (!Execute::file(str, !eval_colon())) - throw Error("Cannot execute macro from file \"%s\"", str); + Execute::file(str, !eval_colon()); return &States::start; } diff --git a/src/qregisters.h b/src/qregisters.h index 840e442..4b1ad3d 100644 --- a/src/qregisters.h +++ b/src/qregisters.h @@ -383,7 +383,7 @@ private: class StateMacroFile : public StateExpectFile { private: - State *done(const gchar *str) throw (Error); + State *done(const gchar *str) throw (Error, ReplaceCmdline); }; class StateCopyToQReg : public StateExpectQReg { |