aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/parser.h
diff options
context:
space:
mode:
Diffstat (limited to 'src/parser.h')
-rw-r--r--src/parser.h70
1 files changed, 70 insertions, 0 deletions
diff --git a/src/parser.h b/src/parser.h
index 0c389cc..75a3432 100644
--- a/src/parser.h
+++ b/src/parser.h
@@ -25,6 +25,7 @@
#include "goto.h"
#include "undo.h"
#include "qreg.h"
+#include "memory.h"
#include "lexer.h"
/*
@@ -481,8 +482,12 @@ typedef enum {
struct teco_machine_main_t {
teco_machine_t parent;
+ /** currently executed macro */
+ const gchar *macro;
/** Program counter, i.e. pointer to the next character in the current macro frame */
gsize macro_pc;
+ /** possibly preliminary end of macro */
+ gsize stop_pos;
struct teco_machine_main_flags_t {
teco_mode_t mode : 8;
@@ -550,6 +555,71 @@ gboolean teco_machine_main_eval_at(teco_machine_main_t *ctx);
gboolean teco_machine_main_step(teco_machine_main_t *ctx,
const gchar *macro, gsize stop_pos, GError **error);
+#if __has_attribute(musttail)
+
+/**
+ * Transition from a teco_machine_main input_cb() to another state.
+ *
+ * Instead of returning to the caller, this can tail call,
+ * which will often be optimized to direct jumps, turning
+ * state transitions into "threaded" code.
+ * In some macros this achieves up to 18% speedup.
+ *
+ * On the other hand, this works only from functions with
+ * compatible signatures (practically only from
+ * teco_state_t::input_cb()).
+ * When this optimization is used, other function calls
+ * may no longer be tail-call optimized.
+ *
+ * @see teco_machine_main_step
+ * @see teco_machine_input
+ */
+#define TECO_RETURN(CTX, STATE, ERROR) G_STMT_START { \
+ teco_machine_main_t *const __ctx = (CTX); \
+ teco_state_t *const __state = (STATE); \
+ GError **const __error = (ERROR); \
+ \
+ if (!__state) \
+ return NULL; \
+ \
+ if (G_UNLIKELY(teco_interface_is_interrupted())) { \
+ teco_error_interrupted_set(__error); \
+ return NULL; \
+ } \
+ \
+ if (!teco_memory_check(0, __error)) \
+ return NULL; \
+ \
+ if (__ctx->macro_pc >= __ctx->stop_pos) \
+ return __state; /* terminate tail calls */ \
+ \
+ if (__state != __ctx->parent.current) { \
+ if (__ctx->parent.must_undo) \
+ teco_undo_ptr(__ctx->parent.current); \
+ __ctx->parent.current = __state; \
+ \
+ if (__ctx->parent.current->initial_cb && \
+ !__ctx->parent.current->initial_cb(&__ctx->parent, __error)) \
+ return NULL; \
+ } \
+ \
+ gunichar chr = g_utf8_get_char(__ctx->macro+__ctx->macro_pc); \
+ __ctx->macro_pc = g_utf8_next_char(__ctx->macro+__ctx->macro_pc) - __ctx->macro; \
+ \
+ /* the caller's signature will not line up exactly with input_cb */ \
+ typedef teco_state_t *(*caller_t)(teco_machine_main_t *, gunichar, GError **); \
+ __attribute__((musttail)) return ((caller_t)__state->input_cb)(__ctx, chr, __error); \
+} G_STMT_END
+
+#else /* __has_attribute(musttail) */
+
+/*
+ * This will effectively return into the teco_machine_main_step() loop.
+ */
+#define TECO_RETURN(CTX, STATE, ERROR) return (STATE)
+
+#endif /* !__has_attribute(musttail) */
+
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);