diff options
Diffstat (limited to 'parser.cpp')
-rw-r--r-- | parser.cpp | 1436 |
1 files changed, 0 insertions, 1436 deletions
diff --git a/parser.cpp b/parser.cpp deleted file mode 100644 index 5ba96fa..0000000 --- a/parser.cpp +++ /dev/null @@ -1,1436 +0,0 @@ -#include <stdarg.h> -#include <string.h> - -#include <glib.h> -#include <glib/gprintf.h> -#include <glib/gstdio.h> - -#include "sciteco.h" -#include "interface.h" -#include "undo.h" -#include "expressions.h" -#include "goto.h" -#include "qregisters.h" -#include "ring.h" -#include "parser.h" -#include "symbols.h" -#include "search.h" - -//#define DEBUG - -gint macro_pc = 0; - -namespace States { - StateStart start; - StateControl control; - StateFCommand fcommand; - StateCondCommand condcommand; - StateECommand ecommand; - StateScintilla_symbols scintilla_symbols; - StateScintilla_lParam scintilla_lparam; - StateInsert insert; - - State *current = &start; -} - -namespace Modifiers { - static bool colon = false; - static bool at = false; -} - -enum Mode mode = MODE_NORMAL; - -/* FIXME: perhaps integrate into Mode */ -static bool skip_else = false; - -static gint nest_level = 0; - -gchar *strings[2] = {NULL, NULL}; -gchar escape_char = '\x1B'; - -void -Execute::step(const gchar *macro) throw (State::Error) -{ - while (macro[macro_pc]) { -#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); -#endif - - State::input(macro[macro_pc]); - macro_pc++; - } -} - -void -Execute::macro(const gchar *macro, bool locals) throw (State::Error) -{ - GotoTable *parent_goto_table = Goto::table; - GotoTable macro_goto_table(false); - - QRegisterTable *parent_locals = QRegisters::locals; - QRegisterTable macro_locals(false); - - State *parent_state = States::current; - gint parent_pc = macro_pc; - - /* - * need this to fixup state on rubout: state machine emits undo token - * resetting state to parent's one, but the macro executed also emitted - * undo tokens resetting the state to StateStart - */ - undo.push_var(States::current) = &States::start; - macro_pc = 0; - - Goto::table = ¯o_goto_table; - if (locals) { - macro_locals.initialize(); - QRegisters::locals = ¯o_locals; - } - - try { - step(macro); - if (Goto::skip_label) - throw State::Error("Label \"%s\" not found", - Goto::skip_label); - } catch (...) { - g_free(Goto::skip_label); - Goto::skip_label = NULL; - - QRegisters::locals = parent_locals; - Goto::table = parent_goto_table; - - macro_pc = parent_pc; - States::current = parent_state; - - throw; /* forward */ - } - - QRegisters::locals = parent_locals; - Goto::table = parent_goto_table; - - macro_pc = parent_pc; - States::current = parent_state; -} - -bool -Execute::file(const gchar *filename, bool locals) -{ - gchar *macro_str, *p = NULL; - - if (!g_file_get_contents(filename, ¯o_str, NULL, NULL)) - return false; - /* only when executing files, ignore Hash-Bang line */ - if (*macro_str == '#') - p = MAX(strchr(macro_str, '\r'), strchr(macro_str, '\n')); - - try { - macro(p ? p+1 : macro_str, locals); - } catch (...) { - g_free(macro_str); - return false; - } - - g_free(macro_str); - return true; -} - -State::Error::Error(const gchar *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - interface.vmsg(Interface::MSG_ERROR, fmt, ap); - va_end(ap); -} - -State::State() -{ - for (guint i = 0; i < G_N_ELEMENTS(transitions); i++) - transitions[i] = NULL; -} - -bool -State::eval_colon(void) -{ - if (!Modifiers::colon) - return false; - - undo.push_var<bool>(Modifiers::colon); - Modifiers::colon = false; - return true; -} - -void -State::input(gchar chr) throw (Error) -{ - State *state = States::current; - - for (;;) { - State *next = state->get_next_state(chr); - - if (next == state) - break; - - state = next; - chr = '\0'; - } - - if (state != States::current) { - undo.push_var<State *>(States::current); - States::current = state; - } -} - -State * -State::get_next_state(gchar chr) throw (Error) -{ - State *next = NULL; - guint upper = g_ascii_toupper(chr); - - if (upper < G_N_ELEMENTS(transitions)) - next = transitions[upper]; - if (!next) - next = custom(chr); - if (!next) - throw SyntaxError(chr); - - return next; -} - -gchar * -StateExpectString::machine_input(gchar chr) throw (Error) -{ - switch (machine.mode) { - case Machine::MODE_UPPER: - chr = g_ascii_toupper(chr); - break; - case Machine::MODE_LOWER: - chr = g_ascii_tolower(chr); - break; - default: - break; - } - - if (machine.toctl) { - chr = CTL_KEY(g_ascii_toupper(chr)); - machine.toctl = false; - } - - if (machine.state == Machine::STATE_ESCAPED) { - machine.state = Machine::STATE_START; - goto original; - } - - if (chr == '^') { - machine.toctl = true; - return NULL; - } - - switch (machine.state) { - case Machine::STATE_START: - switch (chr) { - case CTL_KEY('Q'): - case CTL_KEY('R'): machine.state = Machine::STATE_ESCAPED; break; - case CTL_KEY('V'): machine.state = Machine::STATE_LOWER; break; - case CTL_KEY('W'): machine.state = Machine::STATE_UPPER; break; - case CTL_KEY('E'): machine.state = Machine::STATE_CTL_E; break; - default: - goto original; - } - break; - - case Machine::STATE_LOWER: - machine.state = Machine::STATE_START; - if (chr != CTL_KEY('V')) - return g_strdup((gchar []){g_ascii_tolower(chr), '\0'}); - machine.mode = Machine::MODE_LOWER; - break; - - case Machine::STATE_UPPER: - machine.state = Machine::STATE_START; - if (chr != CTL_KEY('W')) - return g_strdup((gchar []){g_ascii_toupper(chr), '\0'}); - machine.mode = Machine::MODE_UPPER; - break; - - case Machine::STATE_CTL_E: - switch (g_ascii_toupper(chr)) { - case 'Q': machine.state = Machine::STATE_CTL_EQ; break; - case 'U': machine.state = Machine::STATE_CTL_EU; break; - default: - machine.state = Machine::STATE_START; - return g_strdup((gchar []){CTL_KEY('E'), chr, '\0'}); - } - break; - - case Machine::STATE_CTL_EU: - if (chr == '.') { - machine.state = Machine::STATE_CTL_EU_LOCAL; - } else { - QRegister *reg; - - machine.state = Machine::STATE_START; - reg = QRegisters::globals[g_ascii_toupper(chr)]; - if (!reg) - throw InvalidQRegError(chr); - return g_strdup(CHR2STR(reg->get_integer())); - } - break; - - case Machine::STATE_CTL_EU_LOCAL: { - QRegister *reg; - - machine.state = Machine::STATE_START; - reg = (*QRegisters::locals)[g_ascii_toupper(chr)]; - if (!reg) - throw InvalidQRegError(chr, true); - return g_strdup(CHR2STR(reg->get_integer())); - } - - case Machine::STATE_CTL_EQ: - if (chr == '.') { - machine.state = Machine::STATE_CTL_EQ_LOCAL; - } else { - QRegister *reg; - - machine.state = Machine::STATE_START; - reg = QRegisters::globals[g_ascii_toupper(chr)]; - if (!reg) - throw InvalidQRegError(chr); - return reg->get_string(); - } - break; - - case Machine::STATE_CTL_EQ_LOCAL: { - QRegister *reg; - - machine.state = Machine::STATE_START; - reg = (*QRegisters::locals)[g_ascii_toupper(chr)]; - if (!reg) - throw InvalidQRegError(chr, true); - return reg->get_string(); - } - - default: - g_assert(TRUE); - } - - return NULL; - -original: - return g_strdup((gchar []){chr, '\0'}); -} - -State * -StateExpectString::custom(gchar chr) throw (Error) -{ - gchar *insert; - - if (chr == '\0') { - BEGIN_EXEC(this); - initial(); - return this; - } - - /* - * String termination handling - */ - if (Modifiers::at) { - if (last) - undo.push_var(Modifiers::at) = false; - - switch (escape_char) { - case '\x1B': - case '{': - undo.push_var(escape_char) = g_ascii_toupper(chr); - return this; - } - } - - if (escape_char == '{') { - switch (chr) { - case '{': - undo.push_var(nesting); - nesting++; - break; - case '}': - undo.push_var(nesting); - nesting--; - break; - } - } else if (g_ascii_toupper(chr) == escape_char) { - undo.push_var(nesting); - nesting--; - } - - if (!nesting) { - State *next; - gchar *string = strings[0]; - - undo.push_str(strings[0]) = NULL; - if (last) - undo.push_var(escape_char) = '\x1B'; - nesting = 1; - - if (string_building) { - undo.push_var(machine); - machine.state = Machine::STATE_START; - machine.mode = Machine::MODE_NORMAL; - machine.toctl = false; - } - - next = done(string ? : ""); - g_free(string); - return next; - } - - BEGIN_EXEC(this); - - /* - * String building characters - */ - if (string_building) { - undo.push_var(machine); - insert = machine_input(chr); - if (!insert) - return this; - } else { - insert = g_strdup((gchar []){chr, '\0'}); - } - - /* - * String accumulation - */ - undo.push_str(strings[0]); - String::append(strings[0], insert); - - process(strings[0], strlen(insert)); - g_free(insert); - return this; -} - -StateExpectQReg::StateExpectQReg() : State(), got_local(false) -{ - transitions['\0'] = this; -} - -State * -StateExpectQReg::custom(gchar chr) throw (Error) -{ - QRegister *reg; - - if (chr == '.') { - undo.push_var(got_local) = true; - return this; - } - chr = g_ascii_toupper(chr); - - if (got_local) { - undo.push_var(got_local) = false; - reg = (*QRegisters::locals)[chr]; - } else { - reg = QRegisters::globals[chr]; - } - if (!reg) - throw InvalidQRegError(chr, got_local); - - return got_register(reg); -} - -StateStart::StateStart() : State() -{ - transitions['\0'] = this; - init(" \f\r\n\v"); - - transitions['!'] = &States::label; - transitions['O'] = &States::gotocmd; - transitions['^'] = &States::control; - transitions['F'] = &States::fcommand; - transitions['"'] = &States::condcommand; - transitions['E'] = &States::ecommand; - transitions['I'] = &States::insert; - transitions['S'] = &States::search; - transitions['N'] = &States::searchall; - - transitions['['] = &States::pushqreg; - transitions[']'] = &States::popqreg; - transitions['G'] = &States::getqregstring; - transitions['Q'] = &States::getqreginteger; - transitions['U'] = &States::setqreginteger; - transitions['%'] = &States::increaseqreg; - transitions['M'] = &States::macro; - transitions['X'] = &States::copytoqreg; -} - -void -StateStart::insert_integer(gint64 v) -{ - gchar buf[64+1]; /* maximum length if radix = 2 */ - gchar *p = buf + sizeof(buf); - - *--p = '\0'; - interface.ssm(SCI_BEGINUNDOACTION); - if (v < 0) { - interface.ssm(SCI_ADDTEXT, 1, (sptr_t)"-"); - v *= -1; - } - do { - *--p = '0' + (v % expressions.radix); - if (*p > '9') - *p += 'A' - '9'; - } while ((v /= expressions.radix)); - interface.ssm(SCI_ADDTEXT, buf + sizeof(buf) - p - 1, - (sptr_t)p); - interface.ssm(SCI_SCROLLCARET); - interface.ssm(SCI_ENDUNDOACTION); - ring.dirtify(); - - undo.push_msg(SCI_UNDO); -} - -gint64 -StateStart::read_integer(void) -{ - uptr_t pos = interface.ssm(SCI_GETCURRENTPOS); - gchar c = (gchar)interface.ssm(SCI_GETCHARAT, pos); - gint64 v = 0; - gint sign = 1; - - if (c == '-') { - pos++; - sign = -1; - } - - for (;;) { - c = g_ascii_toupper((gchar)interface.ssm(SCI_GETCHARAT, pos)); - if (c >= '0' && c <= '0' + MIN(expressions.radix, 10) - 1) - v = (v*expressions.radix) + (c - '0'); - else if (c >= 'A' && - c <= 'A' + MIN(expressions.radix - 10, 26) - 1) - v = (v*expressions.radix) + 10 + (c - 'A'); - else - break; - - pos++; - } - - return sign * v; -} - -tecoBool -StateStart::move_chars(gint64 n) -{ - sptr_t pos = interface.ssm(SCI_GETCURRENTPOS); - - if (!Validate::pos(pos + n)) - return FAILURE; - - interface.ssm(SCI_GOTOPOS, pos + n); - undo.push_msg(SCI_GOTOPOS, pos); - return SUCCESS; -} - -tecoBool -StateStart::move_lines(gint64 n) -{ - sptr_t pos = interface.ssm(SCI_GETCURRENTPOS); - sptr_t line = interface.ssm(SCI_LINEFROMPOSITION, pos) + n; - - if (!Validate::line(line)) - return FAILURE; - - interface.ssm(SCI_GOTOLINE, line); - undo.push_msg(SCI_GOTOPOS, pos); - return SUCCESS; -} - -tecoBool -StateStart::delete_words(gint64 n) -{ - sptr_t pos, size; - - if (!n) - return SUCCESS; - - pos = interface.ssm(SCI_GETCURRENTPOS); - size = interface.ssm(SCI_GETLENGTH); - interface.ssm(SCI_BEGINUNDOACTION); - /* - * FIXME: would be nice to do this with constant amount of - * editor messages. E.g. by using custom algorithm accessing - * the internal document buffer. - */ - if (n > 0) { - while (n--) { - sptr_t size = interface.ssm(SCI_GETLENGTH); - interface.ssm(SCI_DELWORDRIGHTEND); - if (size == interface.ssm(SCI_GETLENGTH)) - break; - } - } else { - n *= -1; - while (n--) { - sptr_t pos = interface.ssm(SCI_GETCURRENTPOS); - //interface.ssm(SCI_DELWORDLEFTEND); - interface.ssm(SCI_WORDLEFTEND); - if (pos == interface.ssm(SCI_GETCURRENTPOS)) - break; - interface.ssm(SCI_DELWORDRIGHTEND); - } - } - interface.ssm(SCI_ENDUNDOACTION); - - if (n >= 0) { - if (size != interface.ssm(SCI_GETLENGTH)) { - interface.ssm(SCI_UNDO); - interface.ssm(SCI_GOTOPOS, pos); - } - return FAILURE; - } - - undo.push_msg(SCI_GOTOPOS, pos); - undo.push_msg(SCI_UNDO); - ring.dirtify(); - - return SUCCESS; -} - -State * -StateStart::custom(gchar chr) throw (Error) -{ - gint64 v; - tecoBool rc; - - /* - * <CTRL/x> commands implemented in StateCtrlCmd - */ - if (IS_CTL(chr)) - return States::control.get_next_state(CTL_ECHO(chr)); - - /* - * arithmetics - */ - if (g_ascii_isdigit(chr)) { - BEGIN_EXEC(this); - expressions.add_digit(chr); - return this; - } - - chr = g_ascii_toupper(chr); - switch (chr) { - case '/': - BEGIN_EXEC(this); - expressions.push_calc(Expressions::OP_DIV); - break; - - case '*': - BEGIN_EXEC(this); - expressions.push_calc(Expressions::OP_MUL); - break; - - case '+': - BEGIN_EXEC(this); - expressions.push_calc(Expressions::OP_ADD); - break; - - case '-': - BEGIN_EXEC(this); - if (!expressions.args()) - expressions.set_num_sign(-expressions.num_sign); - else - expressions.push_calc(Expressions::OP_SUB); - break; - - case '&': - BEGIN_EXEC(this); - expressions.push_calc(Expressions::OP_AND); - break; - - case '#': - BEGIN_EXEC(this); - expressions.push_calc(Expressions::OP_OR); - break; - - case '(': - BEGIN_EXEC(this); - if (expressions.num_sign < 0) { - expressions.set_num_sign(1); - expressions.eval(); - expressions.push(-1); - expressions.push_calc(Expressions::OP_MUL); - } - expressions.push(Expressions::OP_BRACE); - break; - - case ')': - BEGIN_EXEC(this); - expressions.eval(true); - break; - - case ',': - BEGIN_EXEC(this); - expressions.eval(); - expressions.push(Expressions::OP_NEW); - break; - - case '.': - BEGIN_EXEC(this); - expressions.eval(); - expressions.push(interface.ssm(SCI_GETCURRENTPOS)); - break; - - case 'Z': - BEGIN_EXEC(this); - expressions.eval(); - expressions.push(interface.ssm(SCI_GETLENGTH)); - break; - - case 'H': - BEGIN_EXEC(this); - expressions.eval(); - expressions.push(0); - expressions.push(interface.ssm(SCI_GETLENGTH)); - break; - - case '\\': - BEGIN_EXEC(this); - expressions.eval(); - if (expressions.args()) - insert_integer(expressions.pop_num_calc()); - else - expressions.push(read_integer()); - break; - - /* - * control structures (loops) - */ - case '<': - if (mode == MODE_PARSE_ONLY_LOOP) { - undo.push_var<gint>(nest_level); - nest_level++; - return this; - } - BEGIN_EXEC(this); - - expressions.eval(); - if (!expressions.args()) - /* infinite loop */ - expressions.push(-1); - - if (!expressions.peek_num()) { - expressions.pop_num(); - - /* skip to end of loop */ - undo.push_var<Mode>(mode); - mode = MODE_PARSE_ONLY_LOOP; - } else { - expressions.push(macro_pc); - expressions.push(Expressions::OP_LOOP); - } - break; - - case '>': - if (mode == MODE_PARSE_ONLY_LOOP) { - if (!nest_level) { - undo.push_var<Mode>(mode); - mode = MODE_NORMAL; - } else { - undo.push_var<gint>(nest_level); - nest_level--; - } - } else { - BEGIN_EXEC(this); - gint64 loop_pc, loop_cnt; - - expressions.discard_args(); - g_assert(expressions.pop_op() == Expressions::OP_LOOP); - loop_pc = expressions.pop_num(); - loop_cnt = expressions.pop_num(); - - if (loop_cnt != 1) { - /* repeat loop */ - macro_pc = loop_pc; - expressions.push(MAX(loop_cnt - 1, -1)); - expressions.push(loop_pc); - expressions.push(Expressions::OP_LOOP); - } - } - break; - - case ';': - BEGIN_EXEC(this); - - v = QRegisters::globals["_"]->get_integer(); - rc = expressions.pop_num_calc(1, v); - if (eval_colon()) - rc = ~rc; - - if (IS_FAILURE(rc)) { - expressions.discard_args(); - g_assert(expressions.pop_op() == Expressions::OP_LOOP); - expressions.pop_num(); /* pc */ - expressions.pop_num(); /* counter */ - - /* skip to end of loop */ - undo.push_var<Mode>(mode); - mode = MODE_PARSE_ONLY_LOOP; - } - break; - - /* - * control structures (conditionals) - */ - case '|': - if (mode == MODE_PARSE_ONLY_COND) { - if (!skip_else && !nest_level) { - undo.push_var<Mode>(mode); - mode = MODE_NORMAL; - } - return this; - } - BEGIN_EXEC(this); - - /* skip to end of conditional; skip ELSE-part */ - undo.push_var<Mode>(mode); - mode = MODE_PARSE_ONLY_COND; - break; - - case '\'': - if (mode != MODE_PARSE_ONLY_COND) - break; - - if (!nest_level) { - undo.push_var<Mode>(mode); - mode = MODE_NORMAL; - undo.push_var<bool>(skip_else); - skip_else = false; - } else { - undo.push_var<gint>(nest_level); - nest_level--; - } - break; - - /* - * modifiers - */ - case '@': - /* - * @ modifier has syntactic significance, so set it even - * in PARSE_ONLY* modes - */ - undo.push_var<bool>(Modifiers::at); - Modifiers::at = true; - break; - - case ':': - BEGIN_EXEC(this); - undo.push_var<bool>(Modifiers::colon); - Modifiers::colon = true; - break; - - /* - * commands - */ - case 'J': - BEGIN_EXEC(this); - v = expressions.pop_num_calc(1, 0); - if (Validate::pos(v)) { - undo.push_msg(SCI_GOTOPOS, - interface.ssm(SCI_GETCURRENTPOS)); - interface.ssm(SCI_GOTOPOS, v); - - if (eval_colon()) - expressions.push(SUCCESS); - } else if (eval_colon()) { - expressions.push(FAILURE); - } else { - throw MoveError("J"); - } - break; - - case 'C': - BEGIN_EXEC(this); - rc = move_chars(expressions.pop_num_calc()); - if (eval_colon()) - expressions.push(rc); - else if (IS_FAILURE(rc)) - throw MoveError("C"); - break; - - case 'R': - BEGIN_EXEC(this); - rc = move_chars(-expressions.pop_num_calc()); - if (eval_colon()) - expressions.push(rc); - else if (IS_FAILURE(rc)) - throw MoveError("R"); - break; - - case 'L': - BEGIN_EXEC(this); - rc = move_lines(expressions.pop_num_calc()); - if (eval_colon()) - expressions.push(rc); - else if (IS_FAILURE(rc)) - throw MoveError("L"); - break; - - case 'B': - BEGIN_EXEC(this); - rc = move_lines(-expressions.pop_num_calc()); - if (eval_colon()) - expressions.push(rc); - else if (IS_FAILURE(rc)) - throw MoveError("B"); - break; - - case 'W': { - sptr_t pos; - unsigned int msg = SCI_WORDRIGHTEND; - - BEGIN_EXEC(this); - v = expressions.pop_num_calc(); - - pos = interface.ssm(SCI_GETCURRENTPOS); - /* - * FIXME: would be nice to do this with constant amount of - * editor messages. E.g. by using custom algorithm accessing - * the internal document buffer. - */ - if (v < 0) { - v *= -1; - msg = SCI_WORDLEFTEND; - } - while (v--) { - sptr_t pos = interface.ssm(SCI_GETCURRENTPOS); - interface.ssm(msg); - if (pos == interface.ssm(SCI_GETCURRENTPOS)) - break; - } - if (v < 0) { - undo.push_msg(SCI_GOTOPOS, pos); - if (eval_colon()) - expressions.push(SUCCESS); - } else { - interface.ssm(SCI_GOTOPOS, pos); - if (eval_colon()) - expressions.push(FAILURE); - else - throw MoveError("W"); - } - break; - } - - case 'V': - BEGIN_EXEC(this); - rc = delete_words(expressions.pop_num_calc()); - if (eval_colon()) - expressions.push(rc); - else if (IS_FAILURE(rc)) - throw Error("Not enough words to delete with <V>"); - break; - - case 'Y': - BEGIN_EXEC(this); - rc = delete_words(-expressions.pop_num_calc()); - if (eval_colon()) - expressions.push(rc); - else if (IS_FAILURE(rc)) - throw Error("Not enough words to delete with <Y>"); - break; - - case '=': - BEGIN_EXEC(this); - interface.msg(Interface::MSG_USER, "%" G_GINT64_FORMAT, - expressions.pop_num_calc()); - break; - - case 'K': - case 'D': { - gint64 from, len; - - BEGIN_EXEC(this); - expressions.eval(); - - if (expressions.args() <= 1) { - from = interface.ssm(SCI_GETCURRENTPOS); - if (chr == 'D') { - len = expressions.pop_num_calc(); - rc = TECO_BOOL(Validate::pos(from + len)); - } else /* chr == 'K' */ { - sptr_t line; - line = interface.ssm(SCI_LINEFROMPOSITION, from) + - expressions.pop_num_calc(); - len = interface.ssm(SCI_POSITIONFROMLINE, line) - - from; - rc = TECO_BOOL(Validate::line(line)); - } - if (len < 0) { - len *= -1; - from -= len; - } - } else { - gint64 to = expressions.pop_num(); - from = expressions.pop_num(); - len = to - from; - rc = TECO_BOOL(len >= 0 && Validate::pos(from) && - Validate::pos(to)); - } - - if (eval_colon()) - expressions.push(rc); - else if (IS_FAILURE(rc)) - throw RangeError(chr); - - if (len == 0 || IS_FAILURE(rc)) - break; - - undo.push_msg(SCI_GOTOPOS, interface.ssm(SCI_GETCURRENTPOS)); - undo.push_msg(SCI_UNDO); - - interface.ssm(SCI_BEGINUNDOACTION); - interface.ssm(SCI_DELETERANGE, from, len); - interface.ssm(SCI_ENDUNDOACTION); - ring.dirtify(); - break; - } - - case 'A': - BEGIN_EXEC(this); - v = interface.ssm(SCI_GETCURRENTPOS) + - expressions.pop_num_calc(); - if (!Validate::pos(v)) - throw RangeError("A"); - expressions.push(interface.ssm(SCI_GETCHARAT, v)); - break; - - default: - throw SyntaxError(chr); - } - - return this; -} - -StateFCommand::StateFCommand() : State() -{ - transitions['\0'] = this; - transitions['K'] = &States::searchkill; - transitions['D'] = &States::searchdelete; - transitions['S'] = &States::replace; - transitions['R'] = &States::replacedefault; -} - -State * -StateFCommand::custom(gchar chr) throw (Error) -{ - switch (chr) { - /* - * loop flow control - */ - case '<': - BEGIN_EXEC(&States::start); - /* FIXME: what if in brackets? */ - expressions.discard_args(); - if (expressions.peek_op() == Expressions::OP_LOOP) - /* repeat loop */ - macro_pc = expressions.peek_num(); - else - macro_pc = -1; - break; - - case '>': { - gint64 loop_pc, loop_cnt; - - BEGIN_EXEC(&States::start); - /* FIXME: what if in brackets? */ - expressions.discard_args(); - g_assert(expressions.pop_op() == Expressions::OP_LOOP); - loop_pc = expressions.pop_num(); - loop_cnt = expressions.pop_num(); - - if (loop_cnt != 1) { - /* repeat loop */ - macro_pc = loop_pc; - expressions.push(MAX(loop_cnt - 1, -1)); - expressions.push(loop_pc); - expressions.push(Expressions::OP_LOOP); - } else { - /* skip to end of loop */ - undo.push_var<Mode>(mode); - mode = MODE_PARSE_ONLY_LOOP; - } - break; - } - - /* - * conditional flow control - */ - case '\'': - BEGIN_EXEC(&States::start); - /* skip to end of conditional */ - undo.push_var<Mode>(mode); - mode = MODE_PARSE_ONLY_COND; - undo.push_var<bool>(skip_else); - skip_else = true; - break; - - case '|': - BEGIN_EXEC(&States::start); - /* skip to ELSE-part or end of conditional */ - undo.push_var<Mode>(mode); - mode = MODE_PARSE_ONLY_COND; - break; - - default: - throw SyntaxError(chr); - } - - return &States::start; -} - -StateCondCommand::StateCondCommand() : State() -{ - transitions['\0'] = this; -} - -State * -StateCondCommand::custom(gchar chr) throw (Error) -{ - gint64 value = 0; - bool result; - - switch (mode) { - case MODE_PARSE_ONLY_COND: - undo.push_var<gint>(nest_level); - nest_level++; - break; - case MODE_NORMAL: - value = expressions.pop_num_calc(); - break; - default: - break; - } - - switch (g_ascii_toupper(chr)) { - case 'A': - BEGIN_EXEC(&States::start); - result = g_ascii_isalpha((gchar)value); - break; - case 'C': - BEGIN_EXEC(&States::start); - result = g_ascii_isalnum((gchar)value) || - value == '.' || value == '$' || value == '_'; - break; - case 'D': - BEGIN_EXEC(&States::start); - result = g_ascii_isdigit((gchar)value); - break; - case 'E': - case 'F': - case 'U': - case '=': - BEGIN_EXEC(&States::start); - result = value == 0; - break; - case 'G': - case '>': - BEGIN_EXEC(&States::start); - result = value > 0; - break; - case 'L': - case 'S': - case 'T': - case '<': - BEGIN_EXEC(&States::start); - result = value < 0; - break; - case 'N': - BEGIN_EXEC(&States::start); - result = value != 0; - break; - case 'R': - BEGIN_EXEC(&States::start); - result = g_ascii_isalnum((gchar)value); - break; - case 'V': - BEGIN_EXEC(&States::start); - result = g_ascii_islower((gchar)value); - break; - case 'W': - BEGIN_EXEC(&States::start); - result = g_ascii_isupper((gchar)value); - break; - default: - throw Error("Invalid conditional type \"%c\"", chr); - } - - if (!result) { - /* skip to ELSE-part or end of conditional */ - undo.push_var<Mode>(mode); - mode = MODE_PARSE_ONLY_COND; - } - - return &States::start; -} - -StateControl::StateControl() : State() -{ - transitions['\0'] = this; - transitions['U'] = &States::ctlucommand; -} - -State * -StateControl::custom(gchar chr) throw (Error) -{ - switch (g_ascii_toupper(chr)) { - case 'O': - BEGIN_EXEC(&States::start); - expressions.set_radix(8); - break; - - case 'D': - BEGIN_EXEC(&States::start); - expressions.set_radix(10); - break; - - case 'R': - BEGIN_EXEC(&States::start); - expressions.eval(); - if (!expressions.args()) - expressions.push(expressions.radix); - else - expressions.set_radix(expressions.pop_num_calc()); - break; - - /* - * Alternatives: ^i, ^I, <CTRL/I>, <TAB> - */ - case 'I': - BEGIN_EXEC(&States::insert); - expressions.eval(); - expressions.push('\t'); - return &States::insert; - - /* - * Alternatives: ^[, <CTRL/[>, <ESC> - */ - case '[': - BEGIN_EXEC(&States::start); - expressions.discard_args(); - break; - - /* - * Additional numeric operations - */ - case '_': - BEGIN_EXEC(&States::start); - expressions.push(~expressions.pop_num_calc()); - break; - - case '*': - BEGIN_EXEC(&States::start); - expressions.push_calc(Expressions::OP_POW); - break; - - case '/': - BEGIN_EXEC(&States::start); - expressions.push_calc(Expressions::OP_MOD); - break; - - default: - throw Error("Unsupported command <^%c>", chr); - } - - return &States::start; -} - -StateECommand::StateECommand() : State() -{ - transitions['\0'] = this; - transitions['B'] = &States::editfile; - transitions['S'] = &States::scintilla_symbols; - transitions['Q'] = &States::eqcommand; - transitions['W'] = &States::savefile; -} - -State * -StateECommand::custom(gchar chr) throw (Error) -{ - switch (g_ascii_toupper(chr)) { - case 'F': - BEGIN_EXEC(&States::start); - if (!ring.current) - throw Error("No buffer selected"); - - if (IS_FAILURE(expressions.pop_num_calc()) && - ring.current->dirty) - throw Error("Buffer \"%s\" is dirty", - ring.current->filename ? : "(Unnamed)"); - - ring.close(); - break; - - case 'D': - BEGIN_EXEC(&States::start); - expressions.eval(); - if (!expressions.args()) { - expressions.push(Flags::ed); - } else { - gint64 on = expressions.pop_num_calc(); - gint64 off = expressions.pop_num_calc(1, ~(gint64)0); - - undo.push_var(Flags::ed); - Flags::ed = (Flags::ed & ~off) | on; - } - break; - - case 'X': - BEGIN_EXEC(&States::start); - - if (IS_FAILURE(expressions.pop_num_calc()) && - ring.is_any_dirty()) - throw Error("Modified buffers exist"); - - undo.push_var<bool>(quit_requested); - quit_requested = true; - break; - - default: - throw SyntaxError(chr); - } - - return &States::start; -} - -static struct ScintillaMessage { - unsigned int iMessage; - uptr_t wParam; - sptr_t lParam; -} scintilla_message = {0, 0, 0}; - -State * -StateScintilla_symbols::done(const gchar *str) throw (Error) -{ - BEGIN_EXEC(&States::scintilla_lparam); - - undo.push_var(scintilla_message); - if (*str) { - gchar **symbols = g_strsplit(str, ",", -1); - gint64 v; - - if (!symbols[0]) - goto cleanup; - if (*symbols[0]) { - v = Symbols::scintilla.lookup(symbols[0], "SCI_"); - if (v < 0) - throw Error("Unknown Scintilla message symbol \"%s\"", - symbols[0]); - scintilla_message.iMessage = v; - } - - if (!symbols[1]) - goto cleanup; - if (*symbols[1]) { - v = Symbols::scilexer.lookup(symbols[1]); - if (v < 0) - throw Error("Unknown Scintilla Lexer symbol \"%s\"", - symbols[1]); - scintilla_message.wParam = v; - } - - if (!symbols[2]) - goto cleanup; - if (*symbols[2]) { - v = Symbols::scilexer.lookup(symbols[2]); - if (v < 0) - throw Error("Unknown Scintilla Lexer symbol \"%s\"", - symbols[2]); - scintilla_message.lParam = v; - } - -cleanup: - g_strfreev(symbols); - } - - expressions.eval(); - if (!scintilla_message.iMessage) { - if (!expressions.args()) - throw Error("<ES> command requires at least a message code"); - - scintilla_message.iMessage = expressions.pop_num_calc(1, 0); - } - if (!scintilla_message.wParam) - scintilla_message.wParam = expressions.pop_num_calc(1, 0); - - return &States::scintilla_lparam; -} - -State * -StateScintilla_lParam::done(const gchar *str) throw (Error) -{ - BEGIN_EXEC(&States::start); - - if (!scintilla_message.lParam) - scintilla_message.lParam = *str ? (sptr_t)str - : expressions.pop_num_calc(1, 0); - - expressions.push(interface.ssm(scintilla_message.iMessage, - scintilla_message.wParam, - scintilla_message.lParam)); - - undo.push_var(scintilla_message); - memset(&scintilla_message, 0, sizeof(scintilla_message)); - - return &States::start; -} - -/* - * NOTE: cannot support VideoTECO's <n>I because - * beginning and end of strings must be determined - * syntactically - */ -void -StateInsert::initial(void) throw (Error) -{ - int args; - - expressions.eval(); - args = expressions.args(); - if (!args) - return; - - interface.ssm(SCI_BEGINUNDOACTION); - for (int i = args; i > 0; i--) { - gchar chr = (gchar)expressions.peek_num(i); - interface.ssm(SCI_ADDTEXT, 1, (sptr_t)&chr); - } - for (int i = args; i > 0; i--) - expressions.pop_num_calc(); - interface.ssm(SCI_SCROLLCARET); - interface.ssm(SCI_ENDUNDOACTION); - ring.dirtify(); - - undo.push_msg(SCI_UNDO); -} - -void -StateInsert::process(const gchar *str, gint new_chars) throw (Error) -{ - interface.ssm(SCI_BEGINUNDOACTION); - interface.ssm(SCI_ADDTEXT, new_chars, - (sptr_t)(str + strlen(str) - new_chars)); - interface.ssm(SCI_SCROLLCARET); - interface.ssm(SCI_ENDUNDOACTION); - ring.dirtify(); - - undo.push_msg(SCI_UNDO); -} - -State * -StateInsert::done(const gchar *str __attribute__((unused))) throw (Error) -{ - /* nothing to be done when done */ - return &States::start; -} |