Skip to content

Optimize string comparisons and parser state management - #297

Open
bartveneman wants to merge 3 commits into
mainfrom
claude/epic-franklin-p9u1nc
Open

bartveneman wants to merge 3 commits into
mainfrom
claude/epic-franklin-p9u1nc

Conversation

@bartveneman

Copy link
Copy Markdown
Member

Summary

This PR optimizes string comparisons throughout the parser to avoid unnecessary substring allocations and improves lexer state management by reusing position objects in hot paths.

Key Changes

String Comparison Optimizations

  • Replaced substring() + str_equals() patterns with str_equals_range() to compare string ranges directly without allocating substrings
  • Added is_and_or_not_range() utility function to efficiently check for logical operators (and, or, not) without allocations
  • Removed strip_vendor_prefix() function and replaced with inline range-based vendor prefix stripping in parse_prelude_dispatch()
  • Updated imports across multiple parser files to use the new range-based comparison functions

Parser Method Refactoring

  • Split parse_prelude() into two methods:
    • parse_prelude(): accepts at-rule name as string
    • parse_prelude_named_in_source(): accepts name as source range, avoiding substring allocation
  • Refactored parse_prelude_dispatch() to work with source ranges instead of pre-extracted strings
  • Changed scan_matching_paren() return type from tuple to boolean, storing results in instance fields (paren_content_end, paren_close_end) to avoid tuple allocations

Lexer State Management

  • Added save_position_into() method to reuse existing LexerPosition objects instead of allocating new ones
  • Added reusable lookahead_position field in DeclarationParser for property-name lookahead snapshots
  • Changed ConditionParser.end_position getter to seek_to_end() method that directly updates target lexer state

Data Structure Changes

  • Converted DECLARATION_AT_RULES from Set to array for simpler range-based lookups
  • Updated atrule_has_declarations() to use range-based string comparison instead of Set lookup

Boundary Trimming Refactoring

  • Replaced trim_boundaries() calls with explicit skip_whitespace_and_comments_forward() and skip_whitespace_and_comments_backward() calls for clearer intent and better control over trimmed ranges

Implementation Details

  • All changes maintain backward compatibility with existing parser behavior
  • Range-based comparisons are case-insensitive for CSS keywords
  • The optimizations particularly benefit hot paths like media query parsing and condition parsing where multiple string comparisons occur
  • Instance fields for parenthesis scanning results avoid temporary tuple allocations in frequently-called methods

https://claude.ai/code/session_01Xv2dWHgzdat1eEeyhYGMbA

Avoid substrings for keyword/function-name checks (compare source ranges instead),
replace tuple-returning helpers with fields, drop the at-rule name string and
trim_boundaries tuple on the hot path, and reuse lexer snapshots in the
declaration parser.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

Copy link
Copy Markdown
Member Author

The Audit packages check fails with 7 advisories (e.g. source-map-js, postcss-selector-parser) in dev-only dependencies (vitest coverage, tailwindcss, postcss). This PR touches only src/ and no package.json or lockfile, so the failure isn't from this change and the same audit would fail on main. No fix exists in this PR; resolving it means a dependency bump in a separate change. All other checks are unaffected.


Generated by Claude Code

@github-actions

github-actions Bot commented Oct 10, 2026 •

Copy link
Copy Markdown
Contributor

⚠️ Package Size Increase

📦 Package 📏 Base Size 📏 Source Size 📈 Size Change
@projectwallace/css-parser 45.9 kB 46.2 kB +308 B

@pkg-pr-new

pkg-pr-new Bot commented Oct 10, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@projectwallace/css-parser@29949dc

commit: 29949dc

At-rule names are rare compared to tokens, so the range-based dispatch wasn't worth
~1.4 kB of extra code. Also build the reusable lexer snapshot without a long literal
and shorten new comments.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
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.

2 participants