Skip to content

SYSTEM Cited by 1 source

parslet

parslet (https://github.com/kschiess/parslet) is a Ruby library for writing PEG (Parsing Expression Grammar) parsers using a small combinator DSL. Grammar rules are defined as Ruby methods on a Parslet::Parser subclass; rule bodies combine primitives (match[], str, maybe, repeat) with sequencing (>>), alternation (|), and capture-into-tree (.as(:name)). The parser emits a parse tree (typically a nested Ruby Hash / Array of Slices) that parslet's Transform class can then rewrite into a domain-specific AST.

This page is a stub created for cross-reference from sources/2025-05-13-github-github-issues-search-now-supports-nested-queries-and-boolean; parslet has many features (left-recursion workarounds, error recovery, multiple transforms) that are not covered here.

Why pick parslet

  • Handles both legacy and new syntax in one grammar. In GitHub Issues' 2025 rewrite, the grammar had to accept both flat assignee:@me label:support new-project queries and nested is:issue AND (author:A OR author:B) queries. PEG's ordered alternation (|) and grammar layering (one rule can dispatch to another) make it natural to add nested syntax as a new entry rule without touching the legacy rule. Backward compat is a grammar- design property, not a runtime-fallback mechanism.
  • Operator precedence falls out of rule layering. The canonical PEG pattern for booleans — root = or_operation, or_operation recurses on and_operation, and_operation recurses on primary, primary parenthesises back to or_operation — encodes both precedence (AND binds tighter than OR) and grouping (parentheses override) without a separate precedence table.

The parslet repo ships this exact pattern as https://github.com/kschiess/parslet/blob/master/example/boolean_algebra.rb. The GitHub Issues post quotes a simplified version directly.

Canonical boolean-algebra grammar (from parslet's example)

class Parser < Parslet::Parser
  rule(:space)  { match[" "].repeat(1) }
  rule(:space?) { space.maybe }

  rule(:lparen) { str("(") >> space? }
  rule(:rparen) { str(")") >> space? }

  rule(:and_operator) { str("and") >> space? }
  rule(:or_operator)  { str("or")  >> space? }

  rule(:var) { str("var") >> match["0-9"].repeat(1).as(:var) >> space? }

  rule(:primary) { lparen >> or_operation >> rparen | var }

  rule(:and_operation) {
    (primary.as(:left) >> and_operator >>
      and_operation.as(:right)).as(:and) |
    primary }

  rule(:or_operation)  {
    (and_operation.as(:left) >> or_operator >>
      or_operation.as(:right)).as(:or) |
    and_operation }

  root(:or_operation)
end

Reading: or_operation — the lowest-precedence root — either matches and_operation or or_operation (right-recursive) or falls through to a bare and_operation. Same shape one level in for and_operation. primary handles parentheses (recursing back to or_operation) or atomic terms.

Seen in

Last updated · 200 distilled / 1,178 read