aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
authorRobin Haberkorn <robin.haberkorn@googlemail.com>2013-03-28 17:37:49 +0100
committerRobin Haberkorn <robin.haberkorn@googlemail.com>2014-02-15 15:21:52 +0100
commitd374448af8ab690c810757f73ba44f208db96f30 (patch)
tree48e15c42d7a01b10aaef51d0113572ea53fec797 /src
parent6d4668bdaf393aa45d9adb640774f998c6b4aa58 (diff)
downloadsciteco-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
Diffstat (limited to 'src')
-rw-r--r--src/cmdline.cpp5
-rw-r--r--src/main.cpp37
-rw-r--r--src/parser.cpp71
-rw-r--r--src/parser.h83
-rw-r--r--src/qregisters.cpp10
-rw-r--r--src/qregisters.h2
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, &macro_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 &reg) 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 {