From 94852f59c35d9dba0f2fde2d95f8b484997c3007 Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Thu, 29 Jan 2026 15:02:42 +0100 Subject: [PATCH 01/26] Fix compilation (bad conflict resolution) --- right/src/usb_report_updater.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/right/src/usb_report_updater.c b/right/src/usb_report_updater.c index d198d2d96..24e94ba67 100644 --- a/right/src/usb_report_updater.c +++ b/right/src/usb_report_updater.c @@ -469,13 +469,13 @@ void ApplyKeyAction(key_state_t *keyState, key_action_cached_t *cachedAction, ke case KeyActionType_PlayMacro: if (KeyState_ActivatedNow(keyState)) { resetStickyMods(cachedAction); - Macros_StartMacro(action->playMacro.macroId, keyState, action->playMacro.offset, keyState->activationTimestamp, 255, true); + Macros_StartMacro(action->playMacro.macroId, keyState, action->playMacro.offset, keyState->activationTimestamp, 255, true, NULL); } break; case KeyActionType_InlineMacro: if (KeyState_ActivatedNow(keyState)) { resetStickyMods(cachedAction); - Macros_StartInlineMacro(action->inlineMacro.text, keyState, keyState->timestamp); + Macros_StartInlineMacro(action->inlineMacro.text, keyState, keyState->activationTimestamp); } break; case KeyActionType_Connections: From 1a608186a2bcc910c4daa11ecef08e79d0d6676f Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Sun, 1 Feb 2026 14:49:28 +0100 Subject: [PATCH 02/26] add macroArg command (ignored for now) --- right/src/macros/command_ids.h | 1 + right/src/macros/commands.c | 15 +++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/right/src/macros/command_ids.h b/right/src/macros/command_ids.h index 038eb7cc5..37ef03908 100644 --- a/right/src/macros/command_ids.h +++ b/right/src/macros/command_ids.h @@ -107,6 +107,7 @@ typedef enum { CommandId_ifRegLt, // deprecated // 'm' commands + CommandId_macroArg, CommandId_mulReg, // deprecated // 'n' commands diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index 7fbf5ea70..a356a1063 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -945,6 +945,19 @@ static macro_result_t processPlayMacroCommand(parser_context_t* ctx) return res ? MacroResult_Blocking : MacroResult_Finished; } +static macro_result_t processMacroArgCommand(uint32_t time) +{ + // ignore macroArg command for now, eat rest of line + + uint16_t stringOffset = 0; + uint16_t textIndex = 0; + uint16_t textSubIndex = 0; + + while (Macros_ConsumeCharOfString(ctx, &stringOffset, &textIndex, &textSubIndex) != '\0') {}; + + return MacroResult_Finished; +} + static macro_result_t processWriteCommand(parser_context_t* ctx) { if (Macros_DryRun) { @@ -2229,6 +2242,8 @@ static macro_result_t processCommand(parser_context_t* ctx) return MacroResult_Finished; // 'm' commands + case CommandId_macroArg: + return processMacroArgCommand(ctx); case CommandId_mulReg: Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `setVar varName ($varName*2)`."); return MacroResult_Finished; From befb7dc124b771007dd208a9571bb1a7aeb9e617 Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Tue, 3 Feb 2026 08:29:26 +0100 Subject: [PATCH 03/26] fix params --- right/src/macros/commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index a356a1063..f1913284a 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -945,7 +945,7 @@ static macro_result_t processPlayMacroCommand(parser_context_t* ctx) return res ? MacroResult_Blocking : MacroResult_Finished; } -static macro_result_t processMacroArgCommand(uint32_t time) +static macro_result_t processMacroArgCommand(parser_context_t* ctx) { // ignore macroArg command for now, eat rest of line From 79308c119b849e2347072f7928425bbbc692b090 Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Tue, 3 Feb 2026 09:05:42 +0100 Subject: [PATCH 04/26] update command hash table --- right/src/macros/command_hash.gperf | 1 + 1 file changed, 1 insertion(+) diff --git a/right/src/macros/command_hash.gperf b/right/src/macros/command_hash.gperf index 9148cbb5c..3c58a722d 100644 --- a/right/src/macros/command_hash.gperf +++ b/right/src/macros/command_hash.gperf @@ -88,6 +88,7 @@ ifRegEq, CommandId_ifRegEq ifNotRegEq, CommandId_ifNotRegEq ifRegGt, CommandId_ifRegGt ifRegLt, CommandId_ifRegLt +macroArg, CommandId_macroArg mulReg, CommandId_mulReg noOp, CommandId_noOp notify, CommandId_notify From 7c89196cc7098a0399265467b6c5b9f4ba2aeb63 Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Tue, 3 Feb 2026 14:49:52 +0100 Subject: [PATCH 05/26] attempt at macroArg parser --- right/src/macros/commands.c | 57 +++++++++++++++++++++++++++++++++++-- right/src/macros/typedefs.h | 11 ++++++- right/src/str_utils.c | 5 ++++ right/src/str_utils.h | 2 +- 4 files changed, 71 insertions(+), 4 deletions(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index f1913284a..e82ea25a2 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -947,15 +947,68 @@ static macro_result_t processPlayMacroCommand(parser_context_t* ctx) static macro_result_t processMacroArgCommand(parser_context_t* ctx) { - // ignore macroArg command for now, eat rest of line - uint16_t stringOffset = 0; uint16_t textIndex = 0; uint16_t textSubIndex = 0; + if (Macros_DryRun) { + // parse macroArg command but ignore it for now + + while (Macros_ConsumeCharOfString(ctx, &stringOffset, &textIndex, &textSubIndex) != '\0') {}; + + return MacroResult_Finished; + } + + // parse the argument name (identifier) + const char *idStart = ctx->at; + const char *idEnd = IdentifierEnd(ctx->at); + + if (idEnd == idStart) { + Macros_ReportErrorTok(ctx, "Expected identifier"); + return MacroResult_Finished; + } + ctx->at = idEnd; + + // see if the argument has a type + macro_arg_type_t argType; + + if (*ctx->at == ':') { + ctx->at++; + const char *typeStart = ctx->at; + + if (ConsumeToken(ctx, "int")) { + argType = MacroArgType_Int; + } + else if (ConsumeToken(ctx, "float")) { + argType = MacroArgType_Float; + } + else if (ConsumeToken(ctx, "string")) { + argType = MacroArgType_String; + } + else if (ConsumeToken(ctx, "keyid")) { + argType = MacroArgType_KeyId; + } + else if (ConsumeToken(ctx, "scancode")) { + argType = MacroArgType_ScanCode; + } + else if (ConsumeToken(ctx, "any")) { + argType = MacroArgType_Any; + } + else { + Macros_ReportErrorPos(ctx, "Unrecognized macroArg argument type:"); + } + } + else { + argType = MacroArgType_Any; + ConsumeWhite(ctx); + } + + // rest of command is descriptive label, ignored by firmware while (Macros_ConsumeCharOfString(ctx, &stringOffset, &textIndex, &textSubIndex) != '\0') {}; return MacroResult_Finished; + +// Macros_ReportErrorPrintf(ctx->at, "Parsing failed at '%s'?", OneWord(ctx)); } static macro_result_t processWriteCommand(parser_context_t* ctx) diff --git a/right/src/macros/typedefs.h b/right/src/macros/typedefs.h index 67854c16f..3fdad13fd 100644 --- a/right/src/macros/typedefs.h +++ b/right/src/macros/typedefs.h @@ -14,7 +14,6 @@ // Typedefs: - typedef enum { MacroSubAction_Tap, MacroSubAction_Press, @@ -48,6 +47,16 @@ uint8_t inputModifierMask; } macro_usb_keyboard_reports_t; + typedef enum { + MacroArgType_Any, + MacroArgType_Int, + MacroArgType_Float, + MacroArgType_Bool, + MacroArgType_String, + MacroArgType_KeyId, + MacroArgType_ScanCode + } macro_arg_type_t; + // Variables: // Functions: diff --git a/right/src/str_utils.c b/right/src/str_utils.c index 0f5ccf0b4..f59e1a098 100644 --- a/right/src/str_utils.c +++ b/right/src/str_utils.c @@ -319,6 +319,11 @@ void ConsumeAnyIdentifier(parser_context_t* ctx) consumeWhite(ctx); } +void ConsumeIdentifier(parser_context_t* ctx) +{ + ctx->at = IdentifierEnd(ctx); +} + void ConsumeUntilDot(parser_context_t* ctx) { while(*ctx->at > 32 && *ctx->at != '.' && !isEnd(ctx)) { diff --git a/right/src/str_utils.h b/right/src/str_utils.h index bc72fb3e1..ac3e4f4cf 100644 --- a/right/src/str_utils.h +++ b/right/src/str_utils.h @@ -39,7 +39,7 @@ const char* at; const char* end; uint8_t nestingLevel; - uint8_t nestingBound; // This context can't be popped bellow this bound, because it is a copy. + uint8_t nestingBound; // This context can't be popped below this bound, because it is a copy. } parser_context_t; typedef struct { From aeab45cdba686d06d496a955814e4487d027b8ca Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Tue, 3 Feb 2026 15:19:28 +0100 Subject: [PATCH 06/26] fix? debug? --- right/src/macros/commands.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index e82ea25a2..dc6e2fbad 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -951,6 +951,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) uint16_t textIndex = 0; uint16_t textSubIndex = 0; +/* if (Macros_DryRun) { // parse macroArg command but ignore it for now @@ -958,7 +959,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) return MacroResult_Finished; } - + */ // parse the argument name (identifier) const char *idStart = ctx->at; const char *idEnd = IdentifierEnd(ctx->at); @@ -969,6 +970,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) } ctx->at = idEnd; +/* // see if the argument has a type macro_arg_type_t argType; @@ -996,13 +998,14 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) } else { Macros_ReportErrorPos(ctx, "Unrecognized macroArg argument type:"); + return MacroResult_Finished; } } else { argType = MacroArgType_Any; ConsumeWhite(ctx); } - + */ // rest of command is descriptive label, ignored by firmware while (Macros_ConsumeCharOfString(ctx, &stringOffset, &textIndex, &textSubIndex) != '\0') {}; From 1a8800db00afc57139138b2a3ec92b5a1dee658d Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Tue, 3 Feb 2026 15:25:55 +0100 Subject: [PATCH 07/26] should have read compiler warnings that already told me where I was wrong --- right/src/macros/commands.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index dc6e2fbad..6bddb1672 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -951,7 +951,6 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) uint16_t textIndex = 0; uint16_t textSubIndex = 0; -/* if (Macros_DryRun) { // parse macroArg command but ignore it for now @@ -959,10 +958,9 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) return MacroResult_Finished; } - */ // parse the argument name (identifier) const char *idStart = ctx->at; - const char *idEnd = IdentifierEnd(ctx->at); + const char *idEnd = IdentifierEnd(ctx); if (idEnd == idStart) { Macros_ReportErrorTok(ctx, "Expected identifier"); @@ -970,7 +968,6 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) } ctx->at = idEnd; -/* // see if the argument has a type macro_arg_type_t argType; @@ -1005,7 +1002,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) argType = MacroArgType_Any; ConsumeWhite(ctx); } - */ + // rest of command is descriptive label, ignored by firmware while (Macros_ConsumeCharOfString(ctx, &stringOffset, &textIndex, &textSubIndex) != '\0') {}; From 4a944e1c9066202c6195429c6319a5449293c028 Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Tue, 3 Feb 2026 15:55:53 +0100 Subject: [PATCH 08/26] improved error messages --- right/src/macros/commands.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index 6bddb1672..4ba525c13 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -963,7 +963,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) const char *idEnd = IdentifierEnd(ctx); if (idEnd == idStart) { - Macros_ReportErrorTok(ctx, "Expected identifier"); + Macros_ReportErrorPos(ctx, "Expected identifier"); return MacroResult_Finished; } ctx->at = idEnd; @@ -994,7 +994,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) argType = MacroArgType_Any; } else { - Macros_ReportErrorPos(ctx, "Unrecognized macroArg argument type:"); + Macros_ReportErrorTok(ctx, "Unrecognized macroArg argument type: "); return MacroResult_Finished; } } From 20d37d352bbd82e89c7896ea75ec243985783b45 Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Tue, 3 Feb 2026 17:06:28 +0100 Subject: [PATCH 09/26] message beautification --- right/src/macros/commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index 4ba525c13..8721e8aa5 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -994,7 +994,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) argType = MacroArgType_Any; } else { - Macros_ReportErrorTok(ctx, "Unrecognized macroArg argument type: "); + Macros_ReportErrorTok(ctx, "Unrecognized macroArg argument type:"); return MacroResult_Finished; } } From e2735e57c3727aaa73198e58ecbe758ba74cfdeb Mon Sep 17 00:00:00 2001 From: Karel Tucek Date: Tue, 3 Feb 2026 17:51:28 +0100 Subject: [PATCH 10/26] Fix gperf token vs argument expansion bug. --- right/src/macros/commands.c | 17 +++++------------ right/src/str_utils.c | 13 +++++++++++++ right/src/str_utils.h | 1 + 3 files changed, 19 insertions(+), 12 deletions(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index 7fbf5ea70..3c811bbd7 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -2015,29 +2015,22 @@ static macro_result_t processCommand(parser_context_t* ctx) const char* cmdTokEnd = TokEnd(ctx->at, ctx->end); if (cmdTokEnd > ctx->at && cmdTokEnd[-1] == ':') { //skip labels - ctx->at = NextTok(ctx->at, ctx->end); - if (ctx->at == ctx->end) { + ConsumeAnyToken(ctx); + if (ctx->at == ctx->end && IsEnd(ctx)) { return MacroResult_Finished; } } while(ctx->at < ctx->end || !IsEnd(ctx)) { - // Get the current token for hash lookup - const char* tokStart = ctx->at; - const char* tokEnd = TokEnd(tokStart, ctx->end); - size_t tokLen = tokEnd - tokStart; - // Look up the command in the hash table - const struct command_entry* entry = command_lookup(tokStart, tokLen); + const char* cmdAt = ctx->at; + const struct command_entry* entry = ConsumeGperfToken(ctx); if (entry == NULL) { - Macros_ReportErrorTok(ctx, "Unrecognized command:"); + Macros_ReportError("Unrecognized command:", cmdAt, TokEnd(cmdAt, ctx->end)); return MacroResult_Finished; } - // Consume the token - ctx->at = NextTok(tokStart, ctx->end); - // Dispatch based on command ID switch (entry->id) { // 'a' commands diff --git a/right/src/str_utils.c b/right/src/str_utils.c index 48213b282..d8e11a8a6 100644 --- a/right/src/str_utils.c +++ b/right/src/str_utils.c @@ -6,6 +6,7 @@ #include "module.h" #include "slave_protocol.h" #include "macros/vars.h" +#include "macros/command_hash.h" #include "trace.h" #if !defined(MIN) @@ -351,6 +352,7 @@ const char* TokEnd(const char* cmd, const char *cmdEnd) return cmd; } +// This doesn't handle expansions. Don't use it in actual macro context. const char* NextTok(const char* cmd, const char *cmdEnd) { while(*cmd > 32 && cmd < cmdEnd) { @@ -373,6 +375,17 @@ void ConsumeAnyToken(parser_context_t* ctx) consumeWhite(ctx); } +struct command_entry* ConsumeGperfToken(parser_context_t* ctx) +{ + const char* start = ctx->at; + while (*ctx->at > 32 && ctx->at < ctx->end) { + ctx->at++; + } + struct command_entry* result = command_lookup(start, ctx->at - start); + consumeWhite(ctx); + return result; +} + const char* NextCmd(const char* cmd, const char *cmdEnd) { while(*cmd != '\n' && *cmd != '\r' && cmd < cmdEnd) { diff --git a/right/src/str_utils.h b/right/src/str_utils.h index 82614f432..f7a62291e 100644 --- a/right/src/str_utils.h +++ b/right/src/str_utils.h @@ -63,6 +63,7 @@ const char* FindChar(char c, const char* str, const char* strEnd); bool ConsumeToken(parser_context_t* ctx, const char *b); void ConsumeAnyToken(parser_context_t* ctx); + struct command_entry* ConsumeGperfToken(parser_context_t* ctx); void ConsumeCommentsAsWhite(bool consume); bool ConsumeTokenByRef(parser_context_t* ctx, string_ref_t ref); bool ConsumeIdentifierByRef(parser_context_t* ctx, string_ref_t ref); From 402af5196dbbff6b6a0b9cbe7625dfb47737328c Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Wed, 4 Feb 2026 17:47:15 +0100 Subject: [PATCH 11/26] too memory intensive implementation of macro arguments storage, needs refactoring --- right/src/macros/core.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/right/src/macros/core.h b/right/src/macros/core.h index 5be31bfa3..ff1d5a2f8 100644 --- a/right/src/macros/core.h +++ b/right/src/macros/core.h @@ -21,6 +21,7 @@ #define MACRO_HISTORY_POOL_SIZE 16 #define MACRO_SCOPE_STATE_POOL_SIZE (MACRO_STATE_POOL_SIZE*2) #define MAX_REG_COUNT 32 + #define MAX_MACRO_ARGUMENT_SIZE 8 #define ALTMASK (HID_KEYBOARD_MODIFIER_LEFTALT | HID_KEYBOARD_MODIFIER_RIGHTALT) #define CTRLMASK (HID_KEYBOARD_MODIFIER_LEFTCTRL | HID_KEYBOARD_MODIFIER_RIGHTCTRL) @@ -135,6 +136,11 @@ uint8_t macroIndex; } macro_history_t; + typedef struct { + string_ref_t id; + macro_arg_type_t type; + } ATTR_PACKED macro_arg_t; + struct macro_state_t { // local scope data macro_scope_state_t *ls; @@ -167,6 +173,7 @@ bool autoRepeatInitialDelayPassed: 1; macro_autorepeat_state_t autoRepeatPhase: 1; // ---- 4-aligned ---- + macro_arg_t arguments[MAX_MACRO_ARGUMENT_SIZE]; macro_usb_keyboard_reports_t reports; } ms; From 877f3b4733ffe4a87bd5378ffd7dc5a0cf1fe415 Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Thu, 5 Feb 2026 19:09:36 +0100 Subject: [PATCH 12/26] allow macroArg command only at start of macro --- right/src/macros/commands.c | 779 ++++++++++++++++++------------------ right/src/macros/core.c | 1 - right/src/macros/core.h | 2 + 3 files changed, 401 insertions(+), 381 deletions(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index 05b5cdce8..6f088c5ef 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -951,6 +951,11 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) uint16_t textIndex = 0; uint16_t textSubIndex = 0; + if (S->ms.macroHeadersProcessed) { + Macros_ReportErrorPos(ctx, "macroArg commands must be placed before any other commands in the macro"); + return MacroResult_Finished; + } + if (Macros_DryRun) { // parse macroArg command but ignore it for now @@ -2076,6 +2081,393 @@ static macro_result_t processZephyrCommand(parser_context_t* ctx) { } \ break; +static macro_result_t dispatchCommand(parser_context_t* ctx, command_id_t commandId, bool *headersDone) { + // Dispatch based on command ID + switch (commandId) { + // 'a' commands + case CommandId_activateKeyPostponed: + return processActivateKeyPostponedCommand(ctx); + case CommandId_autoRepeat: + return processAutoRepeatCommand(ctx); + case CommandId_addReg: + Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `setVar varName ($varName+1)`."); + return MacroResult_Finished; + + // 'b' commands + case CommandId_break: + return processBreakCommand(ctx); + case CommandId_bluetooth: + return processBluetoothCommand(ctx); + + // 'c' commands + case CommandId_consumePending: + return processConsumePendingCommand(ctx); + case CommandId_clearStatus: + return Macros_ProcessClearStatusCommand(true); + case CommandId_call: + return processCallCommand(ctx); + + // 'd' commands + case CommandId_delayUntilRelease: + return processDelayUntilReleaseCommand(); + case CommandId_delayUntilReleaseMax: + return processDelayUntilReleaseMaxCommand(ctx); + case CommandId_delayUntil: + return processDelayUntilCommand(ctx); + case CommandId_diagnose: + return Macros_ProcessDiagnoseCommand(); + + // 'e' commands + case CommandId_exec: + return processExecCommand(ctx); + case CommandId_else: + if (!Macros_DryRun && S->ls->ms.lastIfSucceeded) { + return MacroResult_Finished; + } + break; + case CommandId_exit: + return processExitCommand(ctx); + + // 'f' commands + case CommandId_final: + return processFinalCommand(ctx); + case CommandId_fork: + return processForkCommand(ctx); + case CommandId_freeze: + return processFreezeCommand(ctx); + + // 'g' commands + case CommandId_goTo: + return processGoToCommand(ctx); + + // 'h' commands + case CommandId_holdLayer: + return processHoldLayerCommand(ctx); + case CommandId_holdLayerMax: + return processHoldLayerMaxCommand(ctx); + case CommandId_holdKeymapLayer: + return processHoldKeymapLayerCommand(ctx); + case CommandId_holdKeymapLayerMax: + return processHoldKeymapLayerMaxCommand(ctx); + case CommandId_holdKey: + return Macros_ProcessKeyCommandAndConsume(ctx, MacroSubAction_Hold, &S->ms.reports); + + // 'i' commands - conditionals + case CommandId_if: + PROCESS_CONDITION(processIfCommand(ctx)) + case CommandId_ifDoubletap: + PROCESS_CONDITION(processIfDoubletapCommand(false)) + case CommandId_ifNotDoubletap: + PROCESS_CONDITION(processIfDoubletapCommand(true)) + case CommandId_ifInterrupted: + PROCESS_CONDITION(processIfInterruptedCommand(false)) + case CommandId_ifNotInterrupted: + PROCESS_CONDITION(processIfInterruptedCommand(true)) + case CommandId_ifReleased: + PROCESS_CONDITION(processIfReleasedCommand(false)) + case CommandId_ifNotReleased: + PROCESS_CONDITION(processIfReleasedCommand(true)) + case CommandId_ifKeymap: + PROCESS_CONDITION(processIfKeymapCommand(ctx, false)) + case CommandId_ifNotKeymap: + PROCESS_CONDITION(processIfKeymapCommand(ctx, true)) + case CommandId_ifLayer: + PROCESS_CONDITION(processIfLayerCommand(ctx, false)) + case CommandId_ifNotLayer: + PROCESS_CONDITION(processIfLayerCommand(ctx, true)) + case CommandId_ifLayerToggled: + PROCESS_CONDITION(processIfLayerToggledCommand(ctx, false)) + case CommandId_ifNotLayerToggled: + PROCESS_CONDITION(processIfLayerToggledCommand(ctx, true)) + case CommandId_ifPlaytime: + PROCESS_CONDITION(processIfPlaytimeCommand(ctx, false)) + case CommandId_ifNotPlaytime: + PROCESS_CONDITION(processIfPlaytimeCommand(ctx, true)) + case CommandId_ifAnyMod: + PROCESS_CONDITION(processIfModifierCommand(false, 0xFF)) + case CommandId_ifNotAnyMod: + PROCESS_CONDITION(processIfModifierCommand(true, 0xFF)) + case CommandId_ifShift: + PROCESS_CONDITION(processIfModifierCommand(false, SHIFTMASK)) + case CommandId_ifNotShift: + PROCESS_CONDITION(processIfModifierCommand(true, SHIFTMASK)) + case CommandId_ifCtrl: + PROCESS_CONDITION(processIfModifierCommand(false, CTRLMASK)) + case CommandId_ifNotCtrl: + PROCESS_CONDITION(processIfModifierCommand(true, CTRLMASK)) + case CommandId_ifAlt: + PROCESS_CONDITION(processIfModifierCommand(false, ALTMASK)) + case CommandId_ifNotAlt: + PROCESS_CONDITION(processIfModifierCommand(true, ALTMASK)) + case CommandId_ifGui: + PROCESS_CONDITION(processIfModifierCommand(false, GUIMASK)) + case CommandId_ifNotGui: + PROCESS_CONDITION(processIfModifierCommand(true, GUIMASK)) + case CommandId_ifCapsLockOn: + PROCESS_CONDITION(processIfStateKeyCommand(false, &UsbBasicKeyboard_CapsLockOn)) + case CommandId_ifNotCapsLockOn: + PROCESS_CONDITION(processIfStateKeyCommand(true, &UsbBasicKeyboard_CapsLockOn)) + case CommandId_ifNumLockOn: + PROCESS_CONDITION(processIfStateKeyCommand(false, &UsbBasicKeyboard_NumLockOn)) + case CommandId_ifNotNumLockOn: + PROCESS_CONDITION(processIfStateKeyCommand(true, &UsbBasicKeyboard_NumLockOn)) + case CommandId_ifScrollLockOn: + PROCESS_CONDITION(processIfStateKeyCommand(false, &UsbBasicKeyboard_ScrollLockOn)) + case CommandId_ifNotScrollLockOn: + PROCESS_CONDITION(processIfStateKeyCommand(true, &UsbBasicKeyboard_ScrollLockOn)) + case CommandId_ifRecording: + PROCESS_CONDITION(processIfRecordingCommand(false)) + case CommandId_ifNotRecording: + PROCESS_CONDITION(processIfRecordingCommand(true)) + case CommandId_ifRecordingId: + PROCESS_CONDITION(processIfRecordingIdCommand(ctx, false)) + case CommandId_ifNotRecordingId: + PROCESS_CONDITION(processIfRecordingIdCommand(ctx, true)) + case CommandId_ifNotPending: + PROCESS_CONDITION(processIfPendingCommand(ctx, true)) + case CommandId_ifPending: + PROCESS_CONDITION(processIfPendingCommand(ctx, false)) + case CommandId_ifKeyPendingAt: + PROCESS_CONDITION(processIfKeyPendingAtCommand(ctx, false)) + case CommandId_ifNotKeyPendingAt: + PROCESS_CONDITION(processIfKeyPendingAtCommand(ctx, true)) + case CommandId_ifKeyActive: + PROCESS_CONDITION(processIfKeyActiveCommand(ctx, false)) + case CommandId_ifNotKeyActive: + PROCESS_CONDITION(processIfKeyActiveCommand(ctx, true)) + case CommandId_ifPendingKeyReleased: + PROCESS_CONDITION(processIfPendingKeyReleasedCommand(ctx, false)) + case CommandId_ifNotPendingKeyReleased: + PROCESS_CONDITION(processIfPendingKeyReleasedCommand(ctx, true)) + case CommandId_ifKeyDefined: + PROCESS_CONDITION(processIfKeyDefinedCommand(ctx, false)) + case CommandId_ifNotKeyDefined: + PROCESS_CONDITION(processIfKeyDefinedCommand(ctx, true)) + case CommandId_ifModuleConnected: + PROCESS_CONDITION(processIfModuleConnected(ctx, false)) + case CommandId_ifNotModuleConnected: + PROCESS_CONDITION(processIfModuleConnected(ctx, true)) + case CommandId_ifHold: + return processIfHoldCommand(ctx, false); + case CommandId_ifTap: + return processIfHoldCommand(ctx, true); + case CommandId_ifSecondary: + return processIfSecondaryCommand(ctx, false); + case CommandId_ifPrimary: + return processIfSecondaryCommand(ctx, true); + case CommandId_ifShortcut: + return processIfShortcutCommand(ctx, false, true); + case CommandId_ifNotShortcut: + return processIfShortcutCommand(ctx, true, true); + case CommandId_ifGesture: + return processIfShortcutCommand(ctx, false, false); + case CommandId_ifNotGesture: + return processIfShortcutCommand(ctx, true, false); + case CommandId_ifRegEq: + case CommandId_ifNotRegEq: + Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `if ($varName == 1)`."); + return MacroResult_Finished; + case CommandId_ifRegGt: + case CommandId_ifRegLt: + Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `if ($varName >= 1)`."); + return MacroResult_Finished; + + // 'm' commands + case CommandId_macroArg: + *headersFinished = false; // this is a valid header command, stay in header mode + return processMacroArgCommand(ctx); + case CommandId_mulReg: + Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `setVar varName ($varName*2)`."); + return MacroResult_Finished; + + // 'n' commands + case CommandId_noOp: + return processNoOpCommand(); + case CommandId_notify: + return Macros_ProcessNotifyCommand(ctx); + + // 'o' commands + case CommandId_oneShot: + return processOneShotCommand(ctx); + case CommandId_overlayLayer: + return processOverlayLayerCommand(ctx); + case CommandId_overlayKeymap: + return processOverlayKeymapCommand(ctx); + + // 'p' commands + case CommandId_printStatus: + return Macros_ProcessPrintStatusCommand(); + case CommandId_playMacro: + return processPlayMacroCommand(ctx); + case CommandId_pressKey: + return Macros_ProcessKeyCommandAndConsume(ctx, MacroSubAction_Press, &S->ms.reports); + case CommandId_postponeKeys: + processPostponeKeysCommand(); + break; + case CommandId_postponeNext: + return processPostponeNextNCommand(ctx); + case CommandId_progressHue: + return processProgressHueCommand(); + case CommandId_powerMode: + return processPowerModeCommand(ctx); + case CommandId_panic: + return processPanicCommand(ctx); + + // 'r' commands + case CommandId_recordMacro: + return processRecordMacroCommand(ctx, false); + case CommandId_recordMacroBlind: + return processRecordMacroCommand(ctx, true); + case CommandId_recordMacroDelay: + return processRecordMacroDelayCommand(); + case CommandId_resolveNextKeyId: + return processResolveNextKeyIdCommand(); + case CommandId_releaseKey: + return Macros_ProcessKeyCommandAndConsume(ctx, MacroSubAction_Release, &S->ms.reports); + case CommandId_repeatFor: + return processRepeatForCommand(ctx); + case CommandId_resetTrackpoint: + return processResetTrackpointCommand(); + case CommandId_replaceLayer: + return processReplaceLayerCommand(ctx); + case CommandId_replaceKeymap: + return processReplaceKeymapCommand(ctx); + case CommandId_resolveNextKeyEq: + Macros_ReportErrorPos(ctx, "Command deprecated. Please, replace resolveNextKeyEq by ifShortcut or ifGesture, or complain at github that you actually need this."); + return MacroResult_Finished; + case CommandId_resolveSecondary: + Macros_ReportErrorPos(ctx, "Command deprecated. Please, replace resolveSecondary by `ifPrimary advancedStrategy goTo ...` or `ifSecondary advancedStrategy goTo ...`."); + return MacroResult_Finished; + case CommandId_resetConfiguration: + return processResetConfigurationCommand(ctx); + case CommandId_reboot: + return processRebootCommand(); + case CommandId_reconnect: + return processReconnectCommand(); + + // 's' commands + case CommandId_set: + return Macro_ProcessSetCommand(ctx); + case CommandId_setVar: + return Macros_ProcessSetVarCommand(ctx); + case CommandId_setStatus: + return Macros_ProcessSetStatusCommand(ctx, true); + case CommandId_startRecording: + return processStartRecordingCommand(ctx, false); + case CommandId_startRecordingBlind: + return processStartRecordingCommand(ctx, true); + case CommandId_setLedTxt: + return Macros_ProcessSetLedTxtCommand(ctx); + case CommandId_statsRuntime: + return Macros_ProcessStatsRuntimeCommand(); + case CommandId_statsRecordKeyTiming: + return Macros_ProcessStatsRecordKeyTimingCommand(); + case CommandId_statsLayerStack: + return Macros_ProcessStatsLayerStackCommand(); + case CommandId_statsActiveKeys: + return Macros_ProcessStatsActiveKeysCommand(); + case CommandId_statsActiveMacros: + return Macros_ProcessStatsActiveMacrosCommand(); + case CommandId_statsPostponerStack: + return Macros_ProcessStatsPostponerStackCommand(); + case CommandId_statsVariables: + return Macros_ProcessStatsVariablesCommand(); + case CommandId_statsBattery: + return Macros_ProcessStatsBatteryCommand(); + case CommandId_switchKeymap: + return processSwitchKeymapCommand(ctx); + case CommandId_startMouse: + return processMouseCommand(ctx, true); + case CommandId_stopMouse: + return processMouseCommand(ctx, false); + case CommandId_stopRecording: + case CommandId_stopRecordingBlind: + return processStopRecordingCommand(); + case CommandId_stopAllMacros: + return processStopAllMacrosCommand(); + case CommandId_suppressMods: + processSuppressModsCommand(); + break; + case CommandId_setReg: + Macros_ReportErrorPos(ctx, "Command was removed, please use named variables. E.g., `setVar myVar 1` and `write \"$myVar\"`"); + return MacroResult_Finished; + case CommandId_subReg: + Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `setVar varName ($varName+1)`."); + return MacroResult_Finished; + case CommandId_setStatusPart: + Macros_ReportErrorPos(ctx, "Command was removed, please use string interpolated setStatus."); + return MacroResult_Finished; + case CommandId_switchKeymapLayer: + case CommandId_switchLayer: + Macros_ReportErrorPos(ctx, "Command deprecated. Please, replace switchKeymapLayer by toggleKeymapLayer or holdKeymapLayer. Or complain on github that you actually need this command."); + return MacroResult_Finished; + case CommandId_switchHost: + return processSwitchHostCommand(ctx); + + // 't' commands + case CommandId_toggleKeymapLayer: + return processToggleKeymapLayerCommand(ctx); + case CommandId_toggleLayer: + return processToggleLayerCommand(ctx); + case CommandId_tapKey: + return Macros_ProcessKeyCommandAndConsume(ctx, MacroSubAction_Tap, &S->ms.reports); + case CommandId_tapKeySeq: + return Macros_ProcessTapKeySeqCommand(ctx); + case CommandId_toggleKey: + return Macros_ProcessKeyCommandAndConsume(ctx, MacroSubAction_Toggle, &S->ms.reports); + case CommandId_trackpoint: + return processTrackpointCommand(ctx); + case CommandId_trace: + if (!Macros_DryRun) { + Trace_Print(LogTarget_ErrorBuffer, "Triggered by macro command"); + } + return MacroResult_Finished; + case CommandId_testLeakage: + return processTestLeakageCommand(ctx); + case CommandId_testSuite: + return processTestSuiteCommand(ctx); + + // 'u' commands + case CommandId_unToggleLayer: + case CommandId_untoggleLayer: + return processUnToggleLayerCommand(); + case CommandId_unpairHost: + return Macros_ProcessUnpairHostCommand(ctx); + + // 'v' commands + case CommandId_validateUserConfig: + case CommandId_validateMacros: + return processValidateMacrosCommand(ctx); + + // 'w' commands + case CommandId_write: + return processWriteCommand(ctx); + case CommandId_while: + return processWhileCommand(ctx); + case CommandId_writeExpr: + Macros_ReportErrorPos(ctx, "writeExpr is now deprecated, please migrate to interpolated strings"); + return MacroResult_Finished; + + // 'y' commands + case CommandId_yield: + return processYieldCommand(ctx); + + // 'z' commands + case CommandId_zephyr: + return processZephyrCommand(ctx); + + // brace commands + case CommandId_openBrace: + return processOpeningBraceCommand(ctx); + case CommandId_closeBrace: + return processClosingBraceCommand(ctx); + + default: + Macros_ReportErrorTok(ctx, "Unrecognized command:"); + return MacroResult_Finished; + } +} + static macro_result_t processCommand(parser_context_t* ctx) { const char* cmdTokEnd = TokEnd(ctx->at, ctx->end); @@ -2097,390 +2489,17 @@ static macro_result_t processCommand(parser_context_t* ctx) return MacroResult_Finished; } - // Dispatch based on command ID - switch (entry->id) { - // 'a' commands - case CommandId_activateKeyPostponed: - return processActivateKeyPostponedCommand(ctx); - case CommandId_autoRepeat: - return processAutoRepeatCommand(ctx); - case CommandId_addReg: - Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `setVar varName ($varName+1)`."); - return MacroResult_Finished; - - // 'b' commands - case CommandId_break: - return processBreakCommand(ctx); - case CommandId_bluetooth: - return processBluetoothCommand(ctx); - - // 'c' commands - case CommandId_consumePending: - return processConsumePendingCommand(ctx); - case CommandId_clearStatus: - return Macros_ProcessClearStatusCommand(true); - case CommandId_call: - return processCallCommand(ctx); - - // 'd' commands - case CommandId_delayUntilRelease: - return processDelayUntilReleaseCommand(); - case CommandId_delayUntilReleaseMax: - return processDelayUntilReleaseMaxCommand(ctx); - case CommandId_delayUntil: - return processDelayUntilCommand(ctx); - case CommandId_diagnose: - return Macros_ProcessDiagnoseCommand(); - - // 'e' commands - case CommandId_exec: - return processExecCommand(ctx); - case CommandId_else: - if (!Macros_DryRun && S->ls->ms.lastIfSucceeded) { - return MacroResult_Finished; - } - break; - case CommandId_exit: - return processExitCommand(ctx); - - // 'f' commands - case CommandId_final: - return processFinalCommand(ctx); - case CommandId_fork: - return processForkCommand(ctx); - case CommandId_freeze: - return processFreezeCommand(ctx); - - // 'g' commands - case CommandId_goTo: - return processGoToCommand(ctx); - - // 'h' commands - case CommandId_holdLayer: - return processHoldLayerCommand(ctx); - case CommandId_holdLayerMax: - return processHoldLayerMaxCommand(ctx); - case CommandId_holdKeymapLayer: - return processHoldKeymapLayerCommand(ctx); - case CommandId_holdKeymapLayerMax: - return processHoldKeymapLayerMaxCommand(ctx); - case CommandId_holdKey: - return Macros_ProcessKeyCommandAndConsume(ctx, MacroSubAction_Hold, &S->ms.reports); - - // 'i' commands - conditionals - case CommandId_if: - PROCESS_CONDITION(processIfCommand(ctx)) - case CommandId_ifDoubletap: - PROCESS_CONDITION(processIfDoubletapCommand(false)) - case CommandId_ifNotDoubletap: - PROCESS_CONDITION(processIfDoubletapCommand(true)) - case CommandId_ifInterrupted: - PROCESS_CONDITION(processIfInterruptedCommand(false)) - case CommandId_ifNotInterrupted: - PROCESS_CONDITION(processIfInterruptedCommand(true)) - case CommandId_ifReleased: - PROCESS_CONDITION(processIfReleasedCommand(false)) - case CommandId_ifNotReleased: - PROCESS_CONDITION(processIfReleasedCommand(true)) - case CommandId_ifKeymap: - PROCESS_CONDITION(processIfKeymapCommand(ctx, false)) - case CommandId_ifNotKeymap: - PROCESS_CONDITION(processIfKeymapCommand(ctx, true)) - case CommandId_ifLayer: - PROCESS_CONDITION(processIfLayerCommand(ctx, false)) - case CommandId_ifNotLayer: - PROCESS_CONDITION(processIfLayerCommand(ctx, true)) - case CommandId_ifLayerToggled: - PROCESS_CONDITION(processIfLayerToggledCommand(ctx, false)) - case CommandId_ifNotLayerToggled: - PROCESS_CONDITION(processIfLayerToggledCommand(ctx, true)) - case CommandId_ifPlaytime: - PROCESS_CONDITION(processIfPlaytimeCommand(ctx, false)) - case CommandId_ifNotPlaytime: - PROCESS_CONDITION(processIfPlaytimeCommand(ctx, true)) - case CommandId_ifAnyMod: - PROCESS_CONDITION(processIfModifierCommand(false, 0xFF)) - case CommandId_ifNotAnyMod: - PROCESS_CONDITION(processIfModifierCommand(true, 0xFF)) - case CommandId_ifShift: - PROCESS_CONDITION(processIfModifierCommand(false, SHIFTMASK)) - case CommandId_ifNotShift: - PROCESS_CONDITION(processIfModifierCommand(true, SHIFTMASK)) - case CommandId_ifCtrl: - PROCESS_CONDITION(processIfModifierCommand(false, CTRLMASK)) - case CommandId_ifNotCtrl: - PROCESS_CONDITION(processIfModifierCommand(true, CTRLMASK)) - case CommandId_ifAlt: - PROCESS_CONDITION(processIfModifierCommand(false, ALTMASK)) - case CommandId_ifNotAlt: - PROCESS_CONDITION(processIfModifierCommand(true, ALTMASK)) - case CommandId_ifGui: - PROCESS_CONDITION(processIfModifierCommand(false, GUIMASK)) - case CommandId_ifNotGui: - PROCESS_CONDITION(processIfModifierCommand(true, GUIMASK)) - case CommandId_ifCapsLockOn: - PROCESS_CONDITION(processIfStateKeyCommand(false, &UsbBasicKeyboard_CapsLockOn)) - case CommandId_ifNotCapsLockOn: - PROCESS_CONDITION(processIfStateKeyCommand(true, &UsbBasicKeyboard_CapsLockOn)) - case CommandId_ifNumLockOn: - PROCESS_CONDITION(processIfStateKeyCommand(false, &UsbBasicKeyboard_NumLockOn)) - case CommandId_ifNotNumLockOn: - PROCESS_CONDITION(processIfStateKeyCommand(true, &UsbBasicKeyboard_NumLockOn)) - case CommandId_ifScrollLockOn: - PROCESS_CONDITION(processIfStateKeyCommand(false, &UsbBasicKeyboard_ScrollLockOn)) - case CommandId_ifNotScrollLockOn: - PROCESS_CONDITION(processIfStateKeyCommand(true, &UsbBasicKeyboard_ScrollLockOn)) - case CommandId_ifRecording: - PROCESS_CONDITION(processIfRecordingCommand(false)) - case CommandId_ifNotRecording: - PROCESS_CONDITION(processIfRecordingCommand(true)) - case CommandId_ifRecordingId: - PROCESS_CONDITION(processIfRecordingIdCommand(ctx, false)) - case CommandId_ifNotRecordingId: - PROCESS_CONDITION(processIfRecordingIdCommand(ctx, true)) - case CommandId_ifNotPending: - PROCESS_CONDITION(processIfPendingCommand(ctx, true)) - case CommandId_ifPending: - PROCESS_CONDITION(processIfPendingCommand(ctx, false)) - case CommandId_ifKeyPendingAt: - PROCESS_CONDITION(processIfKeyPendingAtCommand(ctx, false)) - case CommandId_ifNotKeyPendingAt: - PROCESS_CONDITION(processIfKeyPendingAtCommand(ctx, true)) - case CommandId_ifKeyActive: - PROCESS_CONDITION(processIfKeyActiveCommand(ctx, false)) - case CommandId_ifNotKeyActive: - PROCESS_CONDITION(processIfKeyActiveCommand(ctx, true)) - case CommandId_ifPendingKeyReleased: - PROCESS_CONDITION(processIfPendingKeyReleasedCommand(ctx, false)) - case CommandId_ifNotPendingKeyReleased: - PROCESS_CONDITION(processIfPendingKeyReleasedCommand(ctx, true)) - case CommandId_ifKeyDefined: - PROCESS_CONDITION(processIfKeyDefinedCommand(ctx, false)) - case CommandId_ifNotKeyDefined: - PROCESS_CONDITION(processIfKeyDefinedCommand(ctx, true)) - case CommandId_ifModuleConnected: - PROCESS_CONDITION(processIfModuleConnected(ctx, false)) - case CommandId_ifNotModuleConnected: - PROCESS_CONDITION(processIfModuleConnected(ctx, true)) - case CommandId_ifHold: - return processIfHoldCommand(ctx, false); - case CommandId_ifTap: - return processIfHoldCommand(ctx, true); - case CommandId_ifSecondary: - return processIfSecondaryCommand(ctx, false); - case CommandId_ifPrimary: - return processIfSecondaryCommand(ctx, true); - case CommandId_ifShortcut: - return processIfShortcutCommand(ctx, false, true); - case CommandId_ifNotShortcut: - return processIfShortcutCommand(ctx, true, true); - case CommandId_ifGesture: - return processIfShortcutCommand(ctx, false, false); - case CommandId_ifNotGesture: - return processIfShortcutCommand(ctx, true, false); - case CommandId_ifRegEq: - case CommandId_ifNotRegEq: - Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `if ($varName == 1)`."); - return MacroResult_Finished; - case CommandId_ifRegGt: - case CommandId_ifRegLt: - Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `if ($varName >= 1)`."); - return MacroResult_Finished; - - // 'm' commands - case CommandId_macroArg: - return processMacroArgCommand(ctx); - case CommandId_mulReg: - Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `setVar varName ($varName*2)`."); - return MacroResult_Finished; + // We assume the next command is a non-header command and will therefore + // finish the header block. + bool headersProcessed = true; - // 'n' commands - case CommandId_noOp: - return processNoOpCommand(); - case CommandId_notify: - return Macros_ProcessNotifyCommand(ctx); - - // 'o' commands - case CommandId_oneShot: - return processOneShotCommand(ctx); - case CommandId_overlayLayer: - return processOverlayLayerCommand(ctx); - case CommandId_overlayKeymap: - return processOverlayKeymapCommand(ctx); - - // 'p' commands - case CommandId_printStatus: - return Macros_ProcessPrintStatusCommand(); - case CommandId_playMacro: - return processPlayMacroCommand(ctx); - case CommandId_pressKey: - return Macros_ProcessKeyCommandAndConsume(ctx, MacroSubAction_Press, &S->ms.reports); - case CommandId_postponeKeys: - processPostponeKeysCommand(); - break; - case CommandId_postponeNext: - return processPostponeNextNCommand(ctx); - case CommandId_progressHue: - return processProgressHueCommand(); - case CommandId_powerMode: - return processPowerModeCommand(ctx); - case CommandId_panic: - return processPanicCommand(ctx); - - // 'r' commands - case CommandId_recordMacro: - return processRecordMacroCommand(ctx, false); - case CommandId_recordMacroBlind: - return processRecordMacroCommand(ctx, true); - case CommandId_recordMacroDelay: - return processRecordMacroDelayCommand(); - case CommandId_resolveNextKeyId: - return processResolveNextKeyIdCommand(); - case CommandId_releaseKey: - return Macros_ProcessKeyCommandAndConsume(ctx, MacroSubAction_Release, &S->ms.reports); - case CommandId_repeatFor: - return processRepeatForCommand(ctx); - case CommandId_resetTrackpoint: - return processResetTrackpointCommand(); - case CommandId_replaceLayer: - return processReplaceLayerCommand(ctx); - case CommandId_replaceKeymap: - return processReplaceKeymapCommand(ctx); - case CommandId_resolveNextKeyEq: - Macros_ReportErrorPos(ctx, "Command deprecated. Please, replace resolveNextKeyEq by ifShortcut or ifGesture, or complain at github that you actually need this."); - return MacroResult_Finished; - case CommandId_resolveSecondary: - Macros_ReportErrorPos(ctx, "Command deprecated. Please, replace resolveSecondary by `ifPrimary advancedStrategy goTo ...` or `ifSecondary advancedStrategy goTo ...`."); - return MacroResult_Finished; - case CommandId_resetConfiguration: - return processResetConfigurationCommand(ctx); - case CommandId_reboot: - return processRebootCommand(); - case CommandId_reconnect: - return processReconnectCommand(); - - // 's' commands - case CommandId_set: - return Macro_ProcessSetCommand(ctx); - case CommandId_setVar: - return Macros_ProcessSetVarCommand(ctx); - case CommandId_setStatus: - return Macros_ProcessSetStatusCommand(ctx, true); - case CommandId_startRecording: - return processStartRecordingCommand(ctx, false); - case CommandId_startRecordingBlind: - return processStartRecordingCommand(ctx, true); - case CommandId_setLedTxt: - return Macros_ProcessSetLedTxtCommand(ctx); - case CommandId_statsRuntime: - return Macros_ProcessStatsRuntimeCommand(); - case CommandId_statsRecordKeyTiming: - return Macros_ProcessStatsRecordKeyTimingCommand(); - case CommandId_statsLayerStack: - return Macros_ProcessStatsLayerStackCommand(); - case CommandId_statsActiveKeys: - return Macros_ProcessStatsActiveKeysCommand(); - case CommandId_statsActiveMacros: - return Macros_ProcessStatsActiveMacrosCommand(); - case CommandId_statsPostponerStack: - return Macros_ProcessStatsPostponerStackCommand(); - case CommandId_statsVariables: - return Macros_ProcessStatsVariablesCommand(); - case CommandId_statsBattery: - return Macros_ProcessStatsBatteryCommand(); - case CommandId_switchKeymap: - return processSwitchKeymapCommand(ctx); - case CommandId_startMouse: - return processMouseCommand(ctx, true); - case CommandId_stopMouse: - return processMouseCommand(ctx, false); - case CommandId_stopRecording: - case CommandId_stopRecordingBlind: - return processStopRecordingCommand(); - case CommandId_stopAllMacros: - return processStopAllMacrosCommand(); - case CommandId_suppressMods: - processSuppressModsCommand(); - break; - case CommandId_setReg: - Macros_ReportErrorPos(ctx, "Command was removed, please use named variables. E.g., `setVar myVar 1` and `write \"$myVar\"`"); - return MacroResult_Finished; - case CommandId_subReg: - Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `setVar varName ($varName+1)`."); - return MacroResult_Finished; - case CommandId_setStatusPart: - Macros_ReportErrorPos(ctx, "Command was removed, please use string interpolated setStatus."); - return MacroResult_Finished; - case CommandId_switchKeymapLayer: - case CommandId_switchLayer: - Macros_ReportErrorPos(ctx, "Command deprecated. Please, replace switchKeymapLayer by toggleKeymapLayer or holdKeymapLayer. Or complain on github that you actually need this command."); - return MacroResult_Finished; - case CommandId_switchHost: - return processSwitchHostCommand(ctx); - - // 't' commands - case CommandId_toggleKeymapLayer: - return processToggleKeymapLayerCommand(ctx); - case CommandId_toggleLayer: - return processToggleLayerCommand(ctx); - case CommandId_tapKey: - return Macros_ProcessKeyCommandAndConsume(ctx, MacroSubAction_Tap, &S->ms.reports); - case CommandId_tapKeySeq: - return Macros_ProcessTapKeySeqCommand(ctx); - case CommandId_toggleKey: - return Macros_ProcessKeyCommandAndConsume(ctx, MacroSubAction_Toggle, &S->ms.reports); - case CommandId_trackpoint: - return processTrackpointCommand(ctx); - case CommandId_trace: - if (!Macros_DryRun) { - Trace_Print(LogTarget_ErrorBuffer, "Triggered by macro command"); - } - return MacroResult_Finished; - case CommandId_testLeakage: - return processTestLeakageCommand(ctx); - case CommandId_testSuite: - return processTestSuiteCommand(ctx); - - // 'u' commands - case CommandId_unToggleLayer: - case CommandId_untoggleLayer: - return processUnToggleLayerCommand(); - case CommandId_unpairHost: - return Macros_ProcessUnpairHostCommand(ctx); - - // 'v' commands - case CommandId_validateUserConfig: - case CommandId_validateMacros: - return processValidateMacrosCommand(ctx); - - // 'w' commands - case CommandId_write: - return processWriteCommand(ctx); - case CommandId_while: - return processWhileCommand(ctx); - case CommandId_writeExpr: - Macros_ReportErrorPos(ctx, "writeExpr is now deprecated, please migrate to interpolated strings"); - return MacroResult_Finished; - - // 'y' commands - case CommandId_yield: - return processYieldCommand(ctx); + macro_result_t res = dispatchCommand(ctx, entry->id, &headersProcessed); - // 'z' commands - case CommandId_zephyr: - return processZephyrCommand(ctx); - - // brace commands - case CommandId_openBrace: - return processOpeningBraceCommand(ctx); - case CommandId_closeBrace: - return processClosingBraceCommand(ctx); - - default: - Macros_ReportErrorTok(ctx, "Unrecognized command:"); - return MacroResult_Finished; + if (headersProcessed) { + S->ms.macroHeadersProcessed = true; } } + //this is reachable if there is a train of conditions/modifiers/labels without any command return MacroResult_Finished; } diff --git a/right/src/macros/core.c b/right/src/macros/core.c index b30d4c763..ce745c755 100644 --- a/right/src/macros/core.c +++ b/right/src/macros/core.c @@ -31,7 +31,6 @@ macro_reference_t AllMacros[MacroIndex_MaxCount] = { }; uint8_t AllMacrosCount; - bool Macros_WakedBecauseOfOneShot = false; bool Macros_WakedBecauseOfTime = false; bool Macros_WakedBecauseOfKeystateChange = false; diff --git a/right/src/macros/core.h b/right/src/macros/core.h index ff1d5a2f8..babc46053 100644 --- a/right/src/macros/core.h +++ b/right/src/macros/core.h @@ -176,6 +176,8 @@ macro_arg_t arguments[MAX_MACRO_ARGUMENT_SIZE]; macro_usb_keyboard_reports_t reports; + + bool macroHeadersProcessed : 1; } ms; // action scope data From f5f33021c013cc9786bd7cd2ac5f56fd36fd7bd4 Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Thu, 5 Feb 2026 19:17:30 +0100 Subject: [PATCH 13/26] fix typo --- right/src/macros/commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index 6f088c5ef..d57ad1489 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -2081,7 +2081,7 @@ static macro_result_t processZephyrCommand(parser_context_t* ctx) { } \ break; -static macro_result_t dispatchCommand(parser_context_t* ctx, command_id_t commandId, bool *headersDone) { +static macro_result_t dispatchCommand(parser_context_t* ctx, command_id_t commandId, bool *headersFinished) { // Dispatch based on command ID switch (commandId) { // 'a' commands From b81f9b3758318fd335add220d713460a8b77b7b9 Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Thu, 5 Feb 2026 19:26:15 +0100 Subject: [PATCH 14/26] actually return the return value --- right/src/macros/commands.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index d57ad1489..8bd4b6558 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -2467,7 +2467,7 @@ static macro_result_t dispatchCommand(parser_context_t* ctx, command_id_t comman return MacroResult_Finished; } } - + static macro_result_t processCommand(parser_context_t* ctx) { const char* cmdTokEnd = TokEnd(ctx->at, ctx->end); @@ -2498,6 +2498,7 @@ static macro_result_t processCommand(parser_context_t* ctx) if (headersProcessed) { S->ms.macroHeadersProcessed = true; } + return res; } //this is reachable if there is a train of conditions/modifiers/labels without any command From 9239daadd8895f1c7574962e856de1c473f82b1f Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Fri, 6 Feb 2026 10:38:28 +0100 Subject: [PATCH 15/26] figure why some parsing broke --- right/src/macros/commands.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index 8bd4b6558..f86daea60 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -2466,6 +2466,9 @@ static macro_result_t dispatchCommand(parser_context_t* ctx, command_id_t comman Macros_ReportErrorTok(ctx, "Unrecognized command:"); return MacroResult_Finished; } + + Macros_ReportErrorTok(ctx, "Fell through the dispatch at:"); + return MacroResult_Finished; } static macro_result_t processCommand(parser_context_t* ctx) From 8da0330424d188d106b1c9f393b29286ae5b892e Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Fri, 6 Feb 2026 11:17:55 +0100 Subject: [PATCH 16/26] fix the reachable unreachable case --- right/src/macros/commands.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index f86daea60..79ed029ba 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -2467,7 +2467,7 @@ static macro_result_t dispatchCommand(parser_context_t* ctx, command_id_t comman return MacroResult_Finished; } - Macros_ReportErrorTok(ctx, "Fell through the dispatch at:"); + //this is reachable in some cases return MacroResult_Finished; } From ab5c4323532bd5dbf9a31dd1a6825fcf4898a948 Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Fri, 6 Feb 2026 12:04:10 +0100 Subject: [PATCH 17/26] introducing MacroResult_None to fix 'if' commands that need to continue the processCommand() loop --- right/src/macros/commands.c | 8 +++++--- right/src/macros/typedefs.h | 1 + 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index 79ed029ba..0e3ed5315 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -2467,8 +2467,8 @@ static macro_result_t dispatchCommand(parser_context_t* ctx, command_id_t comman return MacroResult_Finished; } - //this is reachable in some cases - return MacroResult_Finished; + // this is reachable when 'ifXxx' conditions pass; processCommand() should continue with further commands. + return MacroResult_None; } static macro_result_t processCommand(parser_context_t* ctx) @@ -2501,7 +2501,9 @@ static macro_result_t processCommand(parser_context_t* ctx) if (headersProcessed) { S->ms.macroHeadersProcessed = true; } - return res; + if (res != MacroResult_None) { + return res; + } } //this is reachable if there is a train of conditions/modifiers/labels without any command diff --git a/right/src/macros/typedefs.h b/right/src/macros/typedefs.h index 3fdad13fd..c4349c319 100644 --- a/right/src/macros/typedefs.h +++ b/right/src/macros/typedefs.h @@ -23,6 +23,7 @@ } macro_sub_action_t; typedef enum { + MacroResult_None = 0, MacroResult_InProgressFlag = 1, MacroResult_ActionFinishedFlag = 2, MacroResult_DoneFlag = 4, From 458afe1bdd4660b7e7c0f1b3ed30246d8911a73c Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Fri, 6 Feb 2026 15:05:41 +0100 Subject: [PATCH 18/26] removing unused code --- right/src/str_utils.c | 5 ----- 1 file changed, 5 deletions(-) diff --git a/right/src/str_utils.c b/right/src/str_utils.c index e7651a024..2446cc3d3 100644 --- a/right/src/str_utils.c +++ b/right/src/str_utils.c @@ -320,11 +320,6 @@ void ConsumeAnyIdentifier(parser_context_t* ctx) consumeWhite(ctx); } -void ConsumeIdentifier(parser_context_t* ctx) -{ - ctx->at = IdentifierEnd(ctx); -} - void ConsumeUntilDot(parser_context_t* ctx) { while(*ctx->at > 32 && *ctx->at != '.' && !isEnd(ctx)) { From 2b989253e5a6d06935844e9736df3011ba9c380e Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Fri, 6 Feb 2026 15:55:19 +0100 Subject: [PATCH 19/26] cleaner handling of returns from dispatching, especially for header commands --- right/src/macros/commands.c | 23 ++++++++++------------- right/src/macros/typedefs.h | 1 + 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index 0e3ed5315..ca6e669a7 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -961,7 +961,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) while (Macros_ConsumeCharOfString(ctx, &stringOffset, &textIndex, &textSubIndex) != '\0') {}; - return MacroResult_Finished; + return MacroResult_Header; } // parse the argument name (identifier) const char *idStart = ctx->at; @@ -969,7 +969,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) if (idEnd == idStart) { Macros_ReportErrorPos(ctx, "Expected identifier"); - return MacroResult_Finished; + return MacroResult_Header; } ctx->at = idEnd; @@ -1000,7 +1000,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) } else { Macros_ReportErrorTok(ctx, "Unrecognized macroArg argument type:"); - return MacroResult_Finished; + return MacroResult_Header; } } else { @@ -1011,7 +1011,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) // rest of command is descriptive label, ignored by firmware while (Macros_ConsumeCharOfString(ctx, &stringOffset, &textIndex, &textSubIndex) != '\0') {}; - return MacroResult_Finished; + return MacroResult_Header; // Macros_ReportErrorPrintf(ctx->at, "Parsing failed at '%s'?", OneWord(ctx)); } @@ -2081,7 +2081,7 @@ static macro_result_t processZephyrCommand(parser_context_t* ctx) { } \ break; -static macro_result_t dispatchCommand(parser_context_t* ctx, command_id_t commandId, bool *headersFinished) { +static macro_result_t dispatchCommand(parser_context_t* ctx, command_id_t commandId) { // Dispatch based on command ID switch (commandId) { // 'a' commands @@ -2274,7 +2274,6 @@ static macro_result_t dispatchCommand(parser_context_t* ctx, command_id_t comman // 'm' commands case CommandId_macroArg: - *headersFinished = false; // this is a valid header command, stay in header mode return processMacroArgCommand(ctx); case CommandId_mulReg: Macros_ReportErrorPos(ctx, "Command was removed, please use command similar to `setVar varName ($varName*2)`."); @@ -2492,14 +2491,12 @@ static macro_result_t processCommand(parser_context_t* ctx) return MacroResult_Finished; } - // We assume the next command is a non-header command and will therefore - // finish the header block. - bool headersProcessed = true; + macro_result_t res = dispatchCommand(ctx, entry->id); - macro_result_t res = dispatchCommand(ctx, entry->id, &headersProcessed); - - if (headersProcessed) { - S->ms.macroHeadersProcessed = true; + if (res == MacroResult_Header) { + return MacroResult_Finished; + } else { + S->ms.macroHeadersProcessed = true; // non-header commands mark the header as finished } if (res != MacroResult_None) { return res; diff --git a/right/src/macros/typedefs.h b/right/src/macros/typedefs.h index c4349c319..32ac69ca9 100644 --- a/right/src/macros/typedefs.h +++ b/right/src/macros/typedefs.h @@ -36,6 +36,7 @@ MacroResult_Waiting = MacroResult_InProgressFlag | MacroResult_YieldFlag, MacroResult_Sleeping = MacroResult_InProgressFlag | MacroResult_YieldFlag, MacroResult_Finished = MacroResult_ActionFinishedFlag, + MacroResult_Header = MacroResult ActionFinishedFlag | MacroResult_InProgressFlag, MacroResult_JumpedForward = MacroResult_DoneFlag, MacroResult_JumpedBackward = MacroResult_DoneFlag | MacroResult_YieldFlag, } macro_result_t; From e51d414a6b9e8afa065e6e909edccf817debaafe Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Fri, 6 Feb 2026 16:15:26 +0100 Subject: [PATCH 20/26] syntax fix --- right/src/macros/typedefs.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/right/src/macros/typedefs.h b/right/src/macros/typedefs.h index 32ac69ca9..818bee0bb 100644 --- a/right/src/macros/typedefs.h +++ b/right/src/macros/typedefs.h @@ -36,7 +36,7 @@ MacroResult_Waiting = MacroResult_InProgressFlag | MacroResult_YieldFlag, MacroResult_Sleeping = MacroResult_InProgressFlag | MacroResult_YieldFlag, MacroResult_Finished = MacroResult_ActionFinishedFlag, - MacroResult_Header = MacroResult ActionFinishedFlag | MacroResult_InProgressFlag, + MacroResult_Header = MacroResult_ActionFinishedFlag | MacroResult_InProgressFlag, MacroResult_JumpedForward = MacroResult_DoneFlag, MacroResult_JumpedBackward = MacroResult_DoneFlag | MacroResult_YieldFlag, } macro_result_t; From d540053a15e5adea6213a78ed14aff90a061c671 Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Fri, 6 Feb 2026 18:36:37 +0100 Subject: [PATCH 21/26] moving arg storage, but I'm not happy yet --- right/src/macros/core.c | 2 ++ right/src/macros/core.h | 11 +++++++++-- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/right/src/macros/core.c b/right/src/macros/core.c index ce745c755..ea7700c04 100644 --- a/right/src/macros/core.c +++ b/right/src/macros/core.c @@ -60,6 +60,8 @@ macro_state_t *S = NULL; macro_history_t MacroHistory[MACRO_HISTORY_POOL_SIZE]; uint8_t MacroHistoryPosition = 0; +macro_argument_t MacroArguments[MAX_MACRO_ARGUMENT_POOL_SIZE]; + static void checkSchedulerHealth(const char* tag); static void wakeMacroInSlot(uint8_t slotIdx); static void scheduleSlot(uint8_t slotIdx); diff --git a/right/src/macros/core.h b/right/src/macros/core.h index babc46053..377dab97e 100644 --- a/right/src/macros/core.h +++ b/right/src/macros/core.h @@ -21,7 +21,9 @@ #define MACRO_HISTORY_POOL_SIZE 16 #define MACRO_SCOPE_STATE_POOL_SIZE (MACRO_STATE_POOL_SIZE*2) #define MAX_REG_COUNT 32 - #define MAX_MACRO_ARGUMENT_SIZE 8 + + #define MAX_MACRO_ARGUMENT_COUNT 8 + #define MAX_MACRO_ARGUMENT_POOL_SIZE 32 #define ALTMASK (HID_KEYBOARD_MODIFIER_LEFTALT | HID_KEYBOARD_MODIFIER_RIGHTALT) #define CTRLMASK (HID_KEYBOARD_MODIFIER_LEFTCTRL | HID_KEYBOARD_MODIFIER_RIGHTCTRL) @@ -141,6 +143,11 @@ macro_arg_type_t type; } ATTR_PACKED macro_arg_t; + typedef struct { + uint8 id; + macro_arg_type_t type; + } ATTR_PACKED macro_argref_t; + struct macro_state_t { // local scope data macro_scope_state_t *ls; @@ -173,7 +180,7 @@ bool autoRepeatInitialDelayPassed: 1; macro_autorepeat_state_t autoRepeatPhase: 1; // ---- 4-aligned ---- - macro_arg_t arguments[MAX_MACRO_ARGUMENT_SIZE]; + macro_argref_t arguments[MAX_MACRO_ARGUMENT_COUNT]; macro_usb_keyboard_reports_t reports; From aa66476b357ab426a762f02544ec365b814adbbf Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Sat, 7 Feb 2026 17:22:29 +0100 Subject: [PATCH 22/26] refactor & fix ConsumeUntilDot --- right/src/macros/commands.c | 3 ++- right/src/macros/vars.c | 3 ++- right/src/str_utils.c | 39 +++++++++++++++++++++++++++++++++++-- 3 files changed, 41 insertions(+), 4 deletions(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index ca6e669a7..b360aba52 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -976,6 +976,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) // see if the argument has a type macro_arg_type_t argType; + // TODO: use ConsumeToken(ctx, ":") instead if (*ctx->at == ':') { ctx->at++; const char *typeStart = ctx->at; @@ -1008,7 +1009,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) ConsumeWhite(ctx); } - // rest of command is descriptive label, ignored by firmware + // rest of command is descriptive label, ignore while (Macros_ConsumeCharOfString(ctx, &stringOffset, &textIndex, &textSubIndex) != '\0') {}; return MacroResult_Header; diff --git a/right/src/macros/vars.c b/right/src/macros/vars.c index 727bf8889..d8df05628 100644 --- a/right/src/macros/vars.c +++ b/right/src/macros/vars.c @@ -198,7 +198,8 @@ static macro_variable_t* consumeVarAndAllocate(parser_context_t* ctx) } } - CTX_COPY(bakCtx, *ctx); + // TODO: Is this needed at all? Looks like something left over + // CTX_COPY(bakCtx, *ctx); macro_variable_t configVal = Macro_TryReadConfigVal(ctx); if (configVal.type != MacroVariableType_None) { diff --git a/right/src/str_utils.c b/right/src/str_utils.c index 2446cc3d3..7fce0075e 100644 --- a/right/src/str_utils.c +++ b/right/src/str_utils.c @@ -271,13 +271,11 @@ static bool isIdentifierChar(char c) } } - bool IsIdentifierChar(char c) { return isIdentifierChar(c); } - bool ConsumeIdentifierByRef(parser_context_t* ctx, string_ref_t ref) { const char* at = ctx->at; @@ -320,6 +318,9 @@ void ConsumeAnyIdentifier(parser_context_t* ctx) consumeWhite(ctx); } +#if 0 +/* old ConsumeUntilDot (replaced below) */ + void ConsumeUntilDot(parser_context_t* ctx) { while(*ctx->at > 32 && *ctx->at != '.' && !isEnd(ctx)) { @@ -330,6 +331,40 @@ void ConsumeUntilDot(parser_context_t* ctx) } ctx->at++; } +#endif + +// Consume characters until a specific character is found or whitespace is hit. +// If end of context is reached, report an error. +// If the character is found, consume it. +// If whitespace is found, and failOnWhite is true, report an error. +void ConsumeUntilCharOrWhite(parser_context_t* ctx, char c, bool failOnWhite) +{ + while(*ctx->at > 32 && *ctx->at != c && !isEnd(ctx)) { + ctx->at++; + } + if (IsEnd(ctx)) { + Macros_ReportError("unexpected end of statement", ctx->at, ctx->at); + return; + } + if (*ctx->at == c) { + ctx->at++; + return; + } + if (failOnWhite) { + Macros_ReportErrorPrintf(ctx->at, "'%c' expected", c); + return; + } +} + +void ConsumeUntilDot(parser_context_t* ctx) +{ + ConsumeUntilCharOrWhite(ctx, '.', true); +} + +void ConsumeUntilColon(parser_context_t* ctx) +{ + ConsumeUntilCharOrWhite(ctx, ':', false); +} bool TokenMatches(const char *a, const char *aEnd, const char *b) { From f1d866b7b9565d758ee9f882eab57b8a037ad36a Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Sat, 7 Feb 2026 17:30:02 +0100 Subject: [PATCH 23/26] fix typo --- right/src/macros/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/right/src/macros/core.h b/right/src/macros/core.h index 377dab97e..54a626a97 100644 --- a/right/src/macros/core.h +++ b/right/src/macros/core.h @@ -144,7 +144,7 @@ } ATTR_PACKED macro_arg_t; typedef struct { - uint8 id; + uint8_t id; macro_arg_type_t type; } ATTR_PACKED macro_argref_t; From 04d5aad45af8697e4ec04d213bca53c5f008ce35 Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Sat, 7 Feb 2026 17:35:04 +0100 Subject: [PATCH 24/26] fix typo --- right/src/macros/core.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/right/src/macros/core.h b/right/src/macros/core.h index 54a626a97..831ffa3ad 100644 --- a/right/src/macros/core.h +++ b/right/src/macros/core.h @@ -141,7 +141,7 @@ typedef struct { string_ref_t id; macro_arg_type_t type; - } ATTR_PACKED macro_arg_t; + } ATTR_PACKED macro_argument_t; typedef struct { uint8_t id; From 36ff834c3951683a3126c31a48f9b4afdd8f39ec Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Sun, 8 Feb 2026 12:01:58 +0100 Subject: [PATCH 25/26] simplify processMacroArgCommand; refrain from accessing *ctx->at directly --- right/src/macros/commands.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index b360aba52..fc2859d03 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -976,11 +976,7 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) // see if the argument has a type macro_arg_type_t argType; - // TODO: use ConsumeToken(ctx, ":") instead - if (*ctx->at == ':') { - ctx->at++; - const char *typeStart = ctx->at; - + if (ConsumeToken(ctx, ":")) { if (ConsumeToken(ctx, "int")) { argType = MacroArgType_Int; } @@ -1006,7 +1002,6 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) } else { argType = MacroArgType_Any; - ConsumeWhite(ctx); } // rest of command is descriptive label, ignore From 3c96cec9b05fad54acb37faeb80c25495431c4fc Mon Sep 17 00:00:00 2001 From: Maximilian Hantsch Date: Sun, 8 Feb 2026 13:20:29 +0100 Subject: [PATCH 26/26] refactor white processing for macroArg, safer check for comment --- right/src/macros/commands.c | 8 ++++++++ right/src/str_utils.c | 23 +++++++++++++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/right/src/macros/commands.c b/right/src/macros/commands.c index fc2859d03..6837da447 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -1001,9 +1001,17 @@ static macro_result_t processMacroArgCommand(parser_context_t* ctx) } } else { + if (!IsWhite(ctx)) { + Macros_ReportErrorTok(ctx, "Superfluous non-identifier characters:"); + return MacroResult_Header; + } + ConsumeWhite(ctx); argType = MacroArgType_Any; } + // now allocate argument slot and store the argument info + // TODO: ... + // rest of command is descriptive label, ignore while (Macros_ConsumeCharOfString(ctx, &stringOffset, &textIndex, &textSubIndex) != '\0') {}; diff --git a/right/src/str_utils.c b/right/src/str_utils.c index 7fce0075e..10533a667 100644 --- a/right/src/str_utils.c +++ b/right/src/str_utils.c @@ -13,7 +13,7 @@ #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif -ATTR_UNUSED static parser_context_t parserContextStack[PARSER_CONTEXT_STACK_SIZE]; +static parser_context_t parserContextStack[PARSER_CONTEXT_STACK_SIZE]; static bool consumeCommentsAsWhite = true; @@ -133,13 +133,31 @@ bool IsEnd(parser_context_t* ctx) { return isEnd(ctx); } +static bool isCommentLeader(parser_context_t* ctx) { + return ctx->at + 1 < ctx->end && ctx->at[0] == '/' && ctx->at[1] == '/'; +} + +static bool isWhite(parser_context_t* ctx) { + if (*ctx->at <= 32) { + return true; + } + if (isCommentLeader(ctx)) { + return true; + } + return false; +} + +bool IsWhite(parser_context_t* ctx) { + return isWhite(ctx); +} + static void consumeWhite(parser_context_t* ctx) { while (!isEnd(ctx)) { while (*ctx->at <= 32 && !isEnd(ctx)) { ctx->at++; } - if (ctx->at[0] == '/' && ctx->at[1] == '/' && consumeCommentsAsWhite) { + if (isCommentLeader(ctx) && consumeCommentsAsWhite) { while (*ctx->at != '\n' && !isEnd(ctx)) { ctx->at++; } @@ -176,6 +194,7 @@ void UnconsumeWhite(parser_context_t* ctx) } } +// dangerous due to static return buffer; only use for error messages! const char* OneWord(parser_context_t* ctx) { static char buffer[20];