[libc-commits] [libc] [libc][regex] Add Kleene star and canonical AST normalisation (PR #233980)

Alex Strelnikov via libc-commits libc-commits at lists.llvm.org
Thu Oct 22 07:57:22 PDT 2026


================
@@ -59,70 +60,115 @@ constexpr bool is_unimplemented_ere_escape(char c) {
   }
 }
 
-} // namespace
-
-ErrorOr<ExprId> parse_ere(cpp::string_view pattern, int cflags,
-                          ExprPool &pool) {
-  ErrorOr<ExprId> cur = pool.empty_str();
-  if (!cur.has_value())
-    return cur;
-
-  cpp::string_view rem = pattern;
-  while (!rem.empty()) {
-    char c = rem.front();
+// Parses a single ERE atom and an optional trailing '*' quantifier.
+ErrorOr<ExprId> parse_quantified_atom(cpp::string_view &rem, int cflags,
+                                      ExprPool &pool) {
+  char c = rem.front();
+  rem.remove_prefix(1);
+  ErrorOr<ExprId> atom = INVALID_EXPR_ID;
+  bool can_repeat = true;
+  switch (c) {
+  case '\\': {
+    if (rem.empty())
+      return Error(REG_EESCAPE);
+    c = rem.front();
     rem.remove_prefix(1);
-    ErrorOr<ExprId> atom = INVALID_EXPR_ID;
-    switch (c) {
-    case '\\': {
-      if (rem.empty())
-        return Error(REG_EESCAPE);
-      c = rem.front();
-      rem.remove_prefix(1);
-      if (is_unimplemented_ere_escape(c))
-        return Error(REG_BADPAT);
-      atom = pool.make_lit(c);
-      break;
-    }
-    case '.': {
-      ByteSet bs;
-      if (cflags & REG_NEWLINE) {
-        bs.set_range(0, '\n' - 1);
-        bs.set_range('\n' + 1, BYTE_SET_BITS - 1);
-      } else {
-        bs.flip();
-      }
-      atom = pool.make_byte_set(bs);
-      break;
-    }
-    case '^':
-      atom = pool.make_anchor(ANCHOR_BOL);
-      break;
-    case '$':
-      atom = pool.make_anchor(ANCHOR_EOL);
-      break;
-    // TODO: Wire ERE metacharacters as each operator is implemented in
-    // subsequent patches.
-    case '*':
-    case '+':
-    case '?':
-    case '(':
-    case ')':
-    case '[':
-    case '{':
-    case '|':
+    if (is_unimplemented_ere_escape(c))
       return Error(REG_BADPAT);
-    default:
-      atom = pool.make_lit(c);
-      break;
+    atom = pool.make_lit(c);
+    break;
+  }
+  case '.': {
+    ByteSet bs;
+    if (cflags & REG_NEWLINE) {
+      bs.set_range(0, '\n' - 1);
+      bs.set_range('\n' + 1, BYTE_SET_BITS - 1);
+    } else {
+      bs.flip();
     }
+    atom = pool.make_byte_set(bs);
+    break;
+  }
+  case '^':
+    atom = pool.make_anchor(ANCHOR_BOL);
+    can_repeat = false;
+    break;
+  case '$':
+    atom = pool.make_anchor(ANCHOR_EOL);
+    can_repeat = false;
+    break;
+  // Valid postfix '*' is consumed after a repeatable atom below; reaching a
+  // quantifier at the start of an atom means it has no preceding repeatable
+  // atom (e.g. "*a", "^*", or doubled "a**").
+  case '*':
+  case '+':
+  case '?':
+    return Error(REG_BADRPT);
+  case '{':
+    if (!rem.empty() && rem.front() >= '0' && rem.front() <= '9')
+      return Error(REG_BADRPT);
+    return Error(REG_BADPAT);
+  // TODO: Wire ERE metacharacters as each operator is implemented in
+  // subsequent patches.
+  case '(':
+  case ')':
+  case '[':
+  case '|':
+    return Error(REG_BADPAT);
+  default:
+    atom = pool.make_lit(c);
+    break;
+  }
 
+  if (!atom.has_value() || !can_repeat)
+    return atom;
+  if (rem.starts_with('*')) {
+    rem.remove_prefix(1);
+    return pool.make_star(*atom);
+  }
+  if (rem.starts_with('+') || rem.starts_with('?') || rem.starts_with('{'))
+    return Error(REG_BADPAT);
+  return atom;
+}
+
+// Folds concatenated atoms right-to-left in 64-atom (256-byte) stack chunks so
+// right-associative make_concat stays O(1) without orphaned prefixes and
+// recursion is bounded to <= 157 frames across MAX_NODE_LIMIT.
----------------
strel-12 wrote:

Nit: If `MAX_NODE_LIMIT` is ever updated, I think it would be easy to forget to update the `157`, so how about `⌈MAX_NODE_LIMIT / 64⌉` instead?

https://github.com/llvm/llvm-project/pull/233980


More information about the libc-commits mailing list