Skip to content

Fix string.gsub replacement strings and %f; enable pm.lua - #366

Merged
akeit0 merged 1 commit into
nuskey8:mainfrom
billythekidz:fix/gsub-invalid-capture-index
Oct 23, 2026
Merged

akeit0 merged 1 commit into
nuskey8:mainfrom
billythekidz:fix/gsub-invalid-capture-index

Conversation

@billythekidz

Copy link
Copy Markdown
Contributor

Three problems in the pattern library, found by running tests-lua/pm.lua (currently commented out in LuaTests). The expected values in the new tests were taken from Lua 5.2.4.

string.gsub replacement strings

The replacement was built with chained StringBuilder.Replace calls. As a result:

  • string.gsub("alo", "(.)", "%2") returned a string instead of raising invalid capture index (pm.lua:208).
  • %x and a trailing % were accepted instead of raising invalid use of '%' in replacement string.
  • captured text was scanned again by later replacements, and a NUL in captured text became % (the NUL was used as a temporary marker).

The replacement is now expanded in a single pass, like add_s in lstrlib.c.

%f at the end of the subject

%f[set] never matched at the end of the subject, because the current character was required to exist. Lua uses '\0' there, so string.gsub("function", "%f[^\1-\255]", ".") must give "function." (pm.lua:291).

%f without a set

%f not followed by [ silently failed to match. It now raises missing '[' after '%f' in pattern.

Verification

  • tests-lua/pm.lua is enabled in LuaTests and passes. On main it fails at line 208.
  • New GSubReplacementStringTests: 8 of the 17 test cases fail on main (with the source change reverted), and all pass with it.
  • dotnet test tests/Lua.Tests/Lua.Tests.csproj -c Release --filter "TestCategory!=ExpectedFailure": 628 passed, 0 failed (610 on main + 17 new + pm.lua enabled).
  • dotnet csharpier check . is clean.
  • Run on macOS (arm64), .NET 8 runtime only.

gsub built the replacement with chained StringBuilder.Replace calls, so a
reference to a missing capture (%2) was silently kept, a bare or unknown
%x was accepted, captured text was rescanned, and NUL characters in
captured text turned into '%'. Replacement strings are now expanded in a
single pass and follow Lua 5.2 (invalid capture index, invalid use of '%'
in replacement string).

%f never matched at the end of the subject because the current character
was required to exist. Lua treats it as '\0' there. %f without a set now
raises "missing '[' after '%f' in pattern".

tests-lua/pm.lua passes and is enabled.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Changes recommended

The helper insertion moves the async-builder attribute off GSub, introducing allocation overhead when asynchronous replacements suspend.

1 open issue
What changed in this PR

This PR improves Lua 5.2 compatibility in the string pattern library and enables the previously disabled pattern-matching suite.

Changes:

  • Expands string.gsub replacement strings in a single pass, preserving captured text and validating replacement escapes.
  • Fixes frontier patterns at the end of a subject and reports missing frontier sets.
  • Adds 17 regression cases and enables tests-lua/pm.lua.
File Description
tests/​Lua.Tests/​LuaTests.cs Enables the Lua pattern-matching suite.
tests/​Lua.Tests/​GSubReplacementStringTests.cs Tests replacement strings, errors, and frontier behavior.
src/​Lua/​Standard/​StringLibrary.cs Introduces single-pass replacement expansion.
src/​Lua/​Standard/​Internal/​MatchState.cs Corrects frontier boundary matching and validation.

Review effort: Balanced

}

[AsyncMethodBuilder(typeof(LightAsyncValueTaskMethodBuilder<>))]
static void AppendReplacement(
akeit0 added a commit that referenced this pull request Oct 23, 2026
Integrate #364–#366 with rename safety, w+ seek and gsub allocation fixes
@akeit0
akeit0 merged commit 3fadc2b into nuskey8:main Oct 23, 2026
@akeit0

akeit0 commented Oct 23, 2026

Copy link
Copy Markdown
Collaborator

Thank you for the replacement-string and frontier fixes, the focused regression tests, and enabling pm.lua! Your original commit has been merged through #368 with its authorship preserved. The single-pass replacement parser also substantially improved our capture-heavy microbenchmarks.

Our follow-up restores the pooled async-method builder attribute on GSub, avoiding an extra 256 bytes per suspended call, and returns Lua's expected unfinished-capture error when a replacement references an incomplete capture. We added reusable benchmarks and regression coverage; the combined changes passed local tests and Linux CI.

@akeit0 akeit0 mentioned this pull request Oct 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants