Skip to content

docs: more examples #49

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Jan 22, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const hexDigit = charClass(
charRange('0', '9'),
);

const hexColor = buildRegExp(
const hexColor = buildRegExp([
startOfString,
optional('#'),
capture(
Expand All @@ -31,7 +31,7 @@ const hexColor = buildRegExp(
),
),
endOfString,
);
]);
```

## Installation
Expand Down
121 changes: 115 additions & 6 deletions docs/Examples.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,133 @@
# Regex Examples

## JavaScript number
## Match hashtags

This regex matches and captures all hashtags in a given string.

```ts
const hashtags = buildRegExp(
[
'#',
capture(oneOrMore(word)),
],
{ global: true },
);

const hashtagMatches = '#hello #world'.matchAll(hashtags);
```

Encoded regex: `/#(\w+)/g`.

See tests: [example-hashtags.ts](../src/__tests__/example-hashtags.ts).

## Hex color validation

This regex validate whether given string is a valid hex color, with 6 or 3 hex digits.

```ts
const hexDigit = charClass(digit, charRange('a', 'f'));

const regex = buildRegExp(
[
startOfString,
optional('#'),
choiceOf(
repeat(hexDigit, 6), // #rrggbb
repeat(hexDigit, 3), // #rgb
),
endOfString,
],
{ ignoreCase: true },
);
```

Encoded regex: `/^#?(?:[a-f\d]{6}|[a-f\d]{3})$/i`.

See tests: [example-hex-color.ts](../src/__tests__/example-hex-color.ts).

## Simple URL validation

This regex validates (in simplified way) whether given string is a URL.

```ts
const protocol = [choiceOf('http', 'https'), '://'];
const domainChars = charClass(charRange('a', 'z'), digit);
const domainCharsHypen = charClass(domainChars, anyOf('-'));

const domainSegment = choiceOf(
domainChars, // single char
[domainChars, zeroOrMore(domainCharsHypen), domainChars], // multi char
);

const regex = buildRegExp([
startOfString,
optional(protocol),
oneOrMore([domainSegment, '.']), // domain segment
charRange('a', 'z'), // TLD first char
oneOrMore(domainChars), // TLD remaining chars
endOfString,
]);
```

Encoded regex: `/^(?:(?:http|https):\/\/)?(?:(?:[a-z\d]|[a-z\d][a-z\d-]*[a-z\d])\.)+[a-z][a-z\d]+$/`.

See tests: [example-url.ts](../src/__tests__/example-url.ts).

## Email address validation

This regex validates whether given string is a properly formatted email address.

```ts
const hostnameChars = charClass(charRange('a', 'z'), digit, anyOf('-.'));
const domainChars = charRange('a', 'z');

const regex = buildRegExp(
[
startOfString,
oneOrMore(usernameChars),
'@',
oneOrMore(hostnameChars),
'.',
repeat(domainChars, { min: 2 }),
endOfString,
],
{ ignoreCase: true },
);

const isValid = regex.test("user@example.com");
```

Encoded regex: `/^[a-z\d._%+-]+@[a-z\d.-]+\.[a-z]{2,}$/i`.

See tests: [example-email.ts](../src/__tests__/example-email.ts).

## JavaScript number validation

This regex validates if given string is a valid JavaScript number.


```ts
const sign = anyOf('+-');
const exponent = [anyOf('eE'), optional(sign), oneOrMore(digit)];

const regex = buildRegExp([
startOfString,
optional(sing),
choiceOf(
[oneOrMore(digit), optional(['.', zeroOrMore(digit)])], // leading digit
['.', oneOrMore(digit)], // leading dot
),
optional(exponent), // exponent
endOfString,
]);

const isValid = regex.test("1.0e+27");
```

Encoded regex: `/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/`.

See tests: [example-js-number.ts](../src/__tests__/example-js-number.ts).

## IPv4 address validation

```ts
Expand All @@ -37,9 +149,6 @@ const regex = buildRegExp([
]);
```

This code generates the following regex pattern:
Encoded regex: `/^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/`.

```ts
const regex =
/^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/;
```
See tests: [example-ipv4.ts](../src/__tests__/example-ipv4.ts).
3 changes: 2 additions & 1 deletion jest-setup.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import './test-utils/to-have-pattern';
import './test-utils/to-equal-regex';
import './test-utils/to-match-groups';
import './test-utils/to-match-all-groups';
import './test-utils/to-match-string';
42 changes: 42 additions & 0 deletions src/__tests__/example-email.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import {
anyOf,
buildRegExp,
charClass,
charRange,
digit,
endOfString,
oneOrMore,
repeat,
startOfString,
} from '../index';

test('example: email validation', () => {
const usernameChars = charClass(charRange('a', 'z'), digit, anyOf('._%+-'));
const hostnameChars = charClass(charRange('a', 'z'), digit, anyOf('-.'));
const domainChars = charRange('a', 'z');

const regex = buildRegExp(
[
startOfString,
oneOrMore(usernameChars),
'@',
oneOrMore(hostnameChars),
'.',
repeat(domainChars, { min: 2 }),
endOfString,
],
{ ignoreCase: true },
);

expect(regex).toMatchString('aaa@gmail.co');
expect(regex).toMatchString('aaa@gmail.com');
expect(regex).toMatchString('Aaa@GMail.Com');
expect(regex).toMatchString('aaa@long.domain.example.com');

expect(regex).not.toMatchString('@');
expect(regex).not.toMatchString('aaa@');
expect(regex).not.toMatchString('a@gmail.c');
expect(regex).not.toMatchString('@gmail.com');

expect(regex).toEqualRegex(/^[a-z\d._%+-]+@[a-z\d.-]+\.[a-z]{2,}$/i);
});
24 changes: 24 additions & 0 deletions src/__tests__/example-hashtags.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { buildRegExp } from '../builders';
import { capture, oneOrMore, word } from '../index';

test('example: extracting hashtags', () => {
const regex = buildRegExp(
[
'#', // prettier break-line
capture(oneOrMore(word)),
],
{ global: true },
);

expect(regex).toMatchAllGroups('Hello #world!', [['#world', 'world']]);
expect(regex).toMatchAllGroups('#Hello #world!', [
['#Hello', 'Hello'],
['#world', 'world'],
]);

expect(regex).not.toMatchString('aa');
expect(regex).not.toMatchString('#');
expect(regex).not.toMatchString('a# ');

expect(regex).toEqualRegex(/#(\w+)/g);
});
46 changes: 46 additions & 0 deletions src/__tests__/example-hex-color.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import {
buildRegExp,
charClass,
charRange,
choiceOf,
digit,
endOfString,
optional,
repeat,
startOfString,
} from '../index';

test('example: hex color validation', () => {
const hexDigit = charClass(digit, charRange('a', 'f'));

const regex = buildRegExp(
[
startOfString,
optional('#'),
choiceOf(
repeat(hexDigit, 6), // #rrggbb
repeat(hexDigit, 3), // #rgb
),
endOfString,
],
{ ignoreCase: true },
);

expect(regex).toMatchString('#ffffff');
expect(regex).toMatchString('ffffff');
expect(regex).toMatchString('#eee');
expect(regex).toMatchString('bbb');
expect(regex).toMatchString('#000');
expect(regex).toMatchString('#123456');
expect(regex).toMatchString('123456');
expect(regex).toMatchString('#123');
expect(regex).toMatchString('123');

expect(regex).not.toMatchString('#1');
expect(regex).not.toMatchString('#12');
expect(regex).not.toMatchString('#1234');
expect(regex).not.toMatchString('#12345');
expect(regex).not.toMatchString('#1234567');

expect(regex).toEqualRegex(/^#?(?:[a-f\d]{6}|[a-f\d]{3})$/i);
});
43 changes: 43 additions & 0 deletions src/__tests__/example-ipv4.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import {
buildRegExp,
charRange,
choiceOf,
digit,
endOfString,
repeat,
startOfString,
} from '../index';

test('example: IPv4 address validator', () => {
const octet = choiceOf(
[digit],
[charRange('1', '9'), digit],
['1', repeat(digit, 2)],
['2', charRange('0', '4'), digit],
['25', charRange('0', '5')],
);

const regex = buildRegExp([
startOfString, // prettier break-line
repeat([octet, '.'], 3),
octet,
endOfString,
]);

expect(regex).toMatchString('0.0.0.0');
expect(regex).toMatchString('192.168.0.1');
expect(regex).toMatchString('1.99.100.249');
expect(regex).toMatchString('255.255.255.255');
expect(regex).toMatchString('123.45.67.89');

expect(regex).not.toMatchString('0.0.0.');
expect(regex).not.toMatchString('0.0.0.0.');
expect(regex).not.toMatchString('0.-1.0.0');
expect(regex).not.toMatchString('0.1000.0.0');
expect(regex).not.toMatchString('0.0.300.0');
expect(regex).not.toMatchString('255.255.255.256');

expect(regex).toEqualRegex(
/^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/,
);
});
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import {
anyOf,
buildRegExp,
charRange,
choiceOf,
digit,
endOfString,
oneOrMore,
optional,
repeat,
startOfString,
zeroOrMore,
} from '../index';
Expand Down Expand Up @@ -48,39 +46,5 @@ test('example: validate JavaScript number', () => {
expect(regex).not.toMatchString('.1.1');
expect(regex).not.toMatchString('.');

expect(regex).toHavePattern(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/);
});

test('example: IPv4 address validator', () => {
const octet = choiceOf(
[digit],
[charRange('1', '9'), digit],
['1', repeat(digit, 2)],
['2', charRange('0', '4'), digit],
['25', charRange('0', '5')],
);

const regex = buildRegExp([
startOfString, //
repeat([octet, '.'], 3),
octet,
endOfString,
]);

expect(regex).toMatchString('0.0.0.0');
expect(regex).toMatchString('192.168.0.1');
expect(regex).toMatchString('1.99.100.249');
expect(regex).toMatchString('255.255.255.255');
expect(regex).toMatchString('123.45.67.89');

expect(regex).not.toMatchString('0.0.0.');
expect(regex).not.toMatchString('0.0.0.0.');
expect(regex).not.toMatchString('0.-1.0.0');
expect(regex).not.toMatchString('0.1000.0.0');
expect(regex).not.toMatchString('0.0.300.0');
expect(regex).not.toMatchString('255.255.255.256');

expect(regex).toHavePattern(
/^(?:(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d|[1-9]\d|1\d{2}|2[0-4]\d|25[0-5])$/,
);
expect(regex).toEqualRegex(/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/);
});
Loading