Skip to content

Commit e728c71

Browse files
authored
Refactor C++ regex parser into shared core + EcmaRegExp dialect hooks
1 parent e0cc679 commit e728c71

2 files changed

Lines changed: 711 additions & 361 deletions

File tree

cpp/ql/lib/semmle/code/cpp/regex/RegexFlowConfigs.qll

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -375,6 +375,57 @@ predicate hasNonEcmaScriptGrammarFlag(StringLiteral regex) {
375375
)
376376
}
377377

378+
// ---------------------------------------------------------------------------
379+
// Grammar classification
380+
// ---------------------------------------------------------------------------
381+
382+
/**
383+
* The `std::regex` grammar dialects that the C++ regex parser is aware of.
384+
*
385+
* - `Ecma()` — ECMAScript, the default grammar used by `std::regex` and the
386+
* only grammar currently parsed. Selected either implicitly
387+
* (no explicit grammar flag) or explicitly via
388+
* `std::regex_constants::ECMAScript`.
389+
* - `Bre()` — POSIX Basic Regular Expressions (selected via the `basic` or
390+
* `grep` flags).
391+
* - `Ere()` — POSIX Extended Regular Expressions (selected via the
392+
* `extended`, `egrep`, or `awk` flags).
393+
*
394+
* `Bre()` and `Ere()` are scaffolding for future phases that will add POSIX
395+
* grammar support. Today the parser still excludes non-ECMAScript regexes
396+
* (see `hasNonEcmaScriptGrammarFlag`), so in practice every parsed regex
397+
* classifies as `Ecma()` and only the `Ecma()` branch of `regexGrammar` is
398+
* exercised — the `Bre()`/`Ere()` branches exist purely so the classifier
399+
* agrees with the flag-detection helpers below.
400+
*/
401+
newtype TRegexGrammar =
402+
Ecma() or
403+
Bre() or
404+
Ere()
405+
406+
/**
407+
* Gets the `std::regex` grammar dialect of `regex`, inferred from its
408+
* construction-site `syntax_option_type` flag argument (if any).
409+
*
410+
* The mapping is:
411+
* - `basic` / `grep` → `Bre()`
412+
* - `extended` / `egrep` / `awk` → `Ere()`
413+
* - anything else (default, explicit `ECMAScript`, or unresolved) → `Ecma()`
414+
*
415+
* Because the parser still gates on `not hasNonEcmaScriptGrammarFlag`, every
416+
* literal that is actually parsed today classifies as `Ecma()`. The
417+
* `Bre()`/`Ere()` cases are defined only so the classifier is complete for
418+
* later phases; those branches are not exercised by the current parser.
419+
*/
420+
TRegexGrammar regexGrammar(StringLiteral regex) {
421+
if containsRegexFlag(getConstructionFlagArg(regex), ["basic", "grep"])
422+
then result = Bre()
423+
else
424+
if containsRegexFlag(getConstructionFlagArg(regex), ["extended", "egrep", "awk"])
425+
then result = Ere()
426+
else result = Ecma()
427+
}
428+
378429
/**
379430
* Holds if `regex` is constructed with an explicit ECMAScript grammar flag.
380431
* This is the default, and also the case that the Phase 1 parser handles.

0 commit comments

Comments
 (0)