aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/goto.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/goto.cpp')
-rw-r--r--src/goto.cpp147
1 files changed, 147 insertions, 0 deletions
diff --git a/src/goto.cpp b/src/goto.cpp
new file mode 100644
index 0000000..7af5152
--- /dev/null
+++ b/src/goto.cpp
@@ -0,0 +1,147 @@
+#include <glib.h>
+#include <glib/gprintf.h>
+
+#include "sciteco.h"
+#include "expressions.h"
+#include "parser.h"
+#include "undo.h"
+#include "goto.h"
+
+namespace States {
+ StateLabel label;
+ StateGotoCmd gotocmd;
+}
+
+namespace Goto {
+ GotoTable *table = NULL;
+ gchar *skip_label = NULL;
+}
+
+gint
+GotoTable::remove(gchar *name)
+{
+ gint existing_pc = -1;
+
+ Label label(name);
+ Label *existing = (Label *)RBTree::find(&label);
+
+ if (existing) {
+ existing_pc = existing->pc;
+ RBTree::remove(existing);
+ delete existing;
+ }
+
+ return existing_pc;
+}
+
+gint
+GotoTable::find(gchar *name)
+{
+ Label label(name);
+ Label *existing = (Label *)RBTree::find(&label);
+
+ return existing ? existing->pc : -1;
+}
+
+gint
+GotoTable::set(gchar *name, gint pc)
+{
+ if (pc < 0)
+ return remove(name);
+
+ Label *label = new Label(name, pc);
+ Label *existing;
+ gint existing_pc = -1;
+
+ existing = (Label *)RBTree::find(label);
+ if (existing) {
+ existing_pc = existing->pc;
+ g_free(existing->name);
+ existing->name = label->name;
+ existing->pc = label->pc;
+
+ label->name = NULL;
+ delete label;
+ } else {
+ RBTree::insert(label);
+ }
+
+#ifdef DEBUG
+ dump();
+#endif
+
+ return existing_pc;
+}
+
+#ifdef DEBUG
+void
+GotoTable::dump(void)
+{
+ for (Label *cur = (Label *)RBTree::min();
+ cur != NULL;
+ cur = (Label *)cur->next())
+ g_printf("table[\"%s\"] = %d\n", cur->name, cur->pc);
+ g_printf("---END---\n");
+}
+#endif
+
+/*
+ * Command states
+ */
+
+StateLabel::StateLabel() : State()
+{
+ transitions['\0'] = this;
+}
+
+State *
+StateLabel::custom(gchar chr) throw (Error)
+{
+ if (chr == '!') {
+ Goto::table->undo_set(strings[0],
+ Goto::table->set(strings[0], macro_pc));
+
+ if (!g_strcmp0(strings[0], Goto::skip_label)) {
+ g_free(undo.push_str(Goto::skip_label));
+ Goto::skip_label = NULL;
+
+ undo.push_var(mode) = MODE_NORMAL;
+ }
+
+ g_free(undo.push_str(strings[0]));
+ strings[0] = NULL;
+
+ return &States::start;
+ }
+
+ String::append(undo.push_str(strings[0]), chr);
+ return this;
+}
+
+State *
+StateGotoCmd::done(const gchar *str) throw (Error)
+{
+ gint64 value;
+ gchar **labels;
+
+ BEGIN_EXEC(&States::start);
+
+ value = expressions.pop_num_calc();
+ labels = g_strsplit(str, ",", -1);
+
+ if (value > 0 && value <= g_strv_length(labels) && *labels[value-1]) {
+ gint pc = Goto::table->find(labels[value-1]);
+
+ if (pc >= 0) {
+ macro_pc = pc;
+ } else {
+ /* skip till label is defined */
+ undo.push_str(Goto::skip_label);
+ Goto::skip_label = g_strdup(labels[value-1]);
+ undo.push_var(mode) = MODE_PARSE_ONLY_GOTO;
+ }
+ }
+
+ g_strfreev(labels);
+ return &States::start;
+}