Grammar Formalization: Define Regex Syntax as a Context‑Free Grammar
#141 aperta il 27 nov 2025
Metriche repository
- Star
- (109 stelle)
- Metriche merge PR
- (Metriche PR in attesa)
Descrizione
(This issue is AI-generated an may contain an error. Please comment on the issue to discuss what we'll prove)
Difficulty: Intermediate (Phase 1, 2), Advanced (Phase 3, 4) Area: Regex syntax / parsing / formal verification
Summary
This issue proposes to formally specify the concrete syntax of the regex patterns language (things like [^a-c\d]+) as a context‑free grammar (CFG), and to connect it to the existing parser in regex/Regex/Syntax/Parser.
The end goal is to have:
- A clear grammar specification for the regex syntax used by this library.
- A corresponding
Mathlib.Computability.ContextFreeGrammarvalue capturing the same language as a context-free grammar.
Stretch goals include defining LL(k) languages, checking whether this grammar is LL(1) (or close to it), and (eventually) rewriting the parser with a correctness proof against the formal grammar.
Current Status
Currently:
- ✅ We have a working recursive‑descent parser implemented in
regex/Regex/Syntax/Parser/Basic.leanand its combinator infrastructure in
regex/Regex/Syntax/Parser/Combinators*.lean. - ✅ We have an AST for regex syntax in
regex/Regex/Syntax/Ast.lean, and a translation fromAstto the semanticExpr(Ast.toRegex). - ✅ The parser supports a reasonably rich syntax:
- Primary constructs:
- Literal characters
- Character classes
[ ... ](with ranges, negation, Perl classes inside, etc.) - Dot
.(any char except newline, per current semantics) - Anchors:
^,$,\b,\B - Grouping:
(...) - Non‑capturing groups:
(?:...)
- Operators:
- Alternation:
| - Concatenation (implicit)
- Alternation:
- Repetition:
*,+,?{n},{n,},{n,m}- Greedy / non‑greedy via trailing
?
- Escapes:
- Simple escapes:
\n,\t,\r,\a,\f,\v,\0,\-,\],\}, and any special metacharacter escaped. - Hex escapes:
\xNN,\uNNNN - Perl classes:
\d,\D,\s,\S,\w,\W
- Simple escapes:
- Primary constructs:
- ❌ There is no single, formal grammar (BNF / CFG) in the code or docs that precisely describes the accepted concrete syntax. Rather, the grammar is defined by the implementation.
- ❌ There is no
ContextFreeGrammarvalue in Mathlib corresponding to this syntax. - ❌ There is no machine‑checked statement that “the parser recognizes exactly the language of this grammar”.
This issue aims to fill that gap.
Goals
- Define a formal grammar of the regex concrete syntax (the syntax of strings like
[^a-c\d]+), ideally:- As a human‑readable BNF (or equivalent) in this repository.
- As a Lean definition closely mirroring that BNF.
- Confirm that the grammar is context‑free, and encode it as a
Mathlib.Computability.ContextFreeGrammar:- Choose suitable types of terminals / nonterminals.
- Provide the production rules as a value of
ContextFreeGrammar. - Show that this value generates exactly the intended language of regex patterns.
Stretch goals:
- Define LL(k) languages and check whether this grammar is LL(1) (or close to LL(1)):
- Formally define LL(k) in Lean (if not already provided).
- Analyze / approximate FIRST / FOLLOW sets for this grammar.
- Determine whether the grammar as written is LL(1), LL(k) for some
k, or not LL(k) at all.
- Rewrite the parser and prove that it correctly implements the grammar:
- Implement a new parser (e.g., a recursive-decent parser following the LL(1) structure) and prove:
- Soundness: every parsed AST comes from a word in the CFG language.
- Completeness: every word in the CFG language can be parsed into an AST accepted by the parser.
- Implement a new parser (e.g., a recursive-decent parser following the LL(1) structure) and prove:
Target Syntax (Informal Overview)
The formal grammar should match the current behavior of Regex.Syntax.Parser.Basic as closely as possible.
However, if small tweaks to the behavior makes the grammar context-free or LL(1), it's okay to change the grammar (please discuss with me).
Roughly, the surface language includes:
- Top‑level structure:
- A regex is an alternation of concatenations, where concatenations are sequences of repeated primaries.
- Primaries (
Primaryin the informal grammar below):Char(ordinary characters, excluding metacharacters unless escaped).(dot)- Anchors:
^,$,\b,\B - Character classes:
[ ... ]with:- Optional leading
^(negation). - Single characters.
- Ranges
a-z. - Perl classes inside ranges cause errors (per implementation), so the grammar for valid patterns must account for that.
- It's permissible to accept it in the grammar, but add a validation phase to reject it later.
- Optional leading
- Groups:
- Capturing:
( R ) - Non‑capturing:
(?: R )
- Capturing:
- Escaped characters and classes:
- Standard escapes (
\n,\t,\\, etc.). - Hex and Unicode escapes (
\xNN,\uNNNN). - Perl classes
\d,\s,\wand their negations.
- Standard escapes (
- Repetition operators:
*,+,?{n},{n,},{n,m}with trailing?for non‑greedy.
- Alternation and concatenation:
A | Bbinds weaker than concatenation; concatenation is implicit between primaries / repetitions.
Proposed Formal Grammar (BNF Sketch)
The final grammar may evolve during the work, but a starting point for the core structure could look like:
Regex ::= Alternate
Alternate ::= Concat ('|' Concat)*
Concat ::= Repetition*
Repetition ::= Primary RepetitionOp*
RepetitionOp
::= '*' '?'?
| '+' '?'?
| '?' '?'?
| '{' Number (',' Number?)? '}' '?'?
Primary
::= Char
| '.'
| Anchor
| Class
| Group
| EscapedChar
Group
::= '(' Regex ')'
| '(?:' Regex ')'
Anchor
::= '^'
| '$'
| '\b'
| '\B'
Class
::= '[' '^'? ClassItem+ ']'
ClassItem
::= Range
| ClassAtom
Range
::= ClassAtom '-' ClassAtom
ClassAtom
::= ClassChar
| PerlClass
EscapedChar
::= SimpleEscape
| HexEscape
| UnicodeEscape
| PerlClass
PerlClass
::= '\d' | '\D' | '\s' | '\S' | '\w' | '\W'
Number ::= Digit+
Digit ::= '0' | '1' | ... | '9'
(* Details of Char, ClassChar, SimpleEscape, HexEscape, UnicodeEscape, etc. should be specified. *)
This grammar description should:
- Be made precise enough that we can directly translate it into Lean.
- Explicitly document any intentional exclusions (e.g., invalid ranges, forbidden Perl classes in ranges) as either:
- Part of the grammar of valid patterns, or
- Separate “well‑formedness” predicates on parsed ASTs.
Context‑Free Grammar in Mathlib
Mathlib provides Computability.ContextFreeGrammar (and related definitions) for representing context‑free grammars in Lean. The goal here is to:
- Choose types:
- Terminals: likely some representation of input characters / tokens (
Chardirectly, or a token type if we want to separate lexing). - Nonterminals: an inductive type such as:
inductive Nonterminal | regex | alternate | concat | repetition | primary | group | class | classItem | range | ...
- Terminals: likely some representation of input characters / tokens (
- Instantiate a
ContextFreeGrammarvalue:start := Nonterminal.regexproductionslisting all the rules corresponding to the BNF.
- Relate the grammar to the concrete syntax:
- If terminals are
Char, the language is a subset ofList Char. - If a separate token type is introduced, the grammar will be over token lists, and we will separately specify a lexer from
Stringto token lists.
- If terminals are
At the end of this phase, we should have:
- A Lean definition
regexGrammar : ContextFreeGrammar Nonterminal Terminal(names TBD). - A brief explanation / comments linking each production back to its BNF counterpart and to the corresponding part of the existing parser.
Implementation Tasks
Below is a suggested phasing of the work. PRs do not need to strictly follow these phases, but this gives a rough roadmap.
Phase 1: Formal Grammar Definition at the BNF Level
- Carefully read the existing parser (
Parser.Basic) and produce a list of accepted syntactic forms. - Based on that analysis, write a BNF or EBNF‑style grammar for the regex syntax in this repository:
- Use the sketch in this issue as a starting point and refine / extend it.
- Decide whether to encode certain error cases (e.g. reversed ranges, Perl classes in ranges) as forbidden by the grammar or as allowed by the grammar but rejected later by a validation phase.
- For each nonterminal / production in the BNF, add comments indicating which Lean definitions (
primary,repetition,concat,alternate,group,classes, etc.) correspond to it.
Phase 2: Grammar as Mathlib.Computability.ContextFreeGrammar
- Design and define the types
NonterminalandTerminal. - Use Mathlib’s
ContextFreeGrammarstructure to define a grammarregexGrammar : ContextFreeGrammar Terminal Nonterminalcorresponding to the BNF. - Check (informally) whether the language generated by
regexGrammarmatches the BNF‑specified regex syntax.
Phase 3 (Stretch): LL(k) Languages and LL(1) Check
- Define LL(k) languages in Lean (or reuse existing definitions if they already exist).
- For
regexGrammar:- Add definitions / lemmas that correspond to FIRST / FOLLOW sets or similar analyses.
- Investigate whether the grammar is LL(1), LL(k) for some
k, or not LL(k) at all; at minimum, identify where LL(1)‑ness fails.
Phase 4 (Stretch): Parser Rewrite and Correctness Proof
- Design a new parser whose structure directly reflects
regexGrammar, e.g. a recursive‑descent parser with one function per nonterminal. - Prove correctness of this parser with respect to the CFG:
- Soundness: whenever the parser succeeds and returns an AST, the input string is in the language generated by
regexGrammar. - Completeness: whenever a string is in the language generated by
regexGrammar, the parser succeeds and produces an AST.
- Soundness: whenever the parser succeeds and returns an AST, the input string is in the language generated by
- (Ambitious optional extension) Define a pretty‑printer from ASTs back to strings, and explore round‑trip properties (e.g. that parsing then pretty‑printing yields a string in the same equivalence class of the grammar).
Acceptance Criteria
- The concrete regex syntax has a complete BNF (or equivalent formal grammar) checked into the repository.
- The relationship between the BNF and the existing parser (
Parser.Basic) / AST (Ast) is explained via comments. - A
Mathlib.Computability.ContextFreeGrammarvalue (e.g.regexGrammar) is defined that represents this grammar. - It is made clear, at least informally (and ideally with some Lean support), that this grammar is context‑free.
- Stretch: There is some LL(1) / LL(k) analysis of
regexGrammarrecorded in the code or documentation. - Stretch: There is at least a plan, and possibly partial proofs, connecting the new parser to
regexGrammarvia soundness and completeness properties.
Resources & References
regex/Regex/Syntax/Parser/Basic.lean
Main recursive‑descent parser definitions (group,primary,repetition,concat,alternate,regex, etc.).regex/Regex/Syntax/Ast.lean
Syntax treeAstfor regular expressions, andAst.toRegexfor translation to semanticExpr.regex/Regex/Data/Expr.lean
Semantic (expression‑level) representation of regular expressions.- Mathlib: the
Computability.ContextFreeGrammardefinitions and related modules.
(See the Mathlib repository for precise types and APIs.) - Standard references on formal language theory (e.g. Hopcroft–Ullman) and any existing formal grammars for regex‑like languages.
Notes for Contributors
- Difficulty: Intermediate-Advanced
- Prior exposure to formal language theory (CFGs, LL(k), etc.) will help a lot.
- Experience with Lean / Mathlib formalizations is very valuable, but not strictly required; the issue aims to give enough pointers to get started.
- Estimated effort:
- Phases 1–2 (grammar definition + CFG instantiation) are already a non‑trivial project and may take days to weeks of focused effort.
- The stretch goals (LL(1) analysis, parser correctness proofs) are likely to be a longer‑term effort.