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 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 d4b4a0622..725e30407 100644 --- a/right/src/macros/commands.c +++ b/right/src/macros/commands.c @@ -925,6 +925,81 @@ static macro_result_t processPlayMacroCommand(parser_context_t* ctx) return res ? MacroResult_Blocking : MacroResult_Finished; } +static macro_result_t processMacroArgCommand(parser_context_t* ctx) +{ + uint16_t stringOffset = 0; + 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 + + while (Macros_ConsumeCharOfString(ctx, &stringOffset, &textIndex, &textSubIndex) != '\0') {}; + + return MacroResult_Header; + } + // parse the argument name (identifier) + const char *idStart = ctx->at; + const char *idEnd = IdentifierEnd(ctx); + + if (idEnd == idStart) { + Macros_ReportErrorPos(ctx, "Expected identifier"); + return MacroResult_Header; + } + ctx->at = idEnd; + + // see if the argument has a type + macro_arg_type_t argType; + + if (ConsumeToken(ctx, ":")) { + 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_ReportErrorTok(ctx, "Unrecognized macroArg argument type:"); + return MacroResult_Header; + } + } + 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') {}; + + return MacroResult_Header; + +// Macros_ReportErrorPrintf(ctx->at, "Parsing failed at '%s'?", OneWord(ctx)); +} + static macro_result_t processWriteCommand(parser_context_t* ctx) { if (Macros_DryRun) { @@ -1995,6 +2070,395 @@ static macro_result_t processZephyrCommand(parser_context_t* ctx) { } \ break; +static macro_result_t dispatchCommand(parser_context_t* ctx, command_id_t commandId) { + // 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: + 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; + } + + // 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) { const char* cmdTokEnd = TokEnd(ctx->at, ctx->end); @@ -2016,388 +2480,18 @@ 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_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); + macro_result_t res = dispatchCommand(ctx, entry->id); - // brace commands - case CommandId_openBrace: - return processOpeningBraceCommand(ctx); - case CommandId_closeBrace: - return processClosingBraceCommand(ctx); - - default: - Macros_ReportErrorTok(ctx, "Unrecognized command:"); + 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; + } } + //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 1e17d4c99..b0b05ed05 100644 --- a/right/src/macros/core.c +++ b/right/src/macros/core.c @@ -32,7 +32,6 @@ macro_reference_t AllMacros[MacroIndex_MaxCount] = { }; uint8_t AllMacrosCount; - bool Macros_WakedBecauseOfOneShot = false; bool Macros_WakedBecauseOfTime = false; bool Macros_WakedBecauseOfKeystateChange = false; @@ -62,6 +61,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 154a29e9e..b0667ad16 100644 --- a/right/src/macros/core.h +++ b/right/src/macros/core.h @@ -22,6 +22,9 @@ #define MACRO_SCOPE_STATE_POOL_SIZE (MACRO_STATE_POOL_SIZE*2) #define MAX_REG_COUNT 32 + #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) #define SHIFTMASK (HID_KEYBOARD_MODIFIER_LEFTSHIFT | HID_KEYBOARD_MODIFIER_RIGHTSHIFT) @@ -135,6 +138,16 @@ uint8_t macroIndex; } macro_history_t; + typedef struct { + string_ref_t id; + macro_arg_type_t type; + } ATTR_PACKED macro_argument_t; + + typedef struct { + uint8_t id; + macro_arg_type_t type; + } ATTR_PACKED macro_argref_t; + struct macro_state_t { // local scope data macro_scope_state_t *ls; @@ -169,8 +182,11 @@ bool isDoubletap: 1; secondary_role_state_t secondaryRoleState: 2; // ---- 4-aligned ---- + macro_argref_t arguments[MAX_MACRO_ARGUMENT_COUNT]; macro_usb_keyboard_reports_t reports; + + bool macroHeadersProcessed : 1; } ms; // action scope data diff --git a/right/src/macros/typedefs.h b/right/src/macros/typedefs.h index 67854c16f..818bee0bb 100644 --- a/right/src/macros/typedefs.h +++ b/right/src/macros/typedefs.h @@ -14,7 +14,6 @@ // Typedefs: - typedef enum { MacroSubAction_Tap, MacroSubAction_Press, @@ -24,6 +23,7 @@ } macro_sub_action_t; typedef enum { + MacroResult_None = 0, MacroResult_InProgressFlag = 1, MacroResult_ActionFinishedFlag = 2, MacroResult_DoneFlag = 4, @@ -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; @@ -48,6 +49,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/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..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]; @@ -271,13 +290,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 +337,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 +350,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) { diff --git a/right/src/str_utils.h b/right/src/str_utils.h index 0341d18da..a282bdef8 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 {