Skip to content

Commit e5e7efd

Browse files
feat: base (non-named) capture (#24)
1 parent a5fca65 commit e5e7efd

File tree

5 files changed

+66
-1
lines changed

5 files changed

+66
-1
lines changed

src/__tests__/capture.test.tsx

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import { buildPattern } from '..';
2+
import { capture } from '../capture';
3+
import { oneOrMore } from '../quantifiers/base';
4+
import { execRegex } from '../test-utils';
5+
6+
test('"capture" base cases', () => {
7+
expect(buildPattern(capture('a'))).toBe('(a)');
8+
expect(buildPattern(capture('abc'))).toBe('(abc)');
9+
expect(buildPattern(capture(oneOrMore('abc')))).toBe('((?:abc)+)');
10+
expect(buildPattern(oneOrMore(capture('abc')))).toBe('(abc)+');
11+
});
12+
13+
test('"capture" captures group', () => {
14+
expect(execRegex('ab', [capture('b')])).toEqual(['b', 'b']);
15+
expect(execRegex('ab', ['a', capture('b')])).toEqual(['ab', 'b']);
16+
expect(execRegex('abc', ['a', capture('b'), capture('c')])).toEqual([
17+
'abc',
18+
'b',
19+
'c',
20+
]);
21+
});

src/capture.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import type { Capture, RegexElement } from './types';
2+
import { EncoderPriority, type EncoderNode } from './types-internal';
3+
4+
export function capture(...children: RegexElement[]): Capture {
5+
return {
6+
type: 'capture',
7+
children,
8+
};
9+
}
10+
11+
export function encodeCapture(node: EncoderNode): EncoderNode {
12+
return {
13+
pattern: `(${node.pattern})`,
14+
priority: EncoderPriority.Atom,
15+
};
16+
}

src/encoder.ts

+5
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
} from './quantifiers/base';
1111
import { encodeRepeat } from './quantifiers/repeat';
1212
import { concatNodes, escapeText } from './utils';
13+
import { encodeCapture } from './capture';
1314

1415
export function encodeSequence(elements: RegexElement[]): EncoderNode {
1516
return concatNodes(elements.map((c) => encodeElement(c)));
@@ -48,6 +49,10 @@ export function encodeElement(element: RegexElement): EncoderNode {
4849
return encodeZeroOrMore(encodeSequence(element.children));
4950
}
5051

52+
if (element.type === 'capture') {
53+
return encodeCapture(encodeSequence(element.children));
54+
}
55+
5156
// @ts-expect-error User passed incorrect type
5257
throw new Error(`Unknown elements type ${element.type}`);
5358
}

src/test-utils.ts

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { buildRegex } from '.';
2+
import type { RegexElement } from './types';
3+
4+
export function execRegex(text: string, elements: RegexElement[]) {
5+
const regex = buildRegex(...elements);
6+
return [...regex.exec(text)!];
7+
}
8+
9+
export function execRegexFull(text: string, elements: RegexElement[]) {
10+
const regex = buildRegex(...elements);
11+
return regex.exec(text)!;
12+
}

src/types.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
1-
export type RegexElement = string | ChoiceOf | CharacterClass | Quantifier;
1+
export type RegexElement =
2+
| string
3+
| CharacterClass
4+
| ChoiceOf
5+
| Quantifier
6+
| Capture;
27

38
export type Quantifier = One | OneOrMore | Optionally | ZeroOrMore | Repeat;
49

@@ -41,3 +46,9 @@ export type Repeat = {
4146
};
4247

4348
export type RepeatConfig = { count: number } | { min: number; max?: number };
49+
50+
// Captures
51+
export type Capture = {
52+
type: 'capture';
53+
children: RegexElement[];
54+
};

0 commit comments

Comments
 (0)