Rule Cascade
ReferenceDecision records (ADRs)

ADR 0006: matches accepts one portable subset of regular expressions, with one meaning

Status: accepted

Status: accepted

Context

1.0.0-alpha.1 asked authors to stay within "the syntax shared by RE2, Java, JavaScript and Python". Nothing enforced it, and shared syntax is not shared meaning. $ also matches before a final line break in Python and Java and not in JavaScript or Go. Which line terminators . excludes depends on the engine. \d and \w cover Unicode in Python and ASCII in JavaScript; \s is a different set in all four. Go refuses back-references, look-around and counts above 1000. A pattern could pass in the browser and fail on the server for the same input.

Decision

Specification section 4.4 defines the pattern language: literals, . (any code point, line breaks included), ^ and $ (the very start and end), \d \D \w \W (ASCII), \t \n \r, escaped syntax characters, character classes, groups, alternation and bounded repetition. Counts, nested counts multiplied, and the pattern length are each limited to 1000. Everything else is rejected.

Each runtime has a small scanner that accepts exactly this subset, and translates an accepted pattern into the dialect of the engine it uses. A pattern must be a string literal, and one outside the subset fails at load (PATTERN_NOT_PORTABLE) and at evaluation, in every runtime, whether or not the local engine could run it. The patterns in the ruleset schema follow the same rule.

Consequences

  • A pattern means the same in the browser and on the server; the conformance suite has the cases.
  • Authors lose \s, \b, look-around, back-references and flags. White space is written out, and case-insensitive matching needs lower or explicit classes.
  • rulecheck derive skips OpenAPI patterns outside the subset and says so.
  • Every runtime carries the scanner in addition to its regular-expression engine.
  • The subset bounds what a pattern can ask for. It does not make backtracking engines linear: SECURITY.md says what the host still has to do.

Alternatives considered

  • Document the pitfalls and trust authors, as before. The differences are invisible until two runtimes disagree about a customer.
  • One engine everywhere, for example RE2. A dependency for the Java library, which has none, and for the Python package, and an engine to download in the browser.
  • Our own matcher in every runtime. One meaning by construction, but four implementations of a regular-expression engine to keep identical instead of four scanners.
  • No patterns, only string operators. Too weak for formats such as postal codes and SWIFT codes.

On this page