{"id":13872272,"url":"https://github.com/kodlian/SwiftRegexDSL","last_synced_at":"2025-07-16T02:30:30.375Z","repository":{"id":52284946,"uuid":"295157606","full_name":"kodlian/SwiftRegexDSL","owner":"kodlian","description":"A Declarative Structured Language for regular expressions in Swift.","archived":false,"fork":false,"pushed_at":"2021-09-28T20:57:00.000Z","size":55,"stargazers_count":149,"open_issues_count":0,"forks_count":5,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-30T17:15:06.780Z","etag":null,"topics":["dsl","functionbuilder","regex","resultbuilder","swift"],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kodlian.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-09-13T13:31:12.000Z","updated_at":"2023-09-25T16:40:24.000Z","dependencies_parsed_at":"2022-09-12T12:24:32.306Z","dependency_job_id":null,"html_url":"https://github.com/kodlian/SwiftRegexDSL","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodlian%2FSwiftRegexDSL","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodlian%2FSwiftRegexDSL/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodlian%2FSwiftRegexDSL/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kodlian%2FSwiftRegexDSL/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kodlian","download_url":"https://codeload.github.com/kodlian/SwiftRegexDSL/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":226095632,"owners_count":17572966,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dsl","functionbuilder","regex","resultbuilder","swift"],"created_at":"2024-08-05T23:00:38.397Z","updated_at":"2024-11-23T20:30:50.575Z","avatar_url":"https://github.com/kodlian.png","language":"Swift","funding_links":[],"categories":["Swift"],"sub_categories":[],"readme":"# SwiftRegexDSL\n[![License](https://img.shields.io/badge/license-MIT-blue.svg?style=flat)](http://mit-license.org)\n[![Language](http://img.shields.io/badge/language-swift-orange.svg?style=flat)](https://developer.apple.com/swift)\n\nMost of us, Swift developers, are not using and creating regular expressions on day to day basis. But each time we have to, we rely on web search, old documentations. Then we have to deal with unsafety and perform many runs before achieving the expected result. It feels like a heavy rollback of what we are used to when coding with modern language such as Swift.\n\nWe can emphasize two issues here:\n- in Swift, regexes are strings, which result in no compile-time check, type-safe, and code completion.\n- by nature, regexes are mostly write-only things. Unless you make an effort or this is something you use often, this will never be as easy to understand as the rest of your codebase.   \n\nThis brings SwiftRegexDSL, a Declarative Structured Language for regular expressions in Swift. The idea is to leverage the same \"magic\" that powers SwiftUI, Result Builder (https://github.com/apple/swift-evolution/blob/main/proposals/0289-result-builders.md) to regex. The DSL provides readable expressions, far more suitable for composition, control flows, in addition to bringing compile-time checks. To summarise, fewer headaches with regex! \n\n```swift\nstruct ThisIsARegex: Regex {\n  let shouldMatchLine: Bool\n\n  var body: Regex {\n    \"Hello\"\n    WhiteSpace()\n    \"World,\"\n    if shouldMatchLine {\n      Line()\n    }\n    AnyCharacter()\n      .oneOrMore()\n  }\n}\n...\nlet regex = ThisIsARegex(shouldMatchLine: false)\n\"Hello World, how...\".match(regex) // true\n```\n\n## Installation\nSwiftRegex is bundled as a Swift package, you can simply add it from XCode in your iOS or macOS project in `File \u003e Swift Packages \u003e Add Package Dependency` and looking for `https://github.com/kodlian/SwiftRegexDSL.git`\n\nIf you are doing things outside Xcode,  add it the dependencies section in `Package.swift`\n\n```swift\ndependencies: [\n\ndependencies: [\n    .package(url: \"https://github.com/kodlian/SwiftRegexDSL.git\", .upToNextMajor(from: \"1.0.0\"))\n]\n```\n\n## How to?\nLike SwiftUI view you declare your regex as a struct preferably in a separate file and use the body to build the regex. \n```swift\nimport SwiftRegexDSL\n\nstruct MyRegex: Regex {\n  var body: Regex {\n    Digit()\n     .oneOrMore()  \n    Word()\n  }\n}\n\n```\n\nYou can also annotate any variables or functions with `@RegexBuilder`:\n```\n@RegexBuilder\nvar digits: Regex {\n  Digit()\n   ..exactly(10) \n}\n```\n\n### Pattern\nSwiftRegexDSL implements the most common pattern found in the [`ICU`](https://unicode-org.github.io/icu/userguide/strings/regexp.html) API supported by the Swift Foundation.\n\n#### The basic\nMost common character classes and special characters are supported such as `AnyCharacter`, `Digit`, `NotDigit`, `Word`, `Whitespace`. Please refer to `CharacterClass.swift` and `SpecialCharacters.swift` to see the full coverage. \n\nOf course, you can add any strings in your regex body, either directly or by using the `Text` regex.\n```swift\nvar body: Regex {\n \"Title\"\n Text(\"-\") \n  .quantified(0..\u003c2)\n Digit()\n}\n```\nTake notice that a `String` is not a `Regex` component per se, but rather an expression convertible to a Regex. This means If you need to apply a modifier wrap it in a `Text`.\n\n#### Quantifier\nYou can attach a quantifier using the `quantified(...)`  modifier or any shortcuts `zeroOrMore`,  `oneOrMore`,  `zeroOrOne`,  `exactly`  to specify the number of occurrences a pattern should match.\n```swift\nvar body: Regex {\n Text(\"-\")\n   .zeroOrOne()  \n Digit()\n   .quantified(1..\u003c4)\n}\n```\n\n#### Group and Assertion\nGrouping for readability and for applying a modifier can be added to the body of regex using `Group`.\n```swift\nDigit()\nGroup {\n   Word()\n   Digit()\n}.zeroOrMore()\n...\n```\nIn addition, the DSL supports:\n- A or B pattern using `Alternative` regex\n- Assertions using `LookAheadAssertion`, `NegativeLookAheadAssertion`,... to match but by not advancing the input position \n- Capturing group `CaptureGroup` for retrieving a range matching a subexpression\n- Applying pattern option such as `caseInsensitive` using the `.options(...)` modifier on any pattern\n\n#### Regex Set\nSet can be defined as Array or using Swift Set using either `Character` or range of characters.\n```swift\nDigit()\n[`a`,`c`...`z`]\n...\n```\nExcluding set can be created using `ExclusionSet` structure.\n\n#### Anchor \nAnchor to match a particular area of the input string using  either `StartAnchor` or `EndAnchor` can be added in the body\n```swift\nStartAnchor.line\nDigit()\n...\n```\n\n#### Unicode\nThe DSL supports pattern by Unicode name, hexadecimal or property using `UnsafeUnicode`. Although it is considered to be unsafe as parameters are strings and not bound for hexadecimal.\n\n#### Composition, Parametrisation and Custom\nSwiftRegexDSl is designed to be extensible, you compose your regex using other regexes:\n```swift\nimport SwiftRegexDSL\n\nstruct DomainRegex: Regex { ... }\nstruct ExtensionRegex: Regex { ... }\n\nstruct HostRegex: Regex {\n  var body: Regex {\n     DomainRegex()\n     \".\"\n     ExtensionRegex()\n  }\n}\n\n```\n\nA regex is defined as a `Struct` and the DSL supports control flows, it is easy to define parameters as a type property:\n```swift\nimport SwiftRegexDSL\n\nstruct TitleRegex: Regex {\n\n  let shouldStartWithDigit: Bool\n  \n  var body: Regex {\n    if shouldStartWithDigit {\n      Digit()\n        .oneOrMore()  \n    }\n    AnyCharacter()\n      .oneOrMore()\n  }\n}\n```\n\nIf the framework is missing something such as a regex metacharacters, you can use a `UsafeRawText` in your regex body as `Texts` in the DSL are automatically escaped for safety.\nIf such a case appears, don't hesitate to contribute to the framework to improve the coverage of the regex standard. \n\n### Regex usage\nWhen your regex is ready, the framework offers various extensions on `String`:\n- check if a string match a regex by using `.match(regex)`\n- find the range in string matching the regex using  `.range(of: regex)` \n- find ranges of capturing groups  `.capturedGroupsRanges(of: regex, with: \"foo\")` \n- replace a part of a string using `.replacingOccurrences(of: regex, with: \"foo\")`\n\n`NSRegularExpression` can also be created from a `Regex`. \n\n## Future Direction and final note\nIt is a young project and many improvements can be done: \n- Unicode regex by name or property offers tons of possibilities. Having an enum to describe the most used ones could be a nice addition, offering safety and discoverability.\n-  Retrieving the ranges of capturing groups is still not very convenient with the framework. Perhaps a system could exist where a binding or callback is directly defined within the body of the Regex. \n- Matching digit is easy with Regex, but a number is always a pain. Take for instance IPV4 where each part should not be superior to 255 resulting in quite a long regex hard to read and painful to create, here the way to match 0 to 255:\n```25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?```\nCreating a regex matching closed range of numbers will be a time saver. \n- Anchors are currently a little bit raw and can be anywhere in an expression. There is certainly more safety and convenience to add around them.\n- Set are limited to `Character`, which for now for digits we need to use the `Character` representation of this digit.\n\nFinal word, I am not an expert in Regex hence the existence of this framework to ease my pain working with them: so I may have missed and done some mistakes. By making it open source, I hope the swift community contributions will bring it to the next level.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkodlian%2FSwiftRegexDSL","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkodlian%2FSwiftRegexDSL","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkodlian%2FSwiftRegexDSL/lists"}