From c439489797962e38870a019d5fb66c36bfff0c55 Mon Sep 17 00:00:00 2001 From: Maciej Jastrzebski Date: Mon, 25 Dec 2023 21:27:55 +0100 Subject: [PATCH] feat: base anchors --- src/components/__tests__/anchors.test.tsx | 20 +++++++++++++++++++ src/components/anchors.ts | 19 ++++++++++++++++++ src/components/types.ts | 24 ++++++++++++++--------- src/encoder/encoder.ts | 6 ++++++ 4 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 src/components/__tests__/anchors.test.tsx create mode 100644 src/components/anchors.ts diff --git a/src/components/__tests__/anchors.test.tsx b/src/components/__tests__/anchors.test.tsx new file mode 100644 index 0000000..bfc7a8e --- /dev/null +++ b/src/components/__tests__/anchors.test.tsx @@ -0,0 +1,20 @@ +import { endOfString, startOfString } from '../anchors'; +import { oneOrMore } from '../quantifiers'; + +test('`startOfString` basic cases', () => { + expect([startOfString]).toHavePattern('^'); + expect([startOfString, 'a', 'b']).toHavePattern('^ab'); +}); + +test('`startOfString` regex tests', () => { + expect([startOfString, oneOrMore('a')]).toMatchGroups('a aa aaa', ['a']); +}); + +test('`endOfString` basic cases', () => { + expect([endOfString]).toHavePattern('$'); + expect(['a', 'b', endOfString]).toHavePattern('ab$'); +}); + +test('`endOfString` regex tests', () => { + expect([oneOrMore('a'), endOfString]).toMatchGroups('a aa aaa', ['aaa']); +}); diff --git a/src/components/anchors.ts b/src/components/anchors.ts new file mode 100644 index 0000000..78e537c --- /dev/null +++ b/src/components/anchors.ts @@ -0,0 +1,19 @@ +import { type EncoderNode, EncoderPrecedence } from '../encoder/types'; +import type { Anchor } from './types'; + +export const startOfString: Anchor = { + type: 'anchor', + symbol: '^', +}; + +export const endOfString: Anchor = { + type: 'anchor', + symbol: '$', +}; + +export function encodeAnchor(anchor: Anchor): EncoderNode { + return { + precedence: EncoderPrecedence.Sequence, + pattern: anchor.symbol, + }; +} diff --git a/src/components/types.ts b/src/components/types.ts index ff61c68..7c5e36d 100644 --- a/src/components/types.ts +++ b/src/components/types.ts @@ -1,19 +1,13 @@ export type RegexElement = | string - | Capture | CharacterClass + | Anchor | ChoiceOf - | Quantifier; + | Quantifier + | Capture; export type Quantifier = One | OneOrMore | Optionally | ZeroOrMore | Repeat; -export type CharacterClass = { - type: 'characterClass'; - characters: string[]; - ranges: CharacterRange[]; - isInverted: boolean; -}; - /** * Character range from start to end (inclusive). */ @@ -23,6 +17,18 @@ export type CharacterRange = { }; // Components +export type CharacterClass = { + type: 'characterClass'; + characters: string[]; + ranges: CharacterRange[]; + isInverted: boolean; +}; + +export type Anchor = { + type: 'anchor'; + symbol: string; +}; + export type ChoiceOf = { type: 'choiceOf'; children: RegexElement[][]; diff --git a/src/encoder/encoder.ts b/src/encoder/encoder.ts index bd6c8ec..b588235 100644 --- a/src/encoder/encoder.ts +++ b/src/encoder/encoder.ts @@ -1,4 +1,5 @@ import type { RegexElement } from '../components/types'; +import { encodeAnchor } from '../components/anchors'; import { encodeCapture } from '../components/capture'; import { encodeCharacterClass } from '../components/character-class'; import { encodeChoiceOf } from '../components/choice-of'; @@ -10,6 +11,7 @@ import { } from '../components/quantifiers'; import { encodeRepeat } from '../components/repeat'; import { escapeText } from '../utils/text'; + import { type EncoderNode, EncoderPrecedence } from './types'; import { concatNodes } from './utils'; @@ -26,6 +28,10 @@ export function encodeElement(element: RegexElement): EncoderNode { return encodeCharacterClass(element); } + if (element.type === 'anchor') { + return encodeAnchor(element); + } + if (element.type === 'choiceOf') { return encodeChoiceOf(element, encodeSequence); }