Skip to content

Commit

Permalink
feat: repeat quantifier (#11)
Browse files Browse the repository at this point in the history
  • Loading branch information
okwasniewski authored Dec 6, 2023
1 parent 9503643 commit eb111c2
Show file tree
Hide file tree
Showing 6 changed files with 79 additions and 9 deletions.
4 changes: 3 additions & 1 deletion src/__tests__/compiler.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { buildPattern, buildRegex } from '../compiler';
import { oneOrMore, optionally, one, zeroOrMore } from '../quantifiers';
import { oneOrMore, optionally, one, zeroOrMore, repeat } from '../quantifiers';

test('basic quantifies', () => {
expect(buildPattern('a')).toEqual('a');
Expand All @@ -12,6 +12,8 @@ test('basic quantifies', () => {
expect(buildPattern('a', oneOrMore('bc'))).toEqual('a(?:bc)+');
expect(buildPattern('a', oneOrMore('bc'))).toEqual('a(?:bc)+');

expect(buildPattern('a', repeat({ min: 1, max: 5 }, 'b'))).toEqual('ab{1,5}');

expect(buildPattern('a', zeroOrMore('b'))).toEqual('ab*');
expect(buildPattern('a', zeroOrMore('bc'))).toEqual('a(?:bc)*');
expect(buildPattern('a', zeroOrMore('bc'))).toEqual('a(?:bc)*');
Expand Down
15 changes: 15 additions & 0 deletions src/__tests__/repeat.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { buildPattern } from '../compiler';
import { repeat, zeroOrMore, oneOrMore } from '../quantifiers';

test('"repeat" quantifier', () => {
expect(buildPattern('a', repeat({ min: 1, max: 5 }, 'b'))).toEqual('ab{1,5}');
expect(buildPattern('a', repeat({ min: 1 }, 'b'))).toEqual('ab{1,}');
expect(buildPattern('a', repeat({ count: 1 }, 'b'))).toEqual('ab{1}');

expect(buildPattern('a', repeat({ count: 1 }, 'a', zeroOrMore('b')))).toEqual(
'a(?:ab*){1}'
);
expect(
buildPattern(repeat({ count: 5 }, 'text', ' ', oneOrMore('d')))
).toEqual('(?:text d+){5}');
});
10 changes: 8 additions & 2 deletions src/compiler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { RegexElement } from './types';
import { compilers as quantifiers } from './quantifiers';
import { compileRepeat } from './quantifiers/repeat';

/**
* Generate RegExp object for elements.
Expand Down Expand Up @@ -32,11 +33,16 @@ function compileSingle(elements: RegexElement): string {
return elements;
}

const compiledChildren = compileList(elements.children);

if (elements.type === 'repeat') {
return compileRepeat(elements.config, compiledChildren);
}

const elementCompiler = quantifiers[elements.type];
if (!elementCompiler) {
throw new Error(`Unknown elements type ${elements.type}`);
}

const children = compileList(elements.children);
return elementCompiler(children);
return elementCompiler(compiledChildren);
}
23 changes: 18 additions & 5 deletions src/quantifiers.ts → src/quantifiers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ import type {
OneOrMore,
Optionally,
RegexElement,
Repeat,
RepeatConfig,
ZeroOrMore,
} from './types';
import type { CompilerMap } from './types-internal';
import { wrapGroup } from './utils';
} from '../types';
import type { CompilerMap } from '../types-internal';
import { wrapGroup } from '../utils';

export function oneOrMore(...children: RegexElement[]): OneOrMore {
return {
Expand Down Expand Up @@ -36,9 +38,20 @@ export function zeroOrMore(...children: RegexElement[]): ZeroOrMore {
};
}

export function repeat(
config: RepeatConfig,
...children: RegexElement[]
): Repeat {
return {
type: 'repeat',
children,
config,
};
}

export const compilers = {
one: (compiledChildren) => compiledChildren,
oneOrMore: (compiledChildren) => `${wrapGroup(compiledChildren)}+`,
optionally: (compiledChildren: string) => `${wrapGroup(compiledChildren)}?`,
zeroOrMore: (compiledChildren: string) => `${wrapGroup(compiledChildren)}*`,
optionally: (compiledChildren) => `${wrapGroup(compiledChildren)}?`,
zeroOrMore: (compiledChildren) => `${wrapGroup(compiledChildren)}*`,
} satisfies CompilerMap;
17 changes: 17 additions & 0 deletions src/quantifiers/repeat.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import type { RepeatConfig } from '../types';
import { wrapGroup } from '../utils';

export function compileRepeat(
config: RepeatConfig,
compiledChildren: string
): string {
if ('count' in config && typeof config.count === 'number') {
return `${wrapGroup(compiledChildren)}{${config.count}}`;
}

if ('min' in config && typeof config.min === 'number') {
return `${wrapGroup(compiledChildren)}{${config.min},${config?.max ?? ''}}`;
}

return `${wrapGroup(compiledChildren)}`;
}
19 changes: 18 additions & 1 deletion src/types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
export type RegexElement = string | RegexQuantifier;

export type RegexQuantifier = One | OneOrMore | Optionally | ZeroOrMore;
export type RegexQuantifier =
| One
| OneOrMore
| Optionally
| ZeroOrMore
| Repeat;

// Quantifiers
export type One = {
Expand All @@ -18,6 +23,18 @@ export type Optionally = {
children: RegexElement[];
};

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

export type Repeat = {
type: 'repeat';
children: RegexElement[];
config: RepeatConfig;
};

export type ZeroOrMore = {
type: 'zeroOrMore';
children: RegexElement[];
Expand Down

0 comments on commit eb111c2

Please sign in to comment.