Skip to content

Commit 0484b31

Browse files
committed
feat: add optionally, oneOrMore and times.any and times.atLeast
1 parent b33b093 commit 0484b31

File tree

3 files changed

+32
-3
lines changed

3 files changed

+32
-3
lines changed

README.md

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
- Runtime is zero-dependency and ultra-minimal
1919
- Ships with transform for compiling runtime to pure RegExp
2020
- Supports automatically typed capture groups
21-
- Packed with useful utilities: `charIn`, `charNotIn`, `anyOf`, `char`, `word`, `digit`, `whitespace`, `letter`, `tab`, `linefeed`, `carriageReturn`, `not`, `maybe`, `exactly`
22-
- All chainable with `and`, `or`, `after`, `before`, `notAfter`, `notBefore`, `times`, `as`, `at`
21+
- Packed with useful utilities: `charIn`, `charNotIn`, `anyOf`, `char`, `word`, `digit`, `whitespace`, `letter`, `tab`, `linefeed`, `carriageReturn`, `not`, `maybe`, `exactly`, `oneOrMore`
22+
- All chainable with `and`, `or`, `after`, `before`, `notAfter`, `notBefore`, `times`, `as`, `at`, `optionally`
2323

2424
**Future ideas**
2525

@@ -75,14 +75,16 @@ They are:
7575
- `char`, `word`, `digit`, `whitespace`, `letter`, `tab`, `linefeed` and `carriageReturn` - these are helpers for specific RegExp characters.
7676
- `not` - this can prefix `word`, `digit`, `whitespace`, `letter`, `tab`, `linefeed` or `carriageReturn`. For example `createRegExp(not.letter)`.
7777
- `maybe` - equivalent to `?` - this marks the input as optional.
78+
- `oneOrMore` - equivalent to `+` - this marks the input as repeatable, any number of times but at least once.
7879
- `exactly` - this escapes a string input to match it exactly.
7980

8081
All of these helpers return an object of type `Input` that can be chained with the following helpers:
8182

8283
- `and` - this adds a new pattern to the current input.
8384
- `or` - this provides an alternative to the current input.
8485
- `after`, `before`, `notAfter` and `notBefore` - these activate positive/negative lookahead/lookbehinds. Make sure to check [browser support](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp#browser_compatibility) as not all browsers support lookbehinds (notably Safari).
85-
- `times` - this is a function you can call directly to repeat the previous pattern an exact number of times, or you can use `times.between(min, max)` to specify a range.
86+
- `times` - this is a function you can call directly to repeat the previous pattern an exact number of times, or you can use `times.between(min, max)` to specify a range, `times.atLeast(num)` to indicate it must repeat x times or `times.any()` to indicate it can repeat any number of times, _including none_.
87+
- `optionally` - this is a function you can call to mark the current input as optional.
8688
- `as` - this defines the entire input so far as a named capture group. You will get type safety when using the resulting RegExp with `String.match()`.
8789
- `at` - this allows you to match beginning/ends of lines with `at.lineStart()` and `at.lineEnd()`.
8890

@@ -157,6 +159,22 @@ export default defineBuildConfig({
157159
})
158160
```
159161

162+
## Examples
163+
164+
```js
165+
import { createRegExp, exactly, oneOrMore, digit } from 'magic-regexp'
166+
167+
// Quick-and-dirty semver
168+
createRegExp(
169+
oneOrMore(digit)
170+
.as('major')
171+
.and('.')
172+
.and(oneOrMore(digit).as('minor'))
173+
.and(exactly('.').and(oneOrMore(char).as('patch')).optionally())
174+
)
175+
// /(?<major>(\d)+)\.(?<minor>(\d)+)(\.(?<patch>(.)+))?/
176+
```
177+
160178
## 💻 Development
161179

162180
- Clone this repository

src/core/inputs.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,10 @@ export interface Input<T extends string = never> {
1818
(number: number): Input<T>
1919
/** specify a range of times to repeat the previous pattern */
2020
between: (min: number, max: number) => Input<T>
21+
/** specify that the expression can repeat any number of times, _including none_ */
22+
any: () => Input<T>
23+
/** specify that the expression must occur at least x times */
24+
atLeast: (min: number) => Input<T>
2125
}
2226
/** this defines the entire input so far as a named capture group. You will get type safety when using the resulting RegExp with `String.match()` */
2327
as: <K extends string>(key: K) => Input<T | K>
@@ -26,6 +30,8 @@ export interface Input<T extends string = never> {
2630
lineStart: () => Input<T>
2731
lineEnd: () => Input<T>
2832
}
33+
/** this allows you to mark the input so far as optional */
34+
optionally: () => Input<T>
2935
toString: () => string
3036
}
3137

@@ -61,3 +67,5 @@ export const maybe = (str: string | Input) => createInput(`(${exactly(str)})?`)
6167
/** This escapes a string input to match it exactly */
6268
export const exactly = (str: string | Input) =>
6369
typeof str === 'string' ? createInput(str.replace(/[.*+?^${}()|[\]\\/]/g, '\\$&')) : str
70+
export const oneOrMore = (str: string | Input) => createInput(`(${exactly(str)})+`)
71+
// export const = (str: string | Input) => createInput(`(${exactly(str)})+`)

src/core/internal.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,11 @@ export const createInput = <T extends string = never>(s: string | Input<T>): Inp
1010
notAfter: input => createInput(`(?<!${exactly(input)})${s}`),
1111
notBefore: input => createInput(`${s}(?!${exactly(input)})`),
1212
times: Object.assign((number: number) => createInput(`(${s}){${number}}`), {
13+
any: () => createInput(`(${s})*`),
14+
atLeast: (min: number) => createInput(`(${s}){${min},}`),
1315
between: (min: number, max: number) => createInput(`(${s}){${min},${max}}`),
1416
}),
17+
optionally: () => createInput(`(${s})?`),
1518
as: key => createInput(`(?<${key}>${s})`),
1619
at: {
1720
lineStart: () => createInput(`^${s}`),

0 commit comments

Comments
 (0)