/* * Copyright (C) 2012-2013 Robin Haberkorn * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #ifndef __PARSER_H #define __PARSER_H #include #include #include "undo.h" #include "sciteco.h" /* TECO uses only lower 7 bits for commands */ #define MAX_TRANSITIONS 127 /* thrown as exception, executed at cmdline macro level */ class ReplaceCmdline { public: gchar *new_cmdline; gint pos; ReplaceCmdline(); }; class State { public: class Error { public: Error(const gchar *fmt, ...) G_GNUC_PRINTF(2, 3); }; class SyntaxError : public Error { public: SyntaxError(gchar chr) : Error("Syntax error \"%c\" (%d)", chr, chr) {} }; class MoveError : public Error { public: MoveError(const gchar *cmd) : Error("Attempt to move pointer off page with <%s>", cmd) {} MoveError(gchar cmd) : Error("Attempt to move pointer off page with <%c>", cmd) {} }; class RangeError : public Error { public: RangeError(const gchar *cmd) : Error("Invalid range specified for <%s>", cmd) {} RangeError(gchar cmd) : Error("Invalid range specified for <%c>", cmd) {} }; class InvalidQRegError : public Error { public: InvalidQRegError(const gchar *name, bool local = false) : Error("Invalid Q-Register \"%s%s\"", local ? "." : "", name) {} InvalidQRegError(gchar name, bool local = false) : Error("Invalid Q-Register \"%s%c\"", local ? "." : "", name) {} }; protected: /* static transitions */ State *transitions[MAX_TRANSITIONS]; inline void init(const gchar *chars, State &state) { while (*chars) transitions[(int)*chars++] = &state; } inline void init(const gchar *chars) { init(chars, *this); } public: State(); static void input(gchar chr) throw (Error, ReplaceCmdline); State *get_next_state(gchar chr) throw (Error, ReplaceCmdline); protected: static bool eval_colon(void); virtual State * custom(gchar chr) throw (Error, ReplaceCmdline) { throw SyntaxError(chr); return NULL; } }; template class MicroStateMachine { protected: /* label pointers */ typedef const void *MicroState; const MicroState StateStart; #define MICROSTATE_START G_STMT_START { \ if (this->state != StateStart) \ goto *this->state; \ } G_STMT_END MicroState state; inline void set(MicroState next) { if (next != state) undo.push_var(state) = next; } public: MicroStateMachine() : StateStart(NULL), state(StateStart) {} virtual ~MicroStateMachine() {} virtual inline void reset(void) { set(StateStart); } virtual Type input(gchar chr) throw (State::Error) = 0; }; /* avoid circular dependency on qregisters.h */ class QRegSpecMachine; class StringBuildingMachine : public MicroStateMachine { QRegSpecMachine *qregspec_machine; enum Mode { MODE_NORMAL, MODE_UPPER, MODE_LOWER } mode; bool toctl; public: StringBuildingMachine() : MicroStateMachine(), qregspec_machine(NULL), mode(MODE_NORMAL), toctl(false) {} ~StringBuildingMachine(); void reset(void); gchar *input(gchar chr) throw (State::Error); }; /* * Super-class for states accepting string arguments * Opaquely cares about alternative-escape characters, * string building commands and accumulation into a string */ class StateExpectString : public State { StringBuildingMachine machine; gint nesting; bool string_building; bool last; public: StateExpectString(bool _building = true, bool _last = true) : State(), nesting(1), string_building(_building), last(_last) {} private: State *custom(gchar chr) throw (Error); 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; }; class StateExpectFile : public StateExpectString { public: StateExpectFile(bool _building = true, bool _last = true) : StateExpectString(_building, _last) {} }; class StateStart : public State { public: StateStart(); private: void insert_integer(tecoInt v); tecoInt read_integer(void); tecoBool move_chars(tecoInt n); tecoBool move_lines(tecoInt n); tecoBool delete_words(tecoInt n); State *custom(gchar chr) throw (Error, ReplaceCmdline); }; class StateControl : public State { public: StateControl(); private: State *custom(gchar chr) throw (Error); }; class StateASCII : public State { public: StateASCII(); private: State *custom(gchar chr) throw (Error); }; class StateFCommand : public State { public: StateFCommand(); private: State *custom(gchar chr) throw (Error); }; class StateCondCommand : public State { public: StateCondCommand(); private: State *custom(gchar chr) throw (Error); }; class StateECommand : public State { public: StateECommand(); private: State *custom(gchar chr) throw (Error); }; class StateScintilla_symbols : public StateExpectString { public: StateScintilla_symbols() : StateExpectString(true, false) {} private: State *done(const gchar *str) throw (Error); }; class StateScintilla_lParam : public StateExpectString { private: State *done(const gchar *str) throw (Error); }; /* * also serves as base class for replace-insertion states */ class StateInsert : public StateExpectString { protected: void initial(void) throw (Error); void process(const gchar *str, gint new_chars) throw (Error); State *done(const gchar *str) throw (Error); }; namespace States { extern StateStart start; extern StateControl control; extern StateASCII ascii; extern StateFCommand fcommand; extern StateCondCommand condcommand; extern StateECommand ecommand; extern StateScintilla_symbols scintilla_symbols; extern StateScintilla_lParam scintilla_lparam; extern StateInsert insert; extern State *current; static inline bool is_string() { return dynamic_cast(current); } static inline bool is_file() { return dynamic_cast(current); } } extern enum Mode { MODE_NORMAL = 0, MODE_PARSE_ONLY_GOTO, MODE_PARSE_ONLY_LOOP, MODE_PARSE_ONLY_COND } mode; #define BEGIN_EXEC(STATE) G_STMT_START { \ if (mode > MODE_NORMAL) \ return STATE; \ } G_STMT_END extern gint macro_pc; extern gchar *strings[2]; extern gchar escape_char; namespace Execute { void step(const gchar *macro, gint stop_pos) throw (State::Error, ReplaceCmdline); void macro(const gchar *macro, bool locals = true) throw (State::Error, ReplaceCmdline); bool file(const gchar *filename, bool locals = true); } #endif