diff --git a/right/src/str_utils.c b/right/src/str_utils.c index 93ccf9d36..fa3368c9f 100644 --- a/right/src/str_utils.c +++ b/right/src/str_utils.c @@ -136,11 +136,15 @@ bool IsEnd(parser_context_t* ctx) { static void consumeWhite(parser_context_t* ctx) { while (!isEnd(ctx)) { - while (*ctx->at <= 32 && !isEnd(ctx)) { + // isEnd() may pop an exhausted parser context (e.g. an expandCodes/ + // template expansion), moving ctx->at to the parent's next token. It + // must be checked *before* dereferencing ctx->at, otherwise a ctx->at++ + // here would step over that token's first character and drop it. + while (!isEnd(ctx) && *ctx->at <= 32) { ctx->at++; } if (ctx->at[0] == '/' && ctx->at[1] == '/' && consumeCommentsAsWhite) { - while (*ctx->at != '\n' && !isEnd(ctx)) { + while (!isEnd(ctx) && *ctx->at != '\n') { ctx->at++; } } @@ -322,7 +326,7 @@ void ConsumeAnyIdentifier(parser_context_t* ctx) void ConsumeUntilDot(parser_context_t* ctx) { - while(*ctx->at > 32 && *ctx->at != '.' && !isEnd(ctx)) { + while(!isEnd(ctx) && *ctx->at > 32 && *ctx->at != '.') { ctx->at++; } if (*ctx->at != '.') { diff --git a/right/src/test_suite/CMakeLists.txt b/right/src/test_suite/CMakeLists.txt index 66c648e46..f600bfccd 100644 --- a/right/src/test_suite/CMakeLists.txt +++ b/right/src/test_suite/CMakeLists.txt @@ -19,4 +19,5 @@ target_sources(${PROJECT_NAME} PRIVATE tests/test_sticky.c tests/test_playtime.c tests/test_transport.c + tests/test_tapkeyseq.c ) diff --git a/right/src/test_suite/tests/test_tapkeyseq.c b/right/src/test_suite/tests/test_tapkeyseq.c new file mode 100644 index 000000000..6194e9562 --- /dev/null +++ b/right/src/test_suite/tests/test_tapkeyseq.c @@ -0,0 +1,44 @@ +#include "tests.h" + +// Regression test for a template expansion bug. +// +// `tapKeySeq x y hexCodeOf(!) x y` would produce "xy21y" due to wrong ConsumeWhite/isEnd expansion. + +static const test_action_t test_tapkeyseq_hexcode[] = { + TEST_SET_MACRO("u", + "tapKeySeq x y hexCodeOf(!) x y\n" + ), + TEST_PRESS______("u"), + TEST_DELAY__(20), + // x + TEST_EXPECT__________("x"), + TEST_EXPECT__________(""), + // y + TEST_EXPECT__________("y"), + TEST_EXPECT__________(""), + // hexCodeOf(!) -> 2 1 + TEST_EXPECT__________("2"), + TEST_EXPECT__________(""), + TEST_EXPECT__________("1"), + TEST_EXPECT__________(""), + // x + TEST_EXPECT__________("x"), + TEST_EXPECT__________(""), + // y (the final key of the sequence — the one the bug dropped) + TEST_EXPECT__________("y"), + TEST_EXPECT__________(""), + TEST_RELEASE__U("u"), + TEST_DELAY__(20), + TEST_EXPECT__________(""), + TEST_END() +}; + +static const test_t tapkeyseq_tests[] = { + { .name = "tapkeyseq_hexcode", .actions = test_tapkeyseq_hexcode }, +}; + +const test_module_t TestModule_TapKeySeq = { + .name = "TapKeySeq", + .tests = tapkeyseq_tests, + .testCount = sizeof(tapkeyseq_tests) / sizeof(tapkeyseq_tests[0]) +}; diff --git a/right/src/test_suite/tests/tests.c b/right/src/test_suite/tests/tests.c index cfd57fa29..ca5769e3e 100644 --- a/right/src/test_suite/tests/tests.c +++ b/right/src/test_suite/tests/tests.c @@ -18,6 +18,7 @@ const test_module_t * const AllTestModules[] = { &TestModule_Sticky, &TestModule_Playtime, &TestModule_Transport, + &TestModule_TapKeySeq, }; const uint16_t AllTestModulesCount = sizeof(AllTestModules) / sizeof(AllTestModules[0]); diff --git a/right/src/test_suite/tests/tests.h b/right/src/test_suite/tests/tests.h index 9f8812468..360a489f3 100644 --- a/right/src/test_suite/tests/tests.h +++ b/right/src/test_suite/tests/tests.h @@ -32,5 +32,6 @@ extern const test_module_t TestModule_ParserBenevolence; extern const test_module_t TestModule_Sticky; extern const test_module_t TestModule_Playtime; extern const test_module_t TestModule_Transport; +extern const test_module_t TestModule_TapKeySeq; #endif