# RegExp composition

**URL:** <https://es.discourse.group/t/regexp-composition/1278>\
**Category:** 💡 Ideas\
**Tags:** proposal\
**Created:** [April 4, 2022, 11:48am UTC](https://es.discourse.group/t/regexp-composition/1278 "2022-04-04T11:48:29Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![pygy](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/pygy/32/231_2.png) [@pygy](https://es.discourse.group/u/pygy)\
**Post date:** [April 4, 2022, 11:48am UTC](https://es.discourse.group/t/regexp-composition/1278/1 "2022-04-04T11:48:29Z")

</div>

It would be useful to have a way to compose RegExps without having to resort to splicing source strings. This would let one build complex parsers out of simpler ones without having to think about non-capturing groups, and let one write test for the pieces. Rather than full expressions used in template strings, one could something along `/(?>id)/` where `id` is a JS variable.

I haven't tracked all propositions, but at least two of the current propositions ([pattern modifiers](https://github.com/tc39/proposal-regexp-modifiers) and [set notation](https://github.com/tc39/proposal-regexp-set-notation)) that introduce quite a bit of syntax could be subsumed into this.

- The "modifiers" proposal becomes moot if composition takes into account the flags of the expression that is spliced in.

- The set operations could be implemented as a plain JS API that operates on RegExps that consume exactly one code point.

This would save a lot on syntax.

The RegExp syntax is already unwieldy, it was invented to create write-only searchers at the command line (itself building on a math notation where sub-patterns were abstracted away as single-letter variables), and it shows. Complex RegExps are a nightmare to edit and update.

If one stumbles on a syntactic construct they don't know they're going to have a tough time searching for it. The escaping rules introduced by the set notation proposal makes readability even worse.

OTOH, the regular formalism is all about composition, regular operators are meant to operate on arbitrary expressions, but the current syntax makes it hard to take advantage of.

Here's an [example of a complex RegExp-based parser](https://github.com/npm/node-semver/blob/4907647d169948a53156502867ed679268063a9f/internal/re.js#L19) and here's a [port](https://flems.io/#0=N4IgtglgJlA2CmIBcBGADGgNCAZhBAzsgNqgB2AhmIkiAHQAWALmLCNgMYD2ZT8vyECAC+mclRr0AVkU48+A2gHolAAgAOXAE58oqnFq5hVzJuoJIVAcwhMGAVwBGdbmCVl1bslyjwAtATwYABu8FpKjrBcjkoA7GhQAJw4jgDMOCgcKABMABzZ2bEUqRxJKCi5AGyx8FCOaEUArAAs2ZVoHBRQShC8YZSwSlrwdDIAOmQqqgDCXOoAnloQVsyqABQcAJSqAJIEFBQcqgBadKoAyhwMsPbwfFqqFGR6s7xLjvZM2gQTU8MIFECenszzCqjs8F252mqlgEA4-ECEwm3DIBCYqmAOFgFCsBEwqngtgYYQJBHsODwAA8yfAAI63MgIgmddRMezDYSqAC8qlcmkCACV4FZ4FT1MimTx0aoAF5hOY3Ag81TkykQKlrADkACotZsUdKMWAKPNHJDeWrqdqAPz6w1ojFWKKOCiwABiOKsKuxuII2qs9rIDplqgAsgBBAAaAH1zhH3QBRGPTADyYYACqmAHKJ7MAFRjABk8wBxfMACVUKpQlV+alRMt8H29vOGDIgw21dCUzfsgYNkzUYs0OmVvLAPnsCDoI+0THHmOEIYxc7HKuAy6HqnzJMeHHZbtUw1F4uVVi4qh4hPF84IdGGK+PFpvo4XD5fxAAuk+CFojrya7vn+AGqN+T4YoBt5jnQkFLhMCAYoKKpoJKUw8LA8yqGQ8C1LU+jaKoABCSgAOp8kY6gUBiADuxLgnuHAcsMvCqBGGY7Jg9aqHRdgMZCXS+Ho5o4XgGJcDgFGOn4cIANaQo2TBaBQvQ-Nu14QvoEDwLAUD3txEYaDivRXo4UjwAePFcNOehugQl5QFwdBPlSKo4TRqgZoYVLzGsm4EsAEzVqKTBrEwFBaCFBLqIY6jbIFZDVtWECSWsACE4WRXcjCAqmNFkF5cxhEwvkxXMmzbHYhgee5qiJlohhaGsYwgNml4taoADUGixYOQVJcM7JaIlmUhcQZXqD+iWqKI-WBKFo13NFsUEsEbq3PF-XJTgYURSFOUEHlBWxcVpW9ZVDDVdh8AefVjXNSAwq+HgZC2BAPBIKoLXdRNg5JV903Vopqi9L4rm8oKnWdVtqhTH2VhrKDYqret8B-UlwO+niKprTcIxY2p-1MMQlDULBXAAKrqOoYTTIC8BrJsX4qkjVIwyBxCs8zvK47cdAgTDwyc6CVLc6ovPwJK-2DRyI17Xc42xWLEv9Vuwh9duu6QjgXCwFEdFkN6wpWNOEV1eKwwEAQ71onyTyqOaqj2ECBEPF88mvbKvRWFx2649A1HewSTx6FRWjW4bFxBAAamCoThzbqpKd7+nBtuADE6eqNm9jUEsRw7L4vApdpWgGaq3sIKoAAGaDVwShEUNhPB+PKhiqFAyy2ARetcDR+GOFhbeXoRk7DB3XfvhMVJ0GQueFyqShoAAPsQKB+IkP5jFAOpKNPs-z1ARZcFwgSL8QaAb1+nV72nUyZ9nLdz3n8K7EXTAl2E3HHAqV4PGPkJO42AXASHWvd+7CSwk3RC9w-4mHmOoEkZBg7PH4olYef9uIANhHce4+IJ7APwYRBgCCkGpxnt4MgOcwAL15HvbeOpiAUFbhGPwxw-BfiYSwthl9EgcN3mhNQD8wwqUSrHBOPBuK7mGIArgTAAjwDDtRfCz8wiv2gPwD+eAwjkLoCaXo4jrbXktPSRkCJmrTQoYfX21YtR0C1ASKxNCoA2NUHYhxqgnGFwmIOGe+iyCGJtsfU+L5AgMn4OY-qXij4n0CK49xjiD7OOCXE-qCTPFJMLikyWZANb3yzl5fw-x4D01UIE68hdNGfzLtuQyqj84g3ftUhuDwm6UL8PU9RTTtFaGcmQGeMV4DCgBIEWhhJiRhDWNExJlDqGF18XQQZwySmjJiSElURIIRNWidkmZPA5lQDyUIgpww-DFNKeUu+agKykP4KA3W+sB5YR4JCUe2hZHyMCEo3QPUik6RWZCeORirmNKqT03RSz-mlJMeEpkDM0l+Acf1VkQ14WAwyZCkZ8BC6uOrMPdQSpuweIGcMZZ9N5n9UHBrElQyoWBGySqMJZi0W2MRQSZFFA2QchZUlGlZLVm7Jhviwl6S+V0uxWswI6MqWCNUA-Ii9h8B6DDHcLo1Em6VOLj0gyZAsKuEcL0QO14JIENsPgmBOiWnwMQYiPpM8PhKrGUoC+G9WHHGYbKDhN9ZXysVbpcMqqoDqu4hmJUFcrDIJ7o8yBV4cJwOwTTJYPgFHfIHn6vQ1BwpBvCtxDRWrS66Idf6mFzKLG2M6ki6aKLuVlt5XQItUAcVCoVAS52RLEkNopdNGVIKH7umnLAMpOjE7nGTobcu-jxbDp4Pc8B+E5gfx4G6TCDsoG-LOeKqdEjEoh24g21Qma1XhT6dxNqfAGLUX4rGldJopDaAJJAbwWhorUSuCg0Opzzln0CAeG2yoJLcU0kCxO6IliRwigpTlQ1vaqCsIYew5gzg7j3Puw92am4QGVN4DEFBuLVrA96OD1l1AEnNJ0Z2kJu4EAYNZf12HCTxwdpCCjegTLAckdufkEVMM8DtXQHAA7Q2iMZaYiJDMlDBBtEoRJ-jLkPtNOaKZizSXis2PJs0DN7XpoqvvATesROwvMUoAAetJjJenYBCd6ASJQAASJQRzYQQHkvoAdpHPiPHAcqYIKA6DZDoKkR4qDuS+f86kAkNEGDwgYBoeAipZGqiMHcCA1BU5TDslwAkvm0B0DQG6RBFAUDrEGV+yEvEaMec0iQm1uSeJRauCDZUgymAlTw0YSciUTKaQ8MYE8mGlLzD41EEJVnEolrE7W6szrgjcjGGMAgX5d4ydEZc3ZB6FOaeU7SrF2S1PrY00phtPaZ7DbPuNuFawTNmZO7E+Ao2bP2Y1vvKwTBYBwXwzy6sJoDswy+xty7xAAA8AA+L8DncVJS1NyLUlKfGyvOEluwMG5KQhav5nUHVCItVC3QNm9BT1yMhHYS9LUZ548a48cWbpoCeMFE8UUoKtWkgPSU16kcWpPCwux4MIBuKpjICuzSeBw4YlsEECnoH4Svawu2RVwwoB8apHTw2EqNkTO2ZklxsNiBUijDqMHCylf04lQywC6ulPPyybdmzOu9cG8lGT5XopRsGdLTDabs35uLekzDD7Smjcq-mRD9JfvHfG6DzDEPUGa1h8D4ciH33FNiqxWpmGifNtHbh2nWPzvjJkFN6qUTF33fEBm3NhbS3ffR67Dnk3t3U-oqj1ymvuOnd15CQ3-6TfUX+7b1bjvCf-vJ4BbtwfB2tNKp7fvAPDPztGdM4kl7b2bPl6Wxkmfd288PYc9PtvBemUTau4v17TAV-zbX7X0ba27M77TsDbgYQEQABFJ6LitBqNY1dgAoAJLZ4AkZYx4wkwUx0wsxcwCxiwyxKxhBhB65YY5tuhBwphZhH8bY+kphEwqQlJDgcNdUkdI5icMRuAbIpIEQIA1pIgsJHYm4w5xJJIm5-Y9BAgQgv5+kXAuBH8XxnRog3RPRcRJs7Zm9xNjNV5jMECDcIc-cH9-x4AX9CFO8kp0921BCe9pDn9X8Kox9FNu8a01DZCNCFCptbNV4EDOowcs8pElVIRlIVc0s1AVUngYNMMvoQBhhAQl1KDHhxIHg4MSlYFiced94ogcJ8wrDF4AA-YHKTB3WCKw-MJYYwXkbg10D0L0AQv3ehAgXePbG7EIqws-Agb1XJCYICe8D+XSeAeIlLYUAlQ4F8LUWzFAcImHbPWIio13Q-BfDJYIyo-I9fNve7WGR7feco3wffIvefa7OgHo0IioxJDfK-a3IY2-biOmQaY8Y3OwgNRwyOZwjnDEEZXA4SQ4WSGiCKPSCiMAKiD+SIMrYkFqIIl5NYu4ReObUzGIzoQaKoxI2DF0XgtIjI1fcHbop4iDU-eA+bG+QcUolwME74monEBEFUBolAYzFo-eT4l4ufYQqYno548Ey-LfZYhZTEpgcYwzHExJPEsE+YgYvPa-YYkFQya2K4quF7JQN7JQekBiIOOBKQZ2DEB4kAcES8UGeEZRFwznLdYFB41orjZSL4B4M3LZAQg-C7I-DJJfcEzIi-fjQTPPQw2GYzezLPGeeU6ibQAvTZEkJqdmCYykzUk-Ao3U07TfURQ0kzE04opkxKEcS2YFEUpOJYdQILLCSLMXAgKiJE80JgfufgK9LUoLPQTSfAqwXNY0HwT+fBeyC9DEauYHVQHHVIauHkbkAs-Mos6uDEyiCKC07Qb4lUZI-4-gqvIQy7IEw0meLU50szVsnva0yZG7EbIkwk90ntGE80xU+ExRRE+oxo2zbIWzVIdE7cBHTNKLSOFHGuIs1QPwQsvzOgZoKs7cM9InBgS9CEM+ZdJ2M+TSV012MAUjCyCgCjfieYLUfTc0PDEkDgeSWyKwURGUAct2PcSXSyQie8ycwiHWLQMAbiHEe4E9Ng6rJBNvDo4vdFEzIEyQ6vTbBYg0iHTIzqPwcvG+HCts0c3oD01fL06lRgW5MgPfW7dCyJTCsQ8-H3dFUPVvY3RYgfd3UikiyEzi-6bi-C0RUfAS8-Wi+HTKDY2wh2QEcUvWLCfkmUZdPuUM1MrwzzWAPjdEM2bEpTbsiErIszXUIMCYdQB6KCrQT6FqRJWyzYdgEAfkfAMIZAOePWbAQIBAX9aUQQSoSoJANAEQMQEAUmSQFwK2Fy1EBQJgQQEQL8bAOEMgWSIgJAUgCKiQQQfkEJDdU8dQAAARy0aACxco5DYFoFMHMEsCUBBHUFkisHYLcDysCAKpHBKroDKtSCUFcA6xkD6sonypPBHFGFkBABKhpkEAIA4GDISuEC-GECAA) using my [compose-regexp lib](https://github.com/pygy/compose-regexp.js). The benefits in readbility are hopefully obvious, and would be even better with proper syntactic support.

Edit: alternative syntax: `/\e{js.expression}/` could also work.

---

<div class="post-metadata">

**Author:** ![theScottyJam](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/thescottyjam/32/616_2.png) [@theScottyJam](https://es.discourse.group/u/theScottyJam)\
**Post date:** [April 4, 2022, 12:30pm UTC](https://es.discourse.group/t/regexp-composition/1278/2 "2022-04-04T12:30:45Z")

</div>

There was a fair amount of discussion related to this sort of idea in this thread: [RegExp: Comments](https://es.discourse.group/t/regexp-comments/616)

One of the major hurdles is: How do you compose regular expression flags? Some flags just don't compose at all. Other flags do, but it would be nice if you're able to build the same regular expression without composing as you would build with composing.

In the end, the main differences between composing regular expressions, and composing strings that you pass into a regular expression constructor is this annoying flag situation, and verbosity.

For example, you can do this to achieve composition:

```javascript
const part1 = String.raw`\d{3}`
const part2 = String.raw`\d{4}`

const pattern1 = new RegExp(part1)
const pattern2 = new RegExp(part2)
conts completePattern = new RegExp(`(${part1}) ${part1)-${part2}`)

```

---

<div class="post-metadata">

**Author:** ![pygy](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/pygy/32/231_2.png) [@pygy](https://es.discourse.group/u/pygy)\
**Post date:** [April 4, 2022, 1:00pm UTC](https://es.discourse.group/t/regexp-composition/1278/3 "2022-04-04T13:00:20Z")

</div>

You can throw a `TypeError` for flags that don't make sense.

The string-based approach is fine for trivial patterns, if you want to use quantifiers or disjunctions, you have to either systematically add non-capturing groups (NCG) or parse the input to be spliced in to check if a NCG is needed. ComposeRegExp does the latter to keep the resulting RegExps as short and readable as possible, but it is not trivial. If one doesn't use a dedicated library for this one could easily end up with bugs, especially when refactoring.

```javascript
const part1 = "a"
const part2 = "b"

const combined1 = `${part1}-${part2}`
const combined2 = `${part1}|${part2}`

const badStar1 = new RegExp(`${combined1}*`) // /a-b*/
const badStar2 = new RegExp(`${combined2}*`) // /a|b*/
const goodStar2 = new RegExp(`(?:${combined2})*`) // /(:a|b)*/

const badSequence = RegExp(`${combined1}${combined2}`) // /a-ba|b/
const goodSequence = RegExp(`${combined1}(?:${combined2})`) // /a-b(?:a|b)/

```

Now imagine you set `part1` to `"a|c"`. Have fun debugging if you were not careful adding NCGs everywhere.

You can look at the monstrosity that is the [SemVer parser used by NPM](https://github.com/npm/node-semver/blob/4907647d169948a53156502867ed679268063a9f/internal/re.js#L19), this is what you end up with in practice when using the approach you suggest.

---

<div class="post-metadata">

**Author:** ![pygy](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/pygy/32/231_2.png) [@pygy](https://es.discourse.group/u/pygy)\
**Post date:** [April 4, 2022, 1:25pm UTC](https://es.discourse.group/t/regexp-composition/1278/4 "2022-04-04T13:25:45Z")

</div>

@theScottyJam I've skimmed the "regexp comments" proposal. `new RegExp(RegExp.from`source`, flags)` would be a reasonable solution.

@trusktr has written a lib that implements that idea: [GitHub - trusktr/regexr: Easily compose regular expressions without the need for double-escaping inside strings.](https://github.com/trusktr/regexr).

IMO dedicated syntax or combinators as a JS API would be better though... `new RegExp(RegExp.from` is a lot of visual noise. Syntax highlighters would also need to be made aware of `RegExp.from` whereas `/(?>id)/` would be usable out of the box (and would work with non-unicode RegExps).

---

<div class="post-metadata">

**Author:** ![pygy](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/pygy/32/231_2.png) [@pygy](https://es.discourse.group/u/pygy)\
**Post date:** [April 4, 2022, 4:14pm UTC](https://es.discourse.group/t/regexp-composition/1278/5 "2022-04-04T16:14:28Z")

</div>

While the idea of composing RegExps has long been on my mind, the modifiers and set operations proposal are new to me, and I just realized that composition could not entirely subsume the proposals, because I suppose that we'll want to be able to serialize the result of a composition into something that can be parsed by the `RegExp` constructor.

However, if we provide composition and set operations as JS APIs, the readability of the serialization format become less important, and we could have it live e.g. inside a `\op{}` block, such that escaping rules within charsets don't have to be updated.

---

<div class="post-metadata">

**Author:** ![bakkot](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/bakkot/32/22_2.png) [@bakkot](https://es.discourse.group/u/bakkot)\
**Post date:** [April 5, 2022, 4:32am UTC](https://es.discourse.group/t/regexp-composition/1278/6 "2022-04-05T04:32:52Z")

</div>

This comes up in discussions of the [RegExp.escape](https://github.com/tc39/proposal-regex-escaping) proposal sometimes - some delegates would strongly prefer to have a template tag builder instead of a RegExp.escape method. (This was, in fact, one of the main use cases imaged for template tags when they were being added to the language in the first place.) No one has stepped forward to champion this yet, though.

An example of such a builder (now quite out of date with new features) is

> **[GitHub - mikesamuel/regexp-make-js: ES6 string template tag for creating...](https://github.com/mikesamuel/regexp-make-js)**
>
> ES6 string template tag for creating dynamic regular expressions - GitHub - mikesamuel/regexp-make-js: ES6 string template tag for creating dynamic regular expressions

---

<div class="post-metadata">

**Author:** ![moriken](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/moriken/32/2577_2.png) [@moriken](https://es.discourse.group/u/moriken)\
**Post date:** [July 19, 2022, 9:15am UTC](https://es.discourse.group/t/regexp-composition/1278/7 "2022-07-19T09:15:43Z")

</div>

How about adding a new RegExp Builder Syntax, like Swift, instead of basing it on the existing Template Literal?

```swift
let word = OneOrMore(.word)
let emailPattern = Regex {
    Capture {
        ZeroOrMore {
            word
            "."
        }
        word
    }
    "@"
    Capture {
        word
        OneOrMore {
            "."
            word
        }
    }
}

```

[https://developer.apple.com/documentation/RegexBuilder](https://developer.apple.com/documentation/RegexBuilder)

> <https://github.com/apple/swift-evolution/blob/8711a202af97640eaa0ee040502a365d8b9430c2/proposals/0351-regex-builder.md>

---

<div class="post-metadata">

**Author:** ![pygy](https://yyz2.discourse-cdn.com/free1/user_avatar/es.discourse.group/pygy/32/231_2.png) [@pygy](https://es.discourse.group/u/pygy)\
**Post date:** [November 28, 2022, 1:04pm UTC](https://es.discourse.group/t/regexp-composition/1278/8 "2022-11-28T13:04:33Z")

</div>

FWIW, Swift now provides a RegExp builder that is functionally equivalent to compose-regexp.

 ![a015a24e69e24727](https://global.discourse-cdn.com/free1/uploads/es/original/2X/0/051340602d8bf80511e9cf2871ab6019f988b0a1.jpeg)

Edit: That feature was released six months ago, I learned about it today... Am I living under a rock? Yes, why do you ask :-p
