{{ message }}
Repository navigation
Drafft: Merge runs of simple RegexLexer rules into one regex at build time - #3413
Open
eendebakpt wants to merge 1 commit into
Open
eendebakpt wants to merge 1 commit into
eendebakpt wants to merge 1 commit into
Conversation
RegexLexer.get_tokens_unprocessed tries each rule of the current state in
turn at every position, so a state with N rules costs up to N re.match()
calls per token. A run of consecutive simple rules (a regex and a plain
token type, no state change) is equivalent to the single rule
('(?P<g0>r0)|(?P<g1>r1)|...', merged(t0, t1, ...))
because regex alternation is tried in order, like the rule loop.
Rather than merging at runtime in the metaclass (PR pygments#3155), this does it
at build time, as the maintainers preferred: scripts/optimize_lexers.py
rewrites the lexer modules with an AST-guided text edit that only touches
the merged rules, and the hatchling hook in hatch_build.py runs it when a
wheel is built. The sources in the repository and editable installs are
unchanged, so development runs the unmerged rules; PYGMENTS_OPTIMIZE_LEXERS=0
builds a wheel without the optimization.
Rules whose regex uses a named group, backreference, conditional or global
inline flag are left alone, as are lexers using re.VERBOSE and the classes
whose rules other code inspects (PostgresLexer, TeraTermLexer,
Inform6Lexer). The merged action class lives in pygments.lexer.
The result: 6692 rules merged into 1527 regexes in 216 modules, and with
the benchmark from pygments#3155 (pyperf, Python 3.13) lexing is 1.17x to 1.42x
faster, 1.25x geometric mean. A new test-wheel CI job runs the whole test
suite against the built wheel.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

This is a variation of #3155, which the rules merged at build time. The performance gain is a bit less, but still nice.
Rather than merging at runtime in the metaclass (PR #3155), this does it at build time: scripts/optimize_lexers.py rewrites the lexer modules with an AST-guided text edit that only touches the merged rules, and the hatchling hook in hatch_build.py runs it when a wheel is built. The sources in the repository and editable installs are unchanged, so development runs the unmerged rules; PYGMENTS_OPTIMIZE_LEXERS=0 builds a wheel without the optimization.
Rules whose regex uses a named group, backreference, conditional or global inline flag are left alone, as are lexers using re.VERBOSE and the classes whose rules other code inspects (PostgresLexer, TeraTermLexer, Inform6Lexer). The merged action class lives in pygments.lexer.
The result: 6692 rules merged into 1527 regexes in 216 modules, and with the benchmark from #3155 (pyperf, Python 3.13) lexing is 1.17x to 1.42x faster, 1.25x geometric mean. A new test-wheel CI job runs the whole test suite against the built wheel.