{
  "schemaVersion": 1,
  "datasetVersion": "1.0.0",
  "sources": {
    "swift-type": {
      "title": "Regex type and captures (SE-0350)",
      "url": "https://github.com/swiftlang/swift-evolution/blob/main/proposals/0350-regex-type-overview.md"
    },
    "unicode": {
      "title": "Unicode for String Processing (SE-0363)",
      "url": "https://github.com/swiftlang/swift-evolution/blob/main/proposals/0363-unicode-for-string-processing.md"
    },
    "algorithms": {
      "title": "Regex-powered string processing algorithms (SE-0357)",
      "url": "https://github.com/swiftlang/swift-evolution/blob/main/proposals/0357-regex-string-processing-algorithms.md"
    },
    "foundation": {
      "title": "NSRegularExpression API reference",
      "url": "https://developer.apple.com/documentation/foundation/nsregularexpression"
    }
  },
  "cases": [
    {
      "id": "named-date-captures",
      "topic": "captures",
      "title": "Inspect year and month captures",
      "note": "Group 0 is the full match; numbered groups retain the named year and month text.",
      "pattern": "(?<year>\\d{4})-(?<month>\\d{2})",
      "input": "on 2026-10",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "2026-10",
                  "utf16Range": [
                    3,
                    7
                  ]
                },
                {
                  "text": "2026",
                  "utf16Range": [
                    3,
                    4
                  ]
                },
                {
                  "text": "10",
                  "utf16Range": [
                    8,
                    2
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "2026-10",
                  "utf16Range": [
                    3,
                    7
                  ]
                },
                {
                  "text": "2026",
                  "utf16Range": [
                    3,
                    4
                  ]
                },
                {
                  "text": "10",
                  "utf16Range": [
                    8,
                    2
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "optional-absent-capture",
      "topic": "captures",
      "title": "An optional capture can be absent",
      "note": "The first match has no group 1 range. Selecting group 1 in the app shows one participating range across these two matches.",
      "pattern": "(a)?b",
      "input": "b ab",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "b",
                  "utf16Range": [
                    0,
                    1
                  ]
                },
                {
                  "text": null,
                  "utf16Range": null
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "ab",
                  "utf16Range": [
                    2,
                    2
                  ]
                },
                {
                  "text": "a",
                  "utf16Range": [
                    2,
                    1
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "b",
                  "utf16Range": [
                    0,
                    1
                  ]
                },
                {
                  "text": null,
                  "utf16Range": null
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "ab",
                  "utf16Range": [
                    2,
                    2
                  ]
                },
                {
                  "text": "a",
                  "utf16Range": [
                    2,
                    1
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "empty-participating-capture",
      "topic": "captures",
      "title": "An empty capture still has a range",
      "note": "Group 1 participates with zero length, which differs from an absent optional capture.",
      "pattern": "(a*)b",
      "input": "b",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "b",
                  "utf16Range": [
                    0,
                    1
                  ]
                },
                {
                  "text": "",
                  "utf16Range": [
                    0,
                    0
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "b",
                  "utf16Range": [
                    0,
                    1
                  ]
                },
                {
                  "text": "",
                  "utf16Range": [
                    0,
                    0
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "repeated-capture",
      "topic": "captures",
      "title": "A repeated group retains its final capture",
      "note": "The overall match contains three letters; group 1 refers to the final letter.",
      "pattern": "(a)+",
      "input": "aaa",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "aaa",
                  "utf16Range": [
                    0,
                    3
                  ]
                },
                {
                  "text": "a",
                  "utf16Range": [
                    2,
                    1
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "aaa",
                  "utf16Range": [
                    0,
                    3
                  ]
                },
                {
                  "text": "a",
                  "utf16Range": [
                    2,
                    1
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "noncapturing-prefix",
      "topic": "captures",
      "title": "A noncapturing group adds no capture slot",
      "note": "Only the trailing c becomes group 1.",
      "pattern": "(?:ab)(c)",
      "input": "abc",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "abc",
                  "utf16Range": [
                    0,
                    3
                  ]
                },
                {
                  "text": "c",
                  "utf16Range": [
                    2,
                    1
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "abc",
                  "utf16Range": [
                    0,
                    3
                  ]
                },
                {
                  "text": "c",
                  "utf16Range": [
                    2,
                    1
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "ordered-alternation",
      "topic": "matching",
      "title": "Search accepts the first successful alternative",
      "note": "A search for cat or cater accepts cat here. Whole-input matching has a separate fixture.",
      "pattern": "(cat|cater)",
      "input": "cater",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                },
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                },
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "numeric-backreference",
      "topic": "captures",
      "title": "A backreference repeats captured text",
      "note": "Group 1 supplies the text required after the hyphen.",
      "pattern": "(\\w+)-\\1",
      "input": "go-go",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "go-go",
                  "utf16Range": [
                    0,
                    5
                  ]
                },
                {
                  "text": "go",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "go-go",
                  "utf16Range": [
                    0,
                    5
                  ]
                },
                {
                  "text": "go",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "dot-decomposed-accent",
      "topic": "unicode",
      "title": "Dot counts graphemes in Swift by default",
      "note": "Swift consumes e plus combining acute together. Foundation returns two scalar matches; both report UTF-16 ranges.",
      "pattern": ".",
      "input": "é",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": true,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "é",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "e",
                  "utf16Range": [
                    0,
                    1
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "́",
                  "utf16Range": [
                    1,
                    1
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "dot-flag",
      "topic": "unicode",
      "title": "A flag emoji contains two regional indicators",
      "note": "Swift returns one flag match. Foundation dot returns two regional-indicator matches, each spanning two UTF-16 units.",
      "pattern": ".",
      "input": "🇸🇬",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": true,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "🇸🇬",
                  "utf16Range": [
                    0,
                    4
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "🇸",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "🇬",
                  "utf16Range": [
                    2,
                    2
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "canonical-equivalence",
      "topic": "unicode",
      "title": "A composed pattern matches decomposed text in Swift",
      "note": "The returned text preserves the input scalars. Foundation does not match this composed pattern against decomposed input.",
      "pattern": "é",
      "input": "é",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": true,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "é",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "noMatch",
          "matches": []
        }
      }
    },
    {
      "id": "scalar-dot",
      "topic": "unicode",
      "title": "Swift scalar mode changes dot consumption",
      "note": "Swift matchingSemantics(.unicodeScalar) returns separate e and accent matches, agreeing with Foundation for this input. The app has no scalar-mode control.",
      "pattern": ".",
      "input": "é",
      "operation": "all",
      "options": [
        "unicodeScalar"
      ],
      "appWorkflow": "engineOnly",
      "versionSensitive": true,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "e",
                  "utf16Range": [
                    0,
                    1
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "́",
                  "utf16Range": [
                    1,
                    1
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "e",
                  "utf16Range": [
                    0,
                    1
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "́",
                  "utf16Range": [
                    1,
                    1
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "crlf-character-class",
      "topic": "unicode",
      "title": "A CRLF pair can be one character-class member",
      "note": "This pattern spells CR then LF within the class. The observation applies to this pattern and runtime; compare the newline-sequence fixture.",
      "pattern": "[\r\n]",
      "input": "\r\n",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": true,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "\r\n",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "\r",
                  "utf16Range": [
                    0,
                    1
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "\n",
                  "utf16Range": [
                    1,
                    1
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "newline-sequences",
      "topic": "unicode",
      "title": "Backslash R preserves a CRLF pair",
      "note": "Both APIs find two newline sequences and keep the CRLF pair together.",
      "pattern": "\\R",
      "input": "a\r\nb\n",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": true,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "\r\n",
                  "utf16Range": [
                    1,
                    2
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "\n",
                  "utf16Range": [
                    4,
                    1
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "\r\n",
                  "utf16Range": [
                    1,
                    2
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "\n",
                  "utf16Range": [
                    4,
                    1
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "unicode-decimal-digits",
      "topic": "unicode",
      "title": "Backslash d accepts Arabic-Indic digits",
      "note": "Both APIs accept the Arabic-Indic and ASCII digit runs.",
      "pattern": "\\d+",
      "input": "٣٤ 42",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": true,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "٣٤",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "42",
                  "utf16Range": [
                    3,
                    2
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "٣٤",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "42",
                  "utf16Range": [
                    3,
                    2
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "ascii-digit-contract",
      "topic": "validation",
      "title": "An ASCII range excludes Arabic-Indic digits",
      "note": "Use the ASCII range when the format requires ASCII digits; searching still extracts the later ASCII run.",
      "pattern": "[0-9]+",
      "input": "٣٤ 42",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": true,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "42",
                  "utf16Range": [
                    3,
                    2
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "42",
                  "utf16Range": [
                    3,
                    2
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "unicode-word",
      "topic": "unicode",
      "title": "Backslash w includes an accented letter",
      "note": "Both APIs match café as one word run on this runtime.",
      "pattern": "\\w+",
      "input": "café",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": true,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "café",
                  "utf16Range": [
                    0,
                    4
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "café",
                  "utf16Range": [
                    0,
                    4
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "unicode-ignore-case",
      "topic": "options",
      "title": "Case-insensitive matching includes accented letters",
      "note": "The option matches CAFÉ while preserving the input text in the capture.",
      "pattern": "café",
      "input": "CAFÉ",
      "operation": "all",
      "options": [
        "caseInsensitive"
      ],
      "appWorkflow": "matching",
      "versionSensitive": true,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "CAFÉ",
                  "utf16Range": [
                    0,
                    4
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "CAFÉ",
                  "utf16Range": [
                    0,
                    4
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "dot-excludes-newline",
      "topic": "options",
      "title": "Dot excludes a line feed by default",
      "note": "Neither API matches a newline with dot under the default options.",
      "pattern": "a.b",
      "input": "a\nb",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "noMatch",
          "matches": []
        },
        "foundation": {
          "status": "noMatch",
          "matches": []
        }
      }
    },
    {
      "id": "dot-all",
      "topic": "options",
      "title": "Dot-all permits a line feed",
      "note": "The Swift dotMatchesNewlines and Foundation dotMatchesLineSeparators options both permit this match.",
      "pattern": "a.b",
      "input": "a\nb",
      "operation": "all",
      "options": [
        "dotAll"
      ],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "a\nb",
                  "utf16Range": [
                    0,
                    3
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "a\nb",
                  "utf16Range": [
                    0,
                    3
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "multiline-anchors",
      "topic": "options",
      "title": "Multiline anchors can select an interior line",
      "note": "The multiline option changes the anchors, allowing the middle b line to match.",
      "pattern": "^b$",
      "input": "a\nb\nc",
      "operation": "all",
      "options": [
        "multiline"
      ],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "unicode",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "b",
                  "utf16Range": [
                    2,
                    1
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "b",
                  "utf16Range": [
                    2,
                    1
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "whole-token-rejects-extraction",
      "topic": "validation",
      "title": "Finding digits does not validate the whole input",
      "note": "Whole-input matching rejects a digit run with a preceding ID label. Foundation uses a noncapturing wrapper with absolute start/end anchors, recorded in apis.",
      "pattern": "\\d+",
      "input": "ID:42",
      "operation": "whole",
      "options": [],
      "appWorkflow": "engineOnly",
      "versionSensitive": false,
      "sources": [
        "algorithms",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "noMatch",
          "matches": []
        },
        "foundation": {
          "status": "noMatch",
          "matches": []
        }
      }
    },
    {
      "id": "whole-token-backtracks-alternative",
      "topic": "validation",
      "title": "Whole-input matching can try a longer alternative",
      "note": "Both whole-input operations select ab, even though a first search could accept a.",
      "pattern": "(a|ab)",
      "input": "ab",
      "operation": "whole",
      "options": [],
      "appWorkflow": "engineOnly",
      "versionSensitive": false,
      "sources": [
        "algorithms",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "ab",
                  "utf16Range": [
                    0,
                    2
                  ]
                },
                {
                  "text": "ab",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "ab",
                  "utf16Range": [
                    0,
                    2
                  ]
                },
                {
                  "text": "ab",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "first-extracts-digits",
      "topic": "validation",
      "title": "First-match search extracts a substring",
      "note": "Both first-match APIs return 42 and leave the surrounding text outside the match.",
      "pattern": "\\d+",
      "input": "ID:42,7",
      "operation": "first",
      "options": [],
      "appWorkflow": "engineOnly",
      "versionSensitive": false,
      "sources": [
        "algorithms",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "42",
                  "utf16Range": [
                    3,
                    2
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "42",
                  "utf16Range": [
                    3,
                    2
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "lookahead-context",
      "topic": "context",
      "title": "Lookahead checks following context without consuming it",
      "note": "The match contains the amount; the following space and USD remain outside its range.",
      "pattern": "\\d+(?= USD)",
      "input": "42 USD",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "42",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "42",
                  "utf16Range": [
                    0,
                    2
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "lookbehind-local-support",
      "topic": "support",
      "title": "Test this lookbehind against the recorded runtime",
      "note": "On the recorded environment Swift rejects this pattern during compilation; Foundation matches 42. Retest this pattern when using another Swift version.",
      "pattern": "(?<=\\$)\\d+",
      "input": "cost $42",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": true,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "compileError",
          "matches": []
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "42",
                  "utf16Range": [
                    6,
                    2
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "capture-context-alternative",
      "topic": "context",
      "title": "Capture the amount after a dollar sign",
      "note": "Both APIs match the dollar sign and amount, with only the amount in group 1. In the app, choose capture group 1 to inspect the amount.",
      "pattern": "\\$(\\d+)",
      "input": "cost $42",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "$42",
                  "utf16Range": [
                    5,
                    3
                  ]
                },
                {
                  "text": "42",
                  "utf16Range": [
                    6,
                    2
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "$42",
                  "utf16Range": [
                    5,
                    3
                  ]
                },
                {
                  "text": "42",
                  "utf16Range": [
                    6,
                    2
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "malformed-group",
      "topic": "errors",
      "title": "An unclosed group is a compilation error",
      "note": "Neither API reaches matching. A compilation error must not become an empty successful match list.",
      "pattern": "(",
      "input": "abc",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "compileError",
          "matches": []
        },
        "foundation": {
          "status": "compileError",
          "matches": []
        }
      }
    },
    {
      "id": "invalid-backreference",
      "topic": "errors",
      "title": "A nonexistent capture reference is rejected",
      "note": "Both APIs reject the reference to group 2 in this pattern on the recorded environment.",
      "pattern": "(a)\\2",
      "input": "aa",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": true,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "compileError",
          "matches": []
        },
        "foundation": {
          "status": "compileError",
          "matches": []
        }
      }
    },
    {
      "id": "possessive-quantifier",
      "topic": "matching",
      "title": "A possessive quantifier does not give a letter back",
      "note": "The possessive repetition consumes the available letters, so the final a cannot match.",
      "pattern": "a*+a",
      "input": "aaa",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "noMatch",
          "matches": []
        },
        "foundation": {
          "status": "noMatch",
          "matches": []
        }
      }
    },
    {
      "id": "literal-replacement",
      "topic": "replacement",
      "title": "Replace with a literal dollar expression",
      "note": "Swift inserts $1 literally. Foundation receives escapedTemplate(for:) so its replacement is also literal. This does not call the app replacement parser.",
      "pattern": "(\\w+)",
      "input": "cat",
      "operation": "replaceLiteral",
      "options": [],
      "appWorkflow": "engineOnly",
      "versionSensitive": false,
      "sources": [
        "algorithms",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                },
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                }
              ]
            }
          ],
          "replacement": "$1"
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                },
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                }
              ]
            }
          ],
          "replacement": "$1"
        }
      },
      "replacement": "$1"
    },
    {
      "id": "replacement-api-syntax",
      "topic": "replacement",
      "title": "The same replacement string has different API meanings",
      "note": "Swift replacing with a string inserts $1 literally; Foundation withTemplate expands group 1. This compares replacement APIs, not different regex matching results or the app parser.",
      "pattern": "(\\w+)",
      "input": "cat",
      "operation": "replaceAPISyntax",
      "options": [],
      "appWorkflow": "engineOnly",
      "versionSensitive": false,
      "sources": [
        "algorithms",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                },
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                }
              ]
            }
          ],
          "replacement": "$1"
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                },
                {
                  "text": "cat",
                  "utf16Range": [
                    0,
                    3
                  ]
                }
              ]
            }
          ],
          "replacement": "cat"
        }
      },
      "replacement": "$1"
    },
    {
      "id": "capture-replacement",
      "topic": "replacement",
      "title": "Build a replacement from captured fields",
      "note": "The Swift closure joins capture 2, a space, and capture 1. Foundation uses $2 $1. The fixture names both recipes; the app has its own template parser.",
      "pattern": "(\\w+), (\\w+)",
      "input": "Lovelace, Ada",
      "operation": "replaceCaptures",
      "options": [],
      "appWorkflow": "engineOnly",
      "versionSensitive": false,
      "sources": [
        "algorithms",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "Lovelace, Ada",
                  "utf16Range": [
                    0,
                    13
                  ]
                },
                {
                  "text": "Lovelace",
                  "utf16Range": [
                    0,
                    8
                  ]
                },
                {
                  "text": "Ada",
                  "utf16Range": [
                    10,
                    3
                  ]
                }
              ]
            }
          ],
          "replacement": "Ada Lovelace"
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "Lovelace, Ada",
                  "utf16Range": [
                    0,
                    13
                  ]
                },
                {
                  "text": "Lovelace",
                  "utf16Range": [
                    0,
                    8
                  ]
                },
                {
                  "text": "Ada",
                  "utf16Range": [
                    10,
                    3
                  ]
                }
              ]
            }
          ],
          "replacement": "Ada Lovelace"
        }
      }
    },
    {
      "id": "zero-width-lookahead",
      "topic": "matching",
      "title": "A zero-width match has a participating empty range",
      "note": "Both APIs return two empty group 0 ranges before the letters. No characters are consumed by the lookahead.",
      "pattern": "(?=a)",
      "input": "aa",
      "operation": "all",
      "options": [],
      "appWorkflow": "matching",
      "versionSensitive": false,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "",
                  "utf16Range": [
                    0,
                    0
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "",
                  "utf16Range": [
                    1,
                    0
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "",
                  "utf16Range": [
                    0,
                    0
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "",
                  "utf16Range": [
                    1,
                    0
                  ]
                }
              ]
            }
          ]
        }
      }
    },
    {
      "id": "empty-engine-pattern",
      "topic": "errors",
      "title": "An empty engine pattern can match boundaries",
      "note": "Swift matches three boundaries on this runtime; Foundation rejects the empty pattern during compilation. The app handles an empty pattern as an empty workspace state before compiling it.",
      "pattern": "",
      "input": "ab",
      "operation": "all",
      "options": [],
      "appWorkflow": "engineOnly",
      "versionSensitive": true,
      "sources": [
        "swift-type",
        "foundation"
      ],
      "expected": {
        "swift": {
          "status": "match",
          "matches": [
            {
              "captures": [
                {
                  "text": "",
                  "utf16Range": [
                    0,
                    0
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "",
                  "utf16Range": [
                    1,
                    0
                  ]
                }
              ]
            },
            {
              "captures": [
                {
                  "text": "",
                  "utf16Range": [
                    2,
                    0
                  ]
                }
              ]
            }
          ]
        },
        "foundation": {
          "status": "compileError",
          "matches": []
        }
      }
    }
  ]
}
