Skip to content

refactor: simplify exact repeat quantifier #46

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ const hexColor = buildRegExp(
optionally('#'),
capture(
choiceOf(
repeat(hexDigit, { count: 6 }), // #rrggbb
repeat(hexDigit, { count: 3 }), // #rgb
repeat(hexDigit, 6), // #rrggbb
repeat(hexDigit, 3), // #rgb
),
),
endOfString,
Expand Down Expand Up @@ -79,11 +79,11 @@ const currencyAmount = buildRegExp([
choiceOf(
'$',
'€',
repeat({ count: 3 }, charRange('A', 'Z')), // ISO currency code
repeat(charRange('A', 'Z'), 3), // ISO currency code
),
capture(
oneOrMore(digit), // Integer part
optionally(['.', repeat({ count: 2 }, digit)]), // Fractional part
optionally(['.', repeat(digit, 2)]), // Fractional part
),
]);
```
Expand Down Expand Up @@ -114,7 +114,7 @@ Notes:
| `zeroOrMore(x)` | `x*` | Zero or more occurence of a pattern |
| `oneOrMore(x)` | `x+` | One or more occurence of a pattern |
| `optionally(x)` | `x?` | Zero or one occurence of a pattern |
| `repeat(x, { count: n })` | `x{n}` | Pattern repeats exact number of times |
| `repeat(x, n)` | `x{n}` | Pattern repeats exact number of times |
| `repeat(x, { min: n, })` | `x{n,}` | Pattern repeats at least given number of times |
| `repeat(x, { min: n, max: n2 })` | `x{n1,n2}` | Pattern repeats between n1 and n2 number of times |

Expand Down
2 changes: 1 addition & 1 deletion docs/API.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ function optionally(

```ts
function repeat(
options: { count: number } | { min: number; max?: number },
options: number | { min: number; max?: number },
sequence: RegexSequence,
): Repeat
```
Expand Down
4 changes: 2 additions & 2 deletions docs/Examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
const octet = choiceOf(
[digit],
[charRange('1', '9'), digit],
['1', repeat({ count: 2 }, digit)],
['1', repeat(digit, 2)],
['2', charRange('0', '4'), digit],
['25', charRange('0', '5')]
);

// Match
const regex = buildRegExp([
startOfString, //
repeat([octet, '.'], { count: 3 }),
repeat([octet, '.'], 3),
octet,
endOfString,
]);
Expand Down
4 changes: 2 additions & 2 deletions src/__tests__/examples.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,14 @@ test('example: IPv4 address validator', () => {
const octet = choiceOf(
[digit],
[charRange('1', '9'), digit],
['1', repeat(digit, { count: 2 })],
['1', repeat(digit, 2)],
['2', charRange('0', '4'), digit],
['25', charRange('0', '5')],
);

const regex = buildRegExp([
startOfString, //
repeat([octet, '.'], { count: 3 }),
repeat([octet, '.'], 3),
octet,
endOfString,
]);
Expand Down
2 changes: 1 addition & 1 deletion src/constructs/__tests__/choice-of.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ test('`choiceOf` with sequence options', () => {

test('`choiceOf` using nested regex', () => {
expect(choiceOf(oneOrMore('a'), zeroOrMore('b'))).toHavePattern(/a+|b*/);
expect(choiceOf(repeat('a', { min: 1, max: 3 }), repeat('bx', { count: 5 }))).toHavePattern(
expect(choiceOf(repeat('a', { min: 1, max: 3 }), repeat('bx', 5))).toHavePattern(
/a{1,3}|(?:bx){5}/,
);
});
Expand Down
10 changes: 5 additions & 5 deletions src/constructs/__tests__/repeat.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ import { repeat } from '../repeat';
test('`repeat` quantifier', () => {
expect(['a', repeat('b', { min: 1, max: 5 })]).toHavePattern(/ab{1,5}/);
expect(['a', repeat('b', { min: 1 })]).toHavePattern(/ab{1,}/);
expect(['a', repeat('b', { count: 1 })]).toHavePattern(/ab{1}/);
expect(['a', repeat('b', 1)]).toHavePattern(/ab{1}/);

expect(['a', repeat(['a', zeroOrMore('b')], { count: 1 })]).toHavePattern(/a(?:ab*){1}/);
expect(repeat(['text', ' ', oneOrMore('d')], { count: 5 })).toHavePattern(/(?:text d+){5}/);
expect(['a', repeat(['a', zeroOrMore('b')], 1)]).toHavePattern(/a(?:ab*){1}/);
expect(repeat(['text', ' ', oneOrMore('d')], 5)).toHavePattern(/(?:text d+){5}/);
});

test('`repeat` optimizes grouping for atoms', () => {
expect(repeat(digit, { count: 2 })).toHavePattern(/\d{2}/);
expect(repeat(digit, 2)).toHavePattern(/\d{2}/);
expect(repeat(digit, { min: 2 })).toHavePattern(/\d{2,}/);
expect(repeat(digit, { min: 1, max: 5 })).toHavePattern(/\d{1,5}/);
});

test('`repeat` throws on no children', () => {
expect(() => repeat([], { count: 1 })).toThrowErrorMatchingInlineSnapshot(
expect(() => repeat([], 1)).toThrowErrorMatchingInlineSnapshot(
`"\`repeat\` should receive at least one element"`,
);
});
6 changes: 3 additions & 3 deletions src/constructs/repeat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export interface Repeat extends RegexConstruct {
children: RegexElement[];
}

export type RepeatOptions = { count: number } | { min: number; max?: number };
export type RepeatOptions = number | { min: number; max?: number };

export function repeat(sequence: RegexSequence, options: RepeatOptions): Repeat {
const children = ensureArray(sequence);
Expand All @@ -29,10 +29,10 @@ export function repeat(sequence: RegexSequence, options: RepeatOptions): Repeat
function encodeRepeat(this: Repeat): EncodeResult {
const atomicNodes = encodeAtom(this.children);

if ('count' in this.options) {
if (typeof this.options === 'number') {
return {
precedence: 'sequence',
pattern: `${atomicNodes.pattern}{${this.options.count}}`,
pattern: `${atomicNodes.pattern}{${this.options}}`,
};
}

Expand Down