From 5bfe40d0e62de0eaccc6febcecffa5c4b40e4a2c Mon Sep 17 00:00:00 2001 From: Jan Jaworski Date: Wed, 6 Dec 2023 14:12:03 +0100 Subject: [PATCH 1/8] feat: add whitespace character class --- .gitignore | 2 ++ src/__tests__/characterClasses.test.tsx | 9 +++++++++ src/characterClasses.ts | 5 +++++ src/index.tsx | 1 + src/types.ts | 7 ++++++- 5 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 src/__tests__/characterClasses.test.tsx create mode 100644 src/characterClasses.ts diff --git a/.gitignore b/.gitignore index 24b30e1..c75e6b3 100644 --- a/.gitignore +++ b/.gitignore @@ -30,3 +30,5 @@ buck-out/ # generated by bob lib/ + +/.idea diff --git a/src/__tests__/characterClasses.test.tsx b/src/__tests__/characterClasses.test.tsx new file mode 100644 index 0000000..acaf19c --- /dev/null +++ b/src/__tests__/characterClasses.test.tsx @@ -0,0 +1,9 @@ +import { whitespace } from '../characterClasses'; +import { buildPattern } from '../compiler'; +import { one } from '../quantifiers'; + +test('"whitespace" character class', () => { + expect(buildPattern(whitespace())).toEqual(`\\s`); + expect(buildPattern(one('ab'), whitespace())).toEqual(`ab\\s`); + expect(buildPattern(one('ab'), whitespace(), one('c'))).toEqual(`ab\\sc`); +}); diff --git a/src/characterClasses.ts b/src/characterClasses.ts new file mode 100644 index 0000000..df2c3e8 --- /dev/null +++ b/src/characterClasses.ts @@ -0,0 +1,5 @@ +import type { Whitespace } from './types'; + +export function whitespace(): Whitespace { + return `\\s`; +} diff --git a/src/index.tsx b/src/index.tsx index 3335d4c..207dbb6 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,5 @@ export type * from './types'; +export { whitespace } from './characterClasses'; export { buildRegex, buildPattern } from './compiler'; export { oneOrMore, optionally } from './quantifiers'; diff --git a/src/types.ts b/src/types.ts index b8ea674..5e7d288 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,4 +1,6 @@ -export type RegexElement = string | RegexQuantifier; +export type RegexElement = string | RegexCharacterClass | RegexQuantifier; + +export type RegexCharacterClass = Whitespace; export type RegexQuantifier = | One @@ -7,6 +9,9 @@ export type RegexQuantifier = | ZeroOrMore | Repeat; +// Character classes +export type Whitespace = `\\s`; + // Quantifiers export type One = { type: 'one'; From c5b0d29bb96a8f0669d6e6bd44fa69a8e97afb48 Mon Sep 17 00:00:00 2001 From: Jan Jaworski Date: Wed, 6 Dec 2023 18:27:49 +0100 Subject: [PATCH 2/8] chore: update based on the PR suggestions --- src/__tests__/characterClasses.test.tsx | 20 +++++++++++++++++++- src/characterClasses.ts | 23 +++++++++++++++++++++-- src/compiler.ts | 21 +++++++++++++-------- src/types.ts | 10 ++++++++-- 4 files changed, 61 insertions(+), 13 deletions(-) diff --git a/src/__tests__/characterClasses.test.tsx b/src/__tests__/characterClasses.test.tsx index acaf19c..33f82df 100644 --- a/src/__tests__/characterClasses.test.tsx +++ b/src/__tests__/characterClasses.test.tsx @@ -1,4 +1,4 @@ -import { whitespace } from '../characterClasses'; +import { any, digit, whitespace, word } from '../characterClasses'; import { buildPattern } from '../compiler'; import { one } from '../quantifiers'; @@ -7,3 +7,21 @@ test('"whitespace" character class', () => { expect(buildPattern(one('ab'), whitespace())).toEqual(`ab\\s`); expect(buildPattern(one('ab'), whitespace(), one('c'))).toEqual(`ab\\sc`); }); + +test('"digit" character class', () => { + expect(buildPattern(digit())).toEqual(`\\d`); + expect(buildPattern(one('ab'), digit())).toEqual(`ab\\d`); + expect(buildPattern(one('ab'), digit(), one('c'))).toEqual(`ab\\dc`); +}); + +test('"word" character class', () => { + expect(buildPattern(word())).toEqual(`\\w`); + expect(buildPattern(one('ab'), word())).toEqual(`ab\\w`); + expect(buildPattern(one('ab'), word(), one('c'))).toEqual(`ab\\wc`); +}); + +test('"any" character class', () => { + expect(buildPattern(any())).toEqual(`.`); + expect(buildPattern(one('ab'), any())).toEqual(`ab.`); + expect(buildPattern(one('ab'), any(), one('c'))).toEqual(`ab.c`); +}); diff --git a/src/characterClasses.ts b/src/characterClasses.ts index df2c3e8..12e5780 100644 --- a/src/characterClasses.ts +++ b/src/characterClasses.ts @@ -1,5 +1,24 @@ -import type { Whitespace } from './types'; +import type { Any, Digit, Whitespace, Word } from './types'; export function whitespace(): Whitespace { - return `\\s`; + return { type: 'whitespace' }; } + +export function digit(): Digit { + return { type: 'digit' }; +} + +export function word(): Word { + return { type: 'word' }; +} + +export function any(): Any { + return { type: 'any' }; +} + +export const compilers = { + whitespace: '\\s', + digit: '\\d', + word: '\\w', + any: '.', +}; diff --git a/src/compiler.ts b/src/compiler.ts index 45fe05b..265d549 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1,6 +1,7 @@ import type { RegexElement } from './types'; import { compilers as quantifiers } from './quantifiers'; import { compileRepeat } from './quantifiers/repeat'; +import { compilers as characterClasses } from './characterClasses'; /** * Generate RegExp object for elements. @@ -33,16 +34,20 @@ function compileSingle(elements: RegexElement): string { return elements; } - const compiledChildren = compileList(elements.children); + if ('children' in elements) { + const compiledChildren = compileList(elements.children); - if (elements.type === 'repeat') { - return compileRepeat(elements.config, compiledChildren); - } + 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 elementCompiler = quantifiers[elements.type]; - if (!elementCompiler) { - throw new Error(`Unknown elements type ${elements.type}`); + return elementCompiler(compiledChildren); } - return elementCompiler(compiledChildren); + return characterClasses[elements.type]; } diff --git a/src/types.ts b/src/types.ts index 5e7d288..3b8ef4d 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,6 +1,6 @@ export type RegexElement = string | RegexCharacterClass | RegexQuantifier; -export type RegexCharacterClass = Whitespace; +export type RegexCharacterClass = Whitespace | Digit | Word | Any; export type RegexQuantifier = | One @@ -10,7 +10,13 @@ export type RegexQuantifier = | Repeat; // Character classes -export type Whitespace = `\\s`; +export type Whitespace = { type: 'whitespace' }; + +export type Digit = { type: 'digit' }; + +export type Word = { type: 'word' }; + +export type Any = { type: 'any' }; // Quantifiers export type One = { From f26f35edaa1dd128c516dbc780e4d5025728d693 Mon Sep 17 00:00:00 2001 From: Jan Jaworski Date: Wed, 6 Dec 2023 18:44:07 +0100 Subject: [PATCH 3/8] chore: remove not needed check --- src/quantifiers/repeat.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/quantifiers/repeat.ts b/src/quantifiers/repeat.ts index 4aa379a..35a19db 100644 --- a/src/quantifiers/repeat.ts +++ b/src/quantifiers/repeat.ts @@ -5,11 +5,11 @@ export function compileRepeat( config: RepeatConfig, compiledChildren: string ): string { - if ('count' in config && typeof config.count === 'number') { + if ('count' in config) { return `${wrapGroup(compiledChildren)}{${config.count}}`; } - if ('min' in config && typeof config.min === 'number') { + if ('min' in config) { return `${wrapGroup(compiledChildren)}{${config.min},${config?.max ?? ''}}`; } From 1386a50dd7ed647780691bf358d73fd290989e13 Mon Sep 17 00:00:00 2001 From: Jan Jaworski Date: Wed, 6 Dec 2023 19:26:36 +0100 Subject: [PATCH 4/8] chore: apply suggestions --- src/__tests__/characterClasses.test.tsx | 8 ++++++++ src/characterClasses.ts | 3 ++- src/quantifiers/repeat.ts | 6 +----- src/types-internal.ts | 4 +++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/__tests__/characterClasses.test.tsx b/src/__tests__/characterClasses.test.tsx index 33f82df..487f0ad 100644 --- a/src/__tests__/characterClasses.test.tsx +++ b/src/__tests__/characterClasses.test.tsx @@ -4,24 +4,32 @@ import { one } from '../quantifiers'; test('"whitespace" character class', () => { expect(buildPattern(whitespace())).toEqual(`\\s`); + expect(buildPattern(one('ab'), whitespace())).toEqual(`ab\\s`); + expect(buildPattern(one('ab'), whitespace(), one('c'))).toEqual(`ab\\sc`); }); test('"digit" character class', () => { expect(buildPattern(digit())).toEqual(`\\d`); + expect(buildPattern(one('ab'), digit())).toEqual(`ab\\d`); + expect(buildPattern(one('ab'), digit(), one('c'))).toEqual(`ab\\dc`); }); test('"word" character class', () => { expect(buildPattern(word())).toEqual(`\\w`); + expect(buildPattern(one('ab'), word())).toEqual(`ab\\w`); + expect(buildPattern(one('ab'), word(), one('c'))).toEqual(`ab\\wc`); }); test('"any" character class', () => { expect(buildPattern(any())).toEqual(`.`); + expect(buildPattern(one('ab'), any())).toEqual(`ab.`); + expect(buildPattern(one('ab'), any(), one('c'))).toEqual(`ab.c`); }); diff --git a/src/characterClasses.ts b/src/characterClasses.ts index 12e5780..9858571 100644 --- a/src/characterClasses.ts +++ b/src/characterClasses.ts @@ -1,4 +1,5 @@ import type { Any, Digit, Whitespace, Word } from './types'; +import type { CompilerMap } from './types-internal'; export function whitespace(): Whitespace { return { type: 'whitespace' }; @@ -21,4 +22,4 @@ export const compilers = { digit: '\\d', word: '\\w', any: '.', -}; +} satisfies CompilerMap; diff --git a/src/quantifiers/repeat.ts b/src/quantifiers/repeat.ts index 35a19db..3905274 100644 --- a/src/quantifiers/repeat.ts +++ b/src/quantifiers/repeat.ts @@ -9,9 +9,5 @@ export function compileRepeat( return `${wrapGroup(compiledChildren)}{${config.count}}`; } - if ('min' in config) { - return `${wrapGroup(compiledChildren)}{${config.min},${config?.max ?? ''}}`; - } - - return `${wrapGroup(compiledChildren)}`; + return `${wrapGroup(compiledChildren)}{${config.min},${config?.max ?? ''}}`; } diff --git a/src/types-internal.ts b/src/types-internal.ts index 9d41c14..197cd20 100644 --- a/src/types-internal.ts +++ b/src/types-internal.ts @@ -1,3 +1,5 @@ // Compilation export type ElementCompiler = (compiledChildren: string) => string; -export type CompilerMap = Record; +export type CompilerMap = + | Record + | Record; From 54f969339484f1571ebb8abb6d2e34ef03902c33 Mon Sep 17 00:00:00 2001 From: Jan Jaworski Date: Wed, 6 Dec 2023 20:29:09 +0100 Subject: [PATCH 5/8] chore: throw runtime error when character is not available --- src/compiler.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/compiler.ts b/src/compiler.ts index 265d549..4069c88 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -49,5 +49,11 @@ function compileSingle(elements: RegexElement): string { return elementCompiler(compiledChildren); } - return characterClasses[elements.type]; + const characterCompiler = characterClasses[elements.type]; + + if (!characterCompiler) { + throw new Error(`Unknown character type ${elements.type}`); + } + + return characterCompiler; } From 92676be15f023b0a316a376219e587e8cf95918c Mon Sep 17 00:00:00 2001 From: Jan Jaworski Date: Wed, 6 Dec 2023 20:32:28 +0100 Subject: [PATCH 6/8] chore: change functions to constants --- src/__tests__/characterClasses.test.tsx | 24 ++++++++++++------------ src/characterClasses.ts | 16 ++++------------ 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/__tests__/characterClasses.test.tsx b/src/__tests__/characterClasses.test.tsx index 487f0ad..1f2c7b4 100644 --- a/src/__tests__/characterClasses.test.tsx +++ b/src/__tests__/characterClasses.test.tsx @@ -3,33 +3,33 @@ import { buildPattern } from '../compiler'; import { one } from '../quantifiers'; test('"whitespace" character class', () => { - expect(buildPattern(whitespace())).toEqual(`\\s`); + expect(buildPattern(whitespace)).toEqual(`\\s`); - expect(buildPattern(one('ab'), whitespace())).toEqual(`ab\\s`); + expect(buildPattern(one('ab'), whitespace)).toEqual(`ab\\s`); - expect(buildPattern(one('ab'), whitespace(), one('c'))).toEqual(`ab\\sc`); + expect(buildPattern(one('ab'), whitespace, one('c'))).toEqual(`ab\\sc`); }); test('"digit" character class', () => { - expect(buildPattern(digit())).toEqual(`\\d`); + expect(buildPattern(digit)).toEqual(`\\d`); - expect(buildPattern(one('ab'), digit())).toEqual(`ab\\d`); + expect(buildPattern(one('ab'), digit)).toEqual(`ab\\d`); - expect(buildPattern(one('ab'), digit(), one('c'))).toEqual(`ab\\dc`); + expect(buildPattern(one('ab'), digit, one('c'))).toEqual(`ab\\dc`); }); test('"word" character class', () => { - expect(buildPattern(word())).toEqual(`\\w`); + expect(buildPattern(word)).toEqual(`\\w`); - expect(buildPattern(one('ab'), word())).toEqual(`ab\\w`); + expect(buildPattern(one('ab'), word)).toEqual(`ab\\w`); - expect(buildPattern(one('ab'), word(), one('c'))).toEqual(`ab\\wc`); + expect(buildPattern(one('ab'), word, one('c'))).toEqual(`ab\\wc`); }); test('"any" character class', () => { - expect(buildPattern(any())).toEqual(`.`); + expect(buildPattern(any)).toEqual(`.`); - expect(buildPattern(one('ab'), any())).toEqual(`ab.`); + expect(buildPattern(one('ab'), any)).toEqual(`ab.`); - expect(buildPattern(one('ab'), any(), one('c'))).toEqual(`ab.c`); + expect(buildPattern(one('ab'), any, one('c'))).toEqual(`ab.c`); }); diff --git a/src/characterClasses.ts b/src/characterClasses.ts index 9858571..ea52c07 100644 --- a/src/characterClasses.ts +++ b/src/characterClasses.ts @@ -1,21 +1,13 @@ import type { Any, Digit, Whitespace, Word } from './types'; import type { CompilerMap } from './types-internal'; -export function whitespace(): Whitespace { - return { type: 'whitespace' }; -} +export const whitespace: Whitespace = { type: 'whitespace' }; -export function digit(): Digit { - return { type: 'digit' }; -} +export const digit: Digit = { type: 'digit' }; -export function word(): Word { - return { type: 'word' }; -} +export const word: Word = { type: 'word' }; -export function any(): Any { - return { type: 'any' }; -} +export const any: Any = { type: 'any' }; export const compilers = { whitespace: '\\s', From 725e4d9236a8d95ed618abbbe7f8c3c482c06890 Mon Sep 17 00:00:00 2001 From: Jan Jaworski Date: Wed, 6 Dec 2023 20:35:46 +0100 Subject: [PATCH 7/8] chore: refactor --- src/characterClasses.ts | 4 ++-- src/compiler.ts | 28 ++++++++++++++-------------- src/types-internal.ts | 7 ++++--- 3 files changed, 20 insertions(+), 19 deletions(-) diff --git a/src/characterClasses.ts b/src/characterClasses.ts index ea52c07..dce9858 100644 --- a/src/characterClasses.ts +++ b/src/characterClasses.ts @@ -1,5 +1,5 @@ import type { Any, Digit, Whitespace, Word } from './types'; -import type { CompilerMap } from './types-internal'; +import type { CharacterClassCompilerMap } from './types-internal'; export const whitespace: Whitespace = { type: 'whitespace' }; @@ -14,4 +14,4 @@ export const compilers = { digit: '\\d', word: '\\w', any: '.', -} satisfies CompilerMap; +} satisfies CharacterClassCompilerMap; diff --git a/src/compiler.ts b/src/compiler.ts index 4069c88..54d3da1 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -34,26 +34,26 @@ function compileSingle(elements: RegexElement): string { return elements; } - if ('children' in elements) { - const compiledChildren = compileList(elements.children); + if (!('children' in elements)) { + const characterCompiler = characterClasses[elements.type]; - if (elements.type === 'repeat') { - return compileRepeat(elements.config, compiledChildren); + if (!characterCompiler) { + throw new Error(`Unknown character type ${elements.type}`); } - const elementCompiler = quantifiers[elements.type]; - if (!elementCompiler) { - throw new Error(`Unknown elements type ${elements.type}`); - } - - return elementCompiler(compiledChildren); + return characterCompiler; } - const characterCompiler = characterClasses[elements.type]; + const compiledChildren = compileList(elements.children); + + if (elements.type === 'repeat') { + return compileRepeat(elements.config, compiledChildren); + } - if (!characterCompiler) { - throw new Error(`Unknown character type ${elements.type}`); + const elementCompiler = quantifiers[elements.type]; + if (!elementCompiler) { + throw new Error(`Unknown elements type ${elements.type}`); } - return characterCompiler; + return elementCompiler(compiledChildren); } diff --git a/src/types-internal.ts b/src/types-internal.ts index 197cd20..c998fbe 100644 --- a/src/types-internal.ts +++ b/src/types-internal.ts @@ -1,5 +1,6 @@ // Compilation export type ElementCompiler = (compiledChildren: string) => string; -export type CompilerMap = - | Record - | Record; + +export type CompilerMap = Record; + +export type CharacterClassCompilerMap = Record; From e51c687b382ac751e41232056c9a194b0ded8cf4 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Wed, 6 Dec 2023 22:35:39 +0100 Subject: [PATCH 8/8] refactor: cleanup and optimizing types --- src/__tests__/characterClasses.test.tsx | 4 +-- src/__tests__/compiler.test.tsx | 3 ++- src/__tests__/quantifiers.test.tsx | 2 +- src/__tests__/repeat.test.tsx | 3 ++- src/character-classes.ts | 26 ++++++++++++++++++ src/characterClasses.ts | 17 ------------ src/compiler.ts | 35 +++++++++++-------------- src/index.tsx | 4 +-- src/quantifiers/{index.ts => base.ts} | 25 +++++++----------- src/quantifiers/repeat.ts | 13 ++++++++- src/types-internal.ts | 6 ----- src/types.ts | 14 +++------- 12 files changed, 74 insertions(+), 78 deletions(-) create mode 100644 src/character-classes.ts delete mode 100644 src/characterClasses.ts rename src/quantifiers/{index.ts => base.ts} (75%) delete mode 100644 src/types-internal.ts diff --git a/src/__tests__/characterClasses.test.tsx b/src/__tests__/characterClasses.test.tsx index 1f2c7b4..6081cf5 100644 --- a/src/__tests__/characterClasses.test.tsx +++ b/src/__tests__/characterClasses.test.tsx @@ -1,6 +1,6 @@ -import { any, digit, whitespace, word } from '../characterClasses'; +import { any, digit, whitespace, word } from '../character-classes'; import { buildPattern } from '../compiler'; -import { one } from '../quantifiers'; +import { one } from '../quantifiers/base'; test('"whitespace" character class', () => { expect(buildPattern(whitespace)).toEqual(`\\s`); diff --git a/src/__tests__/compiler.test.tsx b/src/__tests__/compiler.test.tsx index dcdd8e4..96ecb5b 100644 --- a/src/__tests__/compiler.test.tsx +++ b/src/__tests__/compiler.test.tsx @@ -1,5 +1,6 @@ import { buildPattern, buildRegex } from '../compiler'; -import { oneOrMore, optionally, one, zeroOrMore, repeat } from '../quantifiers'; +import { oneOrMore, optionally, one, zeroOrMore } from '../quantifiers/base'; +import { repeat } from '../quantifiers/repeat'; test('basic quantifies', () => { expect(buildPattern('a')).toEqual('a'); diff --git a/src/__tests__/quantifiers.test.tsx b/src/__tests__/quantifiers.test.tsx index 219c0c0..2788ae1 100644 --- a/src/__tests__/quantifiers.test.tsx +++ b/src/__tests__/quantifiers.test.tsx @@ -1,4 +1,4 @@ -import { one, oneOrMore, optionally, zeroOrMore } from '../quantifiers'; +import { one, oneOrMore, optionally, zeroOrMore } from '../quantifiers/base'; import { buildPattern, buildRegex } from '../compiler'; test('"oneOrMore" quantifier', () => { diff --git a/src/__tests__/repeat.test.tsx b/src/__tests__/repeat.test.tsx index 5c8776f..2305ea8 100644 --- a/src/__tests__/repeat.test.tsx +++ b/src/__tests__/repeat.test.tsx @@ -1,5 +1,6 @@ import { buildPattern } from '../compiler'; -import { repeat, zeroOrMore, oneOrMore } from '../quantifiers'; +import { zeroOrMore, oneOrMore } from '../quantifiers/base'; +import { repeat } from '../quantifiers/repeat'; test('"repeat" quantifier', () => { expect(buildPattern('a', repeat({ min: 1, max: 5 }, 'b'))).toEqual('ab{1,5}'); diff --git a/src/character-classes.ts b/src/character-classes.ts new file mode 100644 index 0000000..9a25c83 --- /dev/null +++ b/src/character-classes.ts @@ -0,0 +1,26 @@ +import type { + Any, + CharacterClass, + Digit, + RegexElement, + Whitespace, + Word, +} from './types'; + +export const whitespace: Whitespace = { type: 'whitespace' }; +export const digit: Digit = { type: 'digit' }; +export const word: Word = { type: 'word' }; +export const any: Any = { type: 'any' }; + +export const characterClasses = { + whitespace: '\\s', + digit: '\\d', + word: '\\w', + any: '.', +} as const satisfies Record; + +export function isCharacterClass( + element: Exclude +): element is CharacterClass { + return element.type in characterClasses; +} diff --git a/src/characterClasses.ts b/src/characterClasses.ts deleted file mode 100644 index dce9858..0000000 --- a/src/characterClasses.ts +++ /dev/null @@ -1,17 +0,0 @@ -import type { Any, Digit, Whitespace, Word } from './types'; -import type { CharacterClassCompilerMap } from './types-internal'; - -export const whitespace: Whitespace = { type: 'whitespace' }; - -export const digit: Digit = { type: 'digit' }; - -export const word: Word = { type: 'word' }; - -export const any: Any = { type: 'any' }; - -export const compilers = { - whitespace: '\\s', - digit: '\\d', - word: '\\w', - any: '.', -} satisfies CharacterClassCompilerMap; diff --git a/src/compiler.ts b/src/compiler.ts index 54d3da1..18de405 100644 --- a/src/compiler.ts +++ b/src/compiler.ts @@ -1,7 +1,7 @@ import type { RegexElement } from './types'; -import { compilers as quantifiers } from './quantifiers'; +import { characterClasses, isCharacterClass } from './character-classes'; +import { baseQuantifiers, isBaseQuantifier } from './quantifiers/base'; import { compileRepeat } from './quantifiers/repeat'; -import { compilers as characterClasses } from './characterClasses'; /** * Generate RegExp object for elements. @@ -29,31 +29,26 @@ function compileList(elements: RegexElement[]): string { return elements.map((c) => compileSingle(c)).join(''); } -function compileSingle(elements: RegexElement): string { - if (typeof elements === 'string') { - return elements; +function compileSingle(element: RegexElement): string { + if (typeof element === 'string') { + return element; } - if (!('children' in elements)) { - const characterCompiler = characterClasses[elements.type]; - - if (!characterCompiler) { - throw new Error(`Unknown character type ${elements.type}`); - } - - return characterCompiler; + if (isCharacterClass(element)) { + return characterClasses[element.type]; } - const compiledChildren = compileList(elements.children); + const compiledChildren = compileList(element.children); - if (elements.type === 'repeat') { - return compileRepeat(elements.config, compiledChildren); + if (element.type === 'repeat') { + return compileRepeat(element.config, compiledChildren); } - const elementCompiler = quantifiers[elements.type]; - if (!elementCompiler) { - throw new Error(`Unknown elements type ${elements.type}`); + if (isBaseQuantifier(element)) { + const compiler = baseQuantifiers[element.type]; + return compiler(compiledChildren); } - return elementCompiler(compiledChildren); + // @ts-expect-error User passed incorrect type + throw new Error(`Unknown elements type ${element.type}`); } diff --git a/src/index.tsx b/src/index.tsx index 207dbb6..fd12f03 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,5 +1,5 @@ export type * from './types'; -export { whitespace } from './characterClasses'; +export { whitespace } from './character-classes'; export { buildRegex, buildPattern } from './compiler'; -export { oneOrMore, optionally } from './quantifiers'; +export { oneOrMore, optionally } from './quantifiers/base'; diff --git a/src/quantifiers/index.ts b/src/quantifiers/base.ts similarity index 75% rename from src/quantifiers/index.ts rename to src/quantifiers/base.ts index c6d170d..0564322 100644 --- a/src/quantifiers/index.ts +++ b/src/quantifiers/base.ts @@ -2,12 +2,10 @@ import type { One, OneOrMore, Optionally, + Quantifier, RegexElement, - Repeat, - RepeatConfig, ZeroOrMore, } from '../types'; -import type { CompilerMap } from '../types-internal'; import { wrapGroup } from '../utils'; export function oneOrMore(...children: RegexElement[]): OneOrMore { @@ -38,20 +36,15 @@ export function zeroOrMore(...children: RegexElement[]): ZeroOrMore { }; } -export function repeat( - config: RepeatConfig, - ...children: RegexElement[] -): Repeat { - return { - type: 'repeat', - children, - config, - }; -} - -export const compilers = { +export const baseQuantifiers = { one: (compiledChildren) => compiledChildren, oneOrMore: (compiledChildren) => `${wrapGroup(compiledChildren)}+`, optionally: (compiledChildren) => `${wrapGroup(compiledChildren)}?`, zeroOrMore: (compiledChildren) => `${wrapGroup(compiledChildren)}*`, -} satisfies CompilerMap; +} as const satisfies Record string>; + +export function isBaseQuantifier( + element: Exclude +): element is Quantifier { + return element.type in baseQuantifiers; +} diff --git a/src/quantifiers/repeat.ts b/src/quantifiers/repeat.ts index 3905274..c6c6288 100644 --- a/src/quantifiers/repeat.ts +++ b/src/quantifiers/repeat.ts @@ -1,6 +1,17 @@ -import type { RepeatConfig } from '../types'; +import type { RegexElement, Repeat, RepeatConfig } from '../types'; import { wrapGroup } from '../utils'; +export function repeat( + config: RepeatConfig, + ...children: RegexElement[] +): Repeat { + return { + type: 'repeat', + children, + config, + }; +} + export function compileRepeat( config: RepeatConfig, compiledChildren: string diff --git a/src/types-internal.ts b/src/types-internal.ts deleted file mode 100644 index c998fbe..0000000 --- a/src/types-internal.ts +++ /dev/null @@ -1,6 +0,0 @@ -// Compilation -export type ElementCompiler = (compiledChildren: string) => string; - -export type CompilerMap = Record; - -export type CharacterClassCompilerMap = Record; diff --git a/src/types.ts b/src/types.ts index 3b8ef4d..0da8d35 100644 --- a/src/types.ts +++ b/src/types.ts @@ -1,21 +1,13 @@ -export type RegexElement = string | RegexCharacterClass | RegexQuantifier; +export type RegexElement = string | CharacterClass | Quantifier; -export type RegexCharacterClass = Whitespace | Digit | Word | Any; +export type CharacterClass = Whitespace | Digit | Word | Any; -export type RegexQuantifier = - | One - | OneOrMore - | Optionally - | ZeroOrMore - | Repeat; +export type Quantifier = One | OneOrMore | Optionally | ZeroOrMore | Repeat; // Character classes export type Whitespace = { type: 'whitespace' }; - export type Digit = { type: 'digit' }; - export type Word = { type: 'word' }; - export type Any = { type: 'any' }; // Quantifiers