Skip to content

Commit 91adb02

Browse files
feat: regex construct (#77)
1 parent 4469146 commit 91adb02

File tree

5 files changed

+87
-24
lines changed

5 files changed

+87
-24
lines changed

src/__tests__/example-js-number.ts

Lines changed: 23 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,16 @@ import {
66
endOfString,
77
oneOrMore,
88
optional,
9+
regex,
910
startOfString,
1011
zeroOrMore,
1112
} from '..';
1213

1314
test('example: validate JavaScript number', () => {
1415
const sign = anyOf('+-');
15-
const exponent = [anyOf('eE'), optional(sign), oneOrMore(digit)];
16+
const exponent = regex([anyOf('eE'), optional(sign), oneOrMore(digit)]);
1617

17-
const regex = buildRegExp([
18+
const numberValidator = buildRegExp([
1819
startOfString,
1920
optional(sign),
2021
choiceOf(
@@ -25,26 +26,26 @@ test('example: validate JavaScript number', () => {
2526
endOfString,
2627
]);
2728

28-
expect(regex).toMatchString('0');
29-
expect(regex).toMatchString('-1');
30-
expect(regex).toMatchString('+1');
31-
expect(regex).toMatchString('1.0');
32-
expect(regex).toMatchString('1.1234');
33-
expect(regex).toMatchString('1.');
34-
expect(regex).toMatchString('.1');
35-
expect(regex).toMatchString('-.1234');
36-
expect(regex).toMatchString('+.5');
37-
expect(regex).toMatchString('1e21');
38-
expect(regex).toMatchString('1e-21');
39-
expect(regex).toMatchString('+1e+42');
40-
expect(regex).toMatchString('-1e-42');
29+
expect(numberValidator).toMatchString('0');
30+
expect(numberValidator).toMatchString('-1');
31+
expect(numberValidator).toMatchString('+1');
32+
expect(numberValidator).toMatchString('1.0');
33+
expect(numberValidator).toMatchString('1.1234');
34+
expect(numberValidator).toMatchString('1.');
35+
expect(numberValidator).toMatchString('.1');
36+
expect(numberValidator).toMatchString('-.1234');
37+
expect(numberValidator).toMatchString('+.5');
38+
expect(numberValidator).toMatchString('1e21');
39+
expect(numberValidator).toMatchString('1e-21');
40+
expect(numberValidator).toMatchString('+1e+42');
41+
expect(numberValidator).toMatchString('-1e-42');
4142

42-
expect(regex).not.toMatchString('');
43-
expect(regex).not.toMatchString('a');
44-
expect(regex).not.toMatchString('1a');
45-
expect(regex).not.toMatchString('1.0.');
46-
expect(regex).not.toMatchString('.1.1');
47-
expect(regex).not.toMatchString('.');
43+
expect(numberValidator).not.toMatchString('');
44+
expect(numberValidator).not.toMatchString('a');
45+
expect(numberValidator).not.toMatchString('1a');
46+
expect(numberValidator).not.toMatchString('1.0.');
47+
expect(numberValidator).not.toMatchString('.1.1');
48+
expect(numberValidator).not.toMatchString('.');
4849

49-
expect(regex).toEqualRegex(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/);
50+
expect(numberValidator).toEqualRegex(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/);
5051
});
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { regex } from '../..';
2+
3+
test('`regex` no-op pattern', () => {
4+
expect(regex('a')).toEqualRegex(/a/);
5+
expect(regex(['a', 'b'])).toEqualRegex(/ab/);
6+
expect([regex('a'), regex(['b', 'c'])]).toEqualRegex(/abc/);
7+
});

src/constructs/regex.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { encodeSequence } from '../encoder/encoder';
2+
import type { EncodeResult } from '../encoder/types';
3+
import { ensureArray } from '../utils/elements';
4+
import type { RegexConstruct, RegexElement, RegexSequence } from '../types';
5+
6+
export interface Regex extends RegexConstruct {
7+
type: 'sequence';
8+
children: RegexElement[];
9+
}
10+
11+
export function regex(sequence: RegexSequence): Regex {
12+
return {
13+
type: 'sequence',
14+
children: ensureArray(sequence),
15+
encode: encodeRegex,
16+
};
17+
}
18+
19+
function encodeRegex(this: Regex): EncodeResult {
20+
return encodeSequence(this.children);
21+
}

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,4 +33,5 @@ export { lookbehind } from './constructs/lookbehind';
3333
export { negativeLookahead } from './constructs/negative-lookahead';
3434
export { negativeLookbehind } from './constructs/negative-lookbehind';
3535
export { oneOrMore, optional, zeroOrMore } from './constructs/quantifiers';
36+
export { regex } from './constructs/regex';
3637
export { repeat } from './constructs/repeat';

website/docs/api/constructs.md

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ These functions and objects represent available regex constructs.
99

1010
```ts
1111
function choiceOf(
12-
...alternatives: RegexSequence[]
12+
...alternatives: RegexSequence[],
1313
): ChoiceOf {
1414
```
1515
@@ -22,7 +22,9 @@ Example: `choiceOf("color", "colour")` matches either `color` or `colour` patter
2222
### `capture()`
2323
2424
```ts
25-
function capture(sequence: RegexSequence): Capture;
25+
function capture(
26+
sequence: RegexSequence,
27+
): Capture;
2628
```
2729
2830
Regex syntax: `(...)`.
@@ -35,3 +37,34 @@ TS Regex Builder does not have a construct for non-capturing groups. Such groups
3537
3638
:::
3739
40+
### `regex()`
41+
42+
```ts
43+
function regex(
44+
sequence: RegexSequence,
45+
): Regex;
46+
```
47+
48+
Regex syntax: the pattern remains unchanged when wrapped by this construct.
49+
50+
This construct is a no-op operator that groups array of `RegexElements` into a single element for composition purposes. This is particularly useful for defining smaller sequence patterns as separate variables.
51+
52+
Without `regex()`:
53+
54+
```ts
55+
const exponent = [anyOf('eE'), optional(anyOf('+-')), oneOrMore(digit)];
56+
const numberWithExponent = buildRegExp([
57+
oneOrMore(digit),
58+
...exponent, // Need to spread "exponent" as it's an array.
59+
]);
60+
```
61+
62+
With `regex()`:
63+
64+
```ts
65+
const exponent = regex([anyOf('eE'), optional(anyOf('+-')), oneOrMore(digit)]);
66+
const numberWithExponent = buildRegExp([
67+
oneOrMore(digit),
68+
exponent, // Easily compose "exponent" sequence as a single element.
69+
]);
70+
```

0 commit comments

Comments
 (0)