aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/parser.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser.h')
-rw-r--r--src/parser.h845
1 files changed, 472 insertions, 373 deletions
diff --git a/src/parser.h b/src/parser.h
index 9255268..b594edc 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2012-2017 Robin Haberkorn
+ * Copyright (C) 2012-2021 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
@@ -14,47 +14,130 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
-
-#ifndef __PARSER_H
-#define __PARSER_H
-
-#include <string.h>
+#pragma once
#include <glib.h>
+#include <Scintilla.h>
+
#include "sciteco.h"
-#include "memory.h"
-#include "undo.h"
-#include "error.h"
-#include "expressions.h"
-
-namespace SciTECO {
-
-/* TECO uses only lower 7 bits for commands */
-#define MAX_TRANSITIONS 127
-
-class State : public Object {
-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);
- }
+#include "string-utils.h"
+#include "goto.h"
+#include "qreg.h"
+
+/*
+ * Forward Declarations
+ */
+typedef const struct teco_state_t teco_state_t;
+typedef struct teco_machine_t teco_machine_t;
+typedef struct teco_machine_main_t teco_machine_main_t;
+
+typedef struct {
+ /** how many iterations are left */
+ teco_int_t counter;
+ /** Program counter of loop start command */
+ guint pc : sizeof(guint)*8 - 1;
+ /**
+ * Whether the loop represents an argument
+ * barrier or not (it "passes through"
+ * stack arguments).
+ *
+ * Since the program counter is usually
+ * a signed integer, it's ok steal one
+ * bit for the pass_through flag.
+ */
+ gboolean pass_through : 1;
+} teco_loop_context_t;
+
+extern GArray *teco_loop_stack;
+
+void undo__insert_val__teco_loop_stack(guint, teco_loop_context_t);
+void undo__remove_index__teco_loop_stack(guint);
+
+/**
+ * @defgroup states Parser states
+ *
+ * Parser states are defined as global constants using the TECO_DEFINE_STATE()
+ * macro, allowing individual fields and callbacks to be overwritten.
+ * Derived macros are defined to factor out common fields and settings.
+ * States therefore form a hierarchy, which is documented using
+ * \@interface and \@implements tags.
+ *
+ * @{
+ */
-public:
- State();
+/*
+ * FIXME: Remove _cb from all callback names. See qreg.h.
+ * FIXME: Maybe use TECO_DECLARE_VTABLE_METHOD()?
+ */
+typedef const struct {
+ gboolean string_building : 1;
+ gboolean last : 1;
- static void input(gchar chr);
- State *get_next_state(gchar chr);
+ /**
+ * Called repeatedly to process chunks of input and give interactive feedback.
+ *
+ * Can be NULL if no interactive feedback is required.
+ */
+ gboolean (*process_cb)(teco_machine_main_t *ctx, const teco_string_t *str,
+ gsize new_chars, GError **error);
+
+ /**
+ * Called at the end of the string argument to determine the next state.
+ * Commands that don't give interactive feedback can use this callback
+ * to perform their main processing.
+ */
+ teco_state_t *(*done_cb)(teco_machine_main_t *ctx, const teco_string_t *str, GError **error);
+} teco_state_expectstring_t;
+
+typedef const struct {
+ teco_qreg_type_t type;
+
+ /** Called when a register specification has been successfully parsed. */
+ teco_state_t *(*got_register_cb)(teco_machine_main_t *ctx, teco_qreg_t *qreg,
+ teco_qreg_table_t *table, GError **error);
+} teco_state_expectqreg_t;
+
+typedef gboolean (*teco_state_initial_cb_t)(teco_machine_t *ctx, GError **error);
+typedef teco_state_t *(*teco_state_input_cb_t)(teco_machine_t *ctx, gchar chr, GError **error);
+typedef gboolean (*teco_state_refresh_cb_t)(teco_machine_t *ctx, GError **error);
+typedef gboolean (*teco_state_end_of_macro_cb_t)(teco_machine_t *ctx, GError **error);
+typedef gboolean (*teco_state_process_edit_cmd_cb_t)(teco_machine_t *ctx, teco_machine_t *parent_ctx,
+ gchar key, GError **error);
+
+typedef enum {
+ TECO_FNMACRO_MASK_START = (1 << 0),
+ TECO_FNMACRO_MASK_STRING = (1 << 1),
+ TECO_FNMACRO_MASK_DEFAULT = ~((1 << 2)-1)
+} teco_fnmacro_mask_t;
+
+/**
+ * A teco_machine_t state.
+ * These are declared as constants using TECO_DEFINE_STATE() and friends.
+ *
+ * @note Unless you don't want to manually "upcast" the teco_machine_t* in
+ * callback implementations, you will have to cast your callback types when initializing
+ * the teco_state_t vtables.
+ * Casting to functions of different signature is theoretically undefined behavior,
+ * but works on all major platforms including Emscripten, as long as they differ only
+ * in pointer types.
+ */
+struct teco_state_t {
+ /**
+ * Called the first time this state is entered.
+ * Theoretically, you can use teco_machine_main_transition_t instead,
+ * but this callback improves reusability.
+ *
+ * It can be NULL if not required.
+ */
+ teco_state_initial_cb_t initial_cb;
+
+ /**
+ * Get next state given an input character.
+ *
+ * This is a mandatory field.
+ */
+ teco_state_input_cb_t input_cb;
/**
* Provide interactive feedback.
@@ -63,32 +146,20 @@ public:
* immediate interactive feedback should provide that
* feedback; allowing them to optimize batch mode,
* macro and many other cases.
+ *
+ * It can be NULL if not required.
*/
- virtual void refresh(void) {}
+ teco_state_refresh_cb_t refresh_cb;
/**
* Called at the end of a macro.
* Most states/commands are not allowed to end unterminated
* at the end of a macro.
+ *
+ * It can be NULL if not required.
*/
- virtual void
- end_of_macro(void)
- {
- throw Error("Unterminated command");
- }
+ teco_state_end_of_macro_cb_t end_of_macro_cb;
-protected:
- static bool eval_colon(void);
-
- /** Get next state given an input character */
- virtual State *
- custom(gchar chr)
- {
- throw SyntaxError(chr);
- return NULL;
- }
-
-public:
/**
* Process editing command (or key press).
*
@@ -97,383 +168,411 @@ public:
* editing commands (behaviour on key press).
*
* By implementing this method, sub-states can either
- * handle a key and return or chain to the
- * parent's process_edit_cmd() implementation.
+ * handle a key and return, chain to the
+ * parent's process_edit_cmd() implementation or even
+ * to the parent state machine's handler.
*
* All implementations of this method are defined in
- * cmdline.cpp.
+ * cmdline.c.
+ *
+ * This is a mandatory field.
*/
- virtual void process_edit_cmd(gchar key);
-
- enum fnmacroMask {
- FNMACRO_MASK_START = (1 << 0),
- FNMACRO_MASK_STRING = (1 << 1),
- FNMACRO_MASK_DEFAULT = ~((1 << 2)-1)
- };
+ teco_state_process_edit_cmd_cb_t process_edit_cmd_cb;
/**
- * Get the function key macro mask this
- * state refers to.
+ * Whether this state is a start state (ie. not within any
+ * escape sequence etc.).
+ * This is separate of TECO_FNMACRO_MASK_START which is set
+ * only in the main machine's start states.
+ */
+ gboolean is_start : 1;
+ /**
+ * Function key macro mask.
+ * This is not a bitmask since it is compared with values set
+ * from TECO, so the bitorder needs to be defined.
*
- * Could also be modelled as a State member.
+ * @fixme If we intend to "forward" masks from other state machines like
+ * teco_machine_stringbuilding_t, this should probably be a callback.
*/
- virtual fnmacroMask
- get_fnmacro_mask(void) const
- {
- return FNMACRO_MASK_DEFAULT;
- }
-};
+ teco_fnmacro_mask_t fnmacro_mask : 8;
-/**
- * Base class of states with case-insenstive input.
- *
- * This is meant for states accepting command characters
- * that can possibly be case-folded.
- */
-class StateCaseInsensitive : public State {
-protected:
- /* in cmdline.cpp */
- void process_edit_cmd(gchar key);
-};
-
-template <typename Type>
-class MicroStateMachine : public Object {
-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 bool input(gchar chr, Type &result) = 0;
+ /**
+ * Additional state-dependent callbacks and settings.
+ * This wastes some bytes compared to other techniques for extending teco_state_t
+ * but this is acceptable since there is only a limited number of constant instances.
+ * The main advantage of this approach is that we can use a single
+ * TECO_DEFINE_STATE() for defining and deriving all defaults.
+ */
+ union {
+ teco_state_expectstring_t expectstring;
+ teco_state_expectqreg_t expectqreg;
+ };
};
-/* avoid circular dependency on qregisters.h */
-class QRegSpecMachine;
-
-class StringBuildingMachine : public MicroStateMachine<gchar *> {
- enum Mode {
- MODE_NORMAL,
- MODE_UPPER,
- MODE_LOWER
- } mode;
-
- bool toctl;
-
-public:
- QRegSpecMachine *qregspec_machine;
+/** @} */
- StringBuildingMachine() : MicroStateMachine<gchar *>(),
- mode(MODE_NORMAL), toctl(false),
- qregspec_machine(NULL) {}
- ~StringBuildingMachine();
+gboolean teco_state_end_of_macro(teco_machine_t *ctx, GError **error);
- void reset(void);
+/* in cmdline.c */
+gboolean teco_state_process_edit_cmd(teco_machine_t *ctx, teco_machine_t *parent_ctx, gchar chr, GError **error);
- bool input(gchar chr, gchar *&result);
-};
-
-/*
- * Super-class for states accepting string arguments
- * Opaquely cares about alternative-escape characters,
- * string building commands and accumulation into a string
+/**
+ * @interface TECO_DEFINE_STATE
+ * @implements teco_state_t
+ * @ingroup states
+ *
+ * @todo Should we eliminate required callbacks, this could be turned into a
+ * struct initializer TECO_INIT_STATE() and TECO_DECLARE_STATE() would become pointless.
+ * This would also ease declaring static states.
*/
-class StateExpectString : public State {
- gsize insert_len;
-
- gint nesting;
-
- bool string_building;
- bool last;
-
- StringBuildingMachine machine;
-
-public:
- StateExpectString(bool _building = true, bool _last = true)
- : insert_len(0), nesting(1),
- string_building(_building), last(_last) {}
-
-private:
- State *custom(gchar chr);
- void refresh(void);
-
- virtual fnmacroMask
- get_fnmacro_mask(void) const
- {
- return FNMACRO_MASK_STRING;
+#define TECO_DEFINE_STATE(NAME, ...) \
+ /** @ingroup states */ \
+ teco_state_t NAME = { \
+ .initial_cb = NULL, /* do nothing */ \
+ .input_cb = (teco_state_input_cb_t)NAME##_input, /* always required */ \
+ .refresh_cb = NULL, /* do nothing */ \
+ .end_of_macro_cb = teco_state_end_of_macro, \
+ .process_edit_cmd_cb = teco_state_process_edit_cmd, \
+ .is_start = FALSE, \
+ .fnmacro_mask = TECO_FNMACRO_MASK_DEFAULT, \
+ ##__VA_ARGS__ \
}
-protected:
- virtual void initial(void) {}
- virtual void process(const gchar *str, gint new_chars) {}
- virtual State *done(const gchar *str) = 0;
-
- /* in cmdline.cpp */
- void process_edit_cmd(gchar key);
-};
-
-class StateExpectFile : public StateExpectString {
-public:
- StateExpectFile(bool _building = true, bool _last = true)
- : StateExpectString(_building, _last) {}
-
-private:
- State *done(const gchar *str);
-
-protected:
- virtual State *got_file(const gchar *filename) = 0;
+/** @ingroup states */
+#define TECO_DECLARE_STATE(NAME) \
+ extern teco_state_t NAME
- /* in cmdline.cpp */
- void process_edit_cmd(gchar key);
-};
+/* in cmdline.c */
+gboolean teco_state_caseinsensitive_process_edit_cmd(teco_machine_t *ctx, teco_machine_t *parent_ctx, gchar chr, GError **error);
-class StateExpectDir : public StateExpectFile {
-public:
- StateExpectDir(bool _building = true, bool _last = true)
- : StateExpectFile(_building, _last) {}
+/**
+ * @interface TECO_DEFINE_STATE_CASEINSENSITIVE
+ * @implements TECO_DEFINE_STATE
+ * @ingroup states
+ *
+ * Base class of states with case-insenstive input.
+ *
+ * This is meant for states accepting command characters
+ * that can possibly be case-folded.
+ */
+#define TECO_DEFINE_STATE_CASEINSENSITIVE(NAME, ...) \
+ TECO_DEFINE_STATE(NAME, \
+ .process_edit_cmd_cb = teco_state_caseinsensitive_process_edit_cmd, \
+ ##__VA_ARGS__ \
+ )
-protected:
- /* in cmdline.cpp */
- void process_edit_cmd(gchar key);
+/**
+ * Base class of state machine.
+ *
+ * @note On extending teco_machine_t:
+ * There is `-fplan9-extensions`, but Clang doesn't support it.
+ * There is `-fms-extensions`, but that would require type-unsafe
+ * casting to teco_machine_t*.
+ * It's possible to portably implement typesafe inheritance by using
+ * an anonymous union of an anonymous struct and a named struct, but it's
+ * not really worth the trouble in our flat "class" hierachy.
+ */
+struct teco_machine_t {
+ teco_state_t *current;
+ /**
+ * Whether side effects must be reverted on rubout.
+ * State machines created within macro calls don't have to
+ * even in interactive mode.
+ */
+ gboolean must_undo;
};
-class StateStart : public StateCaseInsensitive {
-public:
- StateStart();
+static inline void
+teco_machine_init(teco_machine_t *ctx, teco_state_t *initial, gboolean must_undo)
+{
+ ctx->current = initial;
+ ctx->must_undo = must_undo;
+}
-private:
- void insert_integer(tecoInt v);
- tecoInt read_integer(void);
+static inline void
+teco_machine_reset(teco_machine_t *ctx, teco_state_t *initial)
+{
+ if (ctx->current != initial)
+ teco_undo_ptr(ctx->current) = initial;
+}
- tecoBool move_chars(tecoInt n);
- tecoBool move_lines(tecoInt n);
+gboolean teco_machine_input(teco_machine_t *ctx, gchar chr, GError **error);
- tecoBool delete_words(tecoInt n);
+typedef enum {
+ TECO_STRINGBUILDING_MODE_NORMAL = 0,
+ TECO_STRINGBUILDING_MODE_UPPER,
+ TECO_STRINGBUILDING_MODE_LOWER
+} teco_stringbuilding_mode_t;
- State *custom(gchar chr);
+/**
+ * A stringbuilding state machine.
+ *
+ * @fixme Should contain the escape char (currently in teco_machine_expectstring_t),
+ * so that we can escape it via ^Q.
+ *
+ * @extends teco_machine_t
+ */
+typedef struct teco_machine_stringbuilding_t {
+ teco_machine_t parent;
- void end_of_macro(void) {}
+ /**
+ * A teco_stringbuilding_mode_t.
+ * This is still a guint, so you can call teco_undo_guint().
+ */
+ guint mode;
- fnmacroMask
- get_fnmacro_mask(void) const
- {
- return FNMACRO_MASK_START;
- }
-};
+ /**
+ * The escape/termination character.
+ *
+ * If this is `[` or `{`, it is assumed that `]` and `}` must
+ * be escaped as well by teco_machine_stringbuilding_escape().
+ */
+ gchar escape_char;
-class StateControl : public StateCaseInsensitive {
-public:
- StateControl();
+ /**
+ * Q-Register table for local registers.
+ * This is stored here only to be passed to the Q-Reg spec machine.
+ */
+ teco_qreg_table_t *qreg_table_locals;
-private:
- State *custom(gchar chr);
-};
+ /**
+ * A QRegister specification parser.
+ * It is allocated since it in turn contains a string building machine.
+ */
+ teco_machine_qregspec_t *machine_qregspec;
-class StateASCII : public State {
-public:
- StateASCII();
+ /**
+ * A string to append characters to or NULL in parse-only mode.
+ *
+ * @bug As a side-effect, rubbing out in parse-only mode is severely limited
+ * (see teco_state_stringbuilding_start_process_edit_cmd()).
+ */
+ teco_string_t *result;
+} teco_machine_stringbuilding_t;
-private:
- State *custom(gchar chr);
-};
+void teco_machine_stringbuilding_init(teco_machine_stringbuilding_t *ctx, gchar escape_char,
+ teco_qreg_table_t *locals, gboolean must_undo);
-class StateEscape : public StateCaseInsensitive {
-public:
- StateEscape();
+void teco_machine_stringbuilding_reset(teco_machine_stringbuilding_t *ctx);
-private:
- State *custom(gchar chr);
+/**
+ * Parse a string building character.
+ *
+ * @param ctx The string building machine.
+ * @param chr The character to parse.
+ * @param result String to append characters to or NULL in parse-only mode.
+ * @param error GError.
+ * @return FALSE in case of error.
+ */
+static inline gboolean
+teco_machine_stringbuilding_input(teco_machine_stringbuilding_t *ctx, gchar chr,
+ teco_string_t *result, GError **error)
+{
+ ctx->result = result;
+ return teco_machine_input(&ctx->parent, chr, error);
+}
- void end_of_macro(void);
+void teco_machine_stringbuilding_escape(teco_machine_stringbuilding_t *ctx, const gchar *str, gsize len,
+ teco_string_t *target);
- /*
- * The state should behave like StateStart
- * when it comes to function key macro masking.
- */
- fnmacroMask
- get_fnmacro_mask(void) const
- {
- return FNMACRO_MASK_START;
- }
-};
+void teco_machine_stringbuilding_clear(teco_machine_stringbuilding_t *ctx);
-class StateFCommand : public StateCaseInsensitive {
-public:
- StateFCommand();
+/**
+ * Peristent state for teco_state_expectstring_input().
+ *
+ * This is part of the main machine instead of being a global variable,
+ * so that parsers can be run in parallel.
+ *
+ * Since it will also be part of a macro invocation frame, it will allow
+ * for tricks like macro-hooks while in "expectstring" states or calling
+ * macros as part of string building characters or macro string arguments.
+ */
+typedef struct {
+ teco_string_t string;
+ gsize insert_len;
+ gint nesting;
-private:
- State *custom(gchar chr);
-};
+ teco_machine_stringbuilding_t machine;
+} teco_machine_expectstring_t;
-class UndoTokenChangeDir : public UndoToken {
- gchar *dir;
+/**
+ * Scintilla message for collection by ES commands.
+ *
+ * @fixme This is a "forward" declaration, so that we don't introduce cyclic
+ * header dependencies.
+ * Could presumably be avoided by splitting parser.h in two.
+ */
+typedef struct {
+ unsigned int iMessage;
+ uptr_t wParam;
+ sptr_t lParam;
+} teco_machine_scintilla_t;
+
+typedef enum {
+ /** Normal parsing - ie. execute while parsing */
+ TECO_MODE_NORMAL = 0,
+ /** Parse, but don't execute until reaching not-yet-defined Goto-label */
+ TECO_MODE_PARSE_ONLY_GOTO,
+ /** Parse, but don't execute until reaching end of loop */
+ TECO_MODE_PARSE_ONLY_LOOP,
+ /** Parse, but don't execute until reaching end of conditional or its else-clause */
+ TECO_MODE_PARSE_ONLY_COND,
+ /** Parse, but don't execute until reaching the very end of conditional */
+ TECO_MODE_PARSE_ONLY_COND_FORCE
+} teco_mode_t;
+
+/** @extends teco_machine_t */
+struct teco_machine_main_t {
+ teco_machine_t parent;
+
+ gint macro_pc;
-public:
/**
- * Construct undo token.
- *
- * This passes ownership of the directory string
- * to the undo token object.
+ * Aliases bitfield with an integer.
+ * This allows teco_undo_guint(__flags),
+ * while still supporting easy-to-access flags.
*/
- UndoTokenChangeDir(gchar *_dir) : dir(_dir) {}
- ~UndoTokenChangeDir()
- {
- g_free(dir);
- }
-
- void run(void);
-};
+ union {
+ struct {
+ teco_mode_t mode : 8;
+
+ gboolean modifier_colon : 1;
+ gboolean modifier_at : 1;
+ };
+ guint __flags;
+ };
-class StateChangeDir : public StateExpectDir {
-private:
- State *got_file(const gchar *filename);
-};
+ /** The nesting level of braces */
+ guint brace_level;
+ /** The nesting level of loops and control structures */
+ gint nest_level;
+ /**
+ * Loop frame pointer: The number of elements on
+ * the loop stack when a macro invocation frame is
+ * created.
+ * This is used to perform checks for flow control
+ * commands to avoid jumping with invalid PCs while
+ * not creating a new stack per macro frame.
+ */
+ guint loop_stack_fp;
-class StateCondCommand : public StateCaseInsensitive {
-public:
- StateCondCommand();
+ teco_goto_table_t goto_table;
+ teco_qreg_table_t *qreg_table_locals;
-private:
- State *custom(gchar chr);
+ /*
+ * teco_state_t-dependent state.
+ *
+ * Some of these cannot be used concurrently and are therefore
+ * grouped into unions.
+ * We could further optimize memory usage by dynamically allocating
+ * some of these structures on demand.
+ */
+ teco_machine_expectstring_t expectstring;
+ union {
+ teco_string_t goto_label;
+ teco_machine_qregspec_t *expectqreg;
+ teco_machine_scintilla_t scintilla;
+ };
};
-class StateECommand : public StateCaseInsensitive {
-public:
- StateECommand();
-
-private:
- State *custom(gchar chr);
-};
+void teco_machine_main_init(teco_machine_main_t *ctx,
+ teco_qreg_table_t *qreg_table_locals,
+ gboolean must_undo);
-class StateScintilla_symbols : public StateExpectString {
-public:
- StateScintilla_symbols() : StateExpectString(true, false) {}
+gboolean teco_machine_main_eval_colon(teco_machine_main_t *ctx);
-private:
- State *done(const gchar *str);
+gboolean teco_machine_main_step(teco_machine_main_t *ctx,
+ const gchar *macro, gint stop_pos, GError **error);
-protected:
- /* in cmdline.cpp */
- void process_edit_cmd(gchar key);
-};
+gboolean teco_execute_macro(const gchar *macro, gsize macro_len,
+ teco_qreg_table_t *qreg_table_locals, GError **error);
+gboolean teco_execute_file(const gchar *filename, teco_qreg_table_t *qreg_table_locals, GError **error);
-class StateScintilla_lParam : public StateExpectString {
-private:
- State *done(const gchar *str);
-};
+typedef const struct {
+ teco_state_t *next;
+ void (*transition_cb)(teco_machine_main_t *ctx, GError **error);
+} teco_machine_main_transition_t;
/*
- * also serves as base class for replace-insertion states
+ * FIXME: There should probably be a teco_state_plain with
+ * the transitions and their length being stored in
+ * teco_state_t::transitions.
+ * This does not exclude the possibility of overwriting input_cb.
*/
-class StateInsert : public StateExpectString {
-public:
- StateInsert(bool building = true)
- : StateExpectString(building) {}
-
-protected:
- void initial(void);
- void process(const gchar *str, gint new_chars);
- State *done(const gchar *str);
-
- /* in cmdline.cpp */
- void process_edit_cmd(gchar key);
-};
-
-class StateInsertIndent : public StateInsert {
-protected:
- void initial(void);
-};
-
-namespace States {
- extern StateStart start;
- extern StateControl control;
- extern StateASCII ascii;
- extern StateEscape escape;
- extern StateFCommand fcommand;
- extern StateChangeDir changedir;
- extern StateCondCommand condcommand;
- extern StateECommand ecommand;
- extern StateScintilla_symbols scintilla_symbols;
- extern StateScintilla_lParam scintilla_lparam;
- extern StateInsert insert_building;
- extern StateInsert insert_nobuilding;
- extern StateInsertIndent insert_indent;
-
- extern State *current;
-
- static inline bool
- is_start(void)
- {
- /*
- * StateEscape should behave very much like StateStart.
- */
- return current == &start || current == &escape;
- }
-}
+teco_state_t *teco_machine_main_transition_input(teco_machine_main_t *ctx,
+ teco_machine_main_transition_t *transitions,
+ guint len, gchar chr, GError **error);
-extern enum Mode {
- MODE_NORMAL = 0,
- MODE_PARSE_ONLY_GOTO,
- MODE_PARSE_ONLY_LOOP,
- MODE_PARSE_ONLY_COND
-} mode;
+void teco_machine_main_clear(teco_machine_main_t *ctx);
-#define BEGIN_EXEC(STATE) G_STMT_START { \
- if (mode > MODE_NORMAL) \
- return STATE; \
-} G_STMT_END
+G_DEFINE_AUTO_CLEANUP_CLEAR_FUNC(teco_machine_main_t, teco_machine_main_clear);
-extern gint macro_pc;
+teco_state_t *teco_state_expectstring_input(teco_machine_main_t *ctx, gchar chr, GError **error);
+gboolean teco_state_expectstring_refresh(teco_machine_main_t *ctx, GError **error);
-extern gchar *strings[2];
-extern gchar escape_char;
+/* in cmdline.c */
+gboolean teco_state_expectstring_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gchar key, GError **error);
-struct LoopContext {
- /** how many iterations are left */
- tecoInt counter;
- /** Program counter of loop start command */
- guint pc : sizeof(guint)*8 - 1;
- /**
- * Whether the loop represents an argument
- * barrier or not (it "passes through"
- * stack arguments).
- *
- * Since the program counter is usually
- * a signed integer, it's ok steal one
- * bit for the pass_through flag.
- */
- bool pass_through : 1;
-};
-typedef ValueStack<LoopContext> LoopStack;
-extern LoopStack loop_stack;
+/**
+ * @interface TECO_DEFINE_STATE_EXPECTSTRING
+ * @implements TECO_DEFINE_STATE
+ * @ingroup states
+ *
+ * Super-class for states accepting string arguments
+ * Opaquely cares about alternative-escape characters,
+ * string building commands and accumulation into a string
+ *
+ * @note Generating the input_cb could be avoided if there were a default
+ * implementation.
+ */
+#define TECO_DEFINE_STATE_EXPECTSTRING(NAME, ...) \
+ static teco_state_t * \
+ NAME##_input(teco_machine_main_t *ctx, gchar chr, GError **error) \
+ { \
+ return teco_state_expectstring_input(ctx, chr, error); \
+ } \
+ TECO_DEFINE_STATE(NAME, \
+ .refresh_cb = (teco_state_refresh_cb_t)teco_state_expectstring_refresh, \
+ .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) \
+ teco_state_expectstring_process_edit_cmd, \
+ .fnmacro_mask = TECO_FNMACRO_MASK_STRING, \
+ .expectstring.string_building = TRUE, \
+ .expectstring.last = TRUE, \
+ .expectstring.process_cb = NULL, /* do nothing */ \
+ .expectstring.done_cb = NAME##_done, /* always required */ \
+ ##__VA_ARGS__ \
+ )
+
+gboolean teco_state_expectfile_process(teco_machine_main_t *ctx, const teco_string_t *str,
+ gsize new_chars, GError **error);
+
+/* in cmdline.c */
+gboolean teco_state_expectfile_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gchar key, GError **error);
-namespace Execute {
- void step(const gchar *macro, gint stop_pos);
- void macro(const gchar *macro, bool locals = true);
- void file(const gchar *filename, bool locals = true);
-}
+/**
+ * @interface TECO_DEFINE_STATE_EXPECTFILE
+ * @implements TECO_DEFINE_STATE_EXPECTSTRING
+ * @ingroup states
+ */
+#define TECO_DEFINE_STATE_EXPECTFILE(NAME, ...) \
+ TECO_DEFINE_STATE_EXPECTSTRING(NAME, \
+ .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) \
+ teco_state_expectfile_process_edit_cmd, \
+ .expectstring.process_cb = teco_state_expectfile_process, \
+ ##__VA_ARGS__ \
+ )
-} /* namespace SciTECO */
+/* in cmdline.c */
+gboolean teco_state_expectdir_process_edit_cmd(teco_machine_main_t *ctx, teco_machine_t *parent_ctx, gchar key, GError **error);
-#endif
+/**
+ * @interface TECO_DEFINE_STATE_EXPECTDIR
+ * @implements TECO_DEFINE_STATE_EXPECTFILE
+ * @ingroup states
+ */
+#define TECO_DEFINE_STATE_EXPECTDIR(NAME, ...) \
+ TECO_DEFINE_STATE_EXPECTFILE(NAME, \
+ .process_edit_cmd_cb = (teco_state_process_edit_cmd_cb_t) \
+ teco_state_expectdir_process_edit_cmd, \
+ ##__VA_ARGS__ \
+ )