@ptolemy2002/rgx 9.0.0 → 11.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -17,6 +17,7 @@ Because there is so much to document, it has been broken up into multiple files.
17
17
  - [escapeRegex](./docs/util/escapeRegex.md) - The `escapeRegex` function, which escapes special regex characters in a given string and assures you that the result is valid Regex.
18
18
  - [regexMatchAtPosition](./docs/util/regexMatchAtPosition.md) - The `regexMatchAtPosition` function and related functions, which attempt to match a given regular expression at a specific position in a string.
19
19
  - [regexWithFlags](./docs/util/regexWithFlags.md) - The `regexWithFlags` function, which creates a new regular expression with the same source as a given regular expression but with different flags.
20
+ - [createRegex](./docs/util/createRegex.md) - The `createRegex` function, which safely constructs a `RegExp` or `ExtRegExp` from a pattern string, converting `SyntaxError` into `RGXInvalidRegexStringError`.
20
21
  - [createRGXClassGuardFunction](./docs/util/createRGXClassGuardFunction.md) - The `createRGXClassGuardFunction` and `createAssertRGXClassGuardFunction` utilities for creating type guard and assertion functions for class instances.
21
22
  - `class` - A directory containing documentation for all classes in the library.
22
23
  - [collection](./docs/class/collection.md) - The `RGXTokenCollection` class, which is a collection of tokens.
@@ -1,5 +1,5 @@
1
1
  import { ValidRegexFlags } from "./types";
2
- export type RegExpFlagTransformer = (exp: RegExp) => RegExp;
2
+ export type RegExpFlagTransformer = (exp: RegExp) => [string, string];
3
3
  export declare class ExtRegExp extends RegExp {
4
4
  private extFlags;
5
5
  constructor(pattern: string | RegExp, flags?: string);
package/dist/ExtRegExp.js CHANGED
@@ -12,6 +12,7 @@ exports.assertValidRegexFlags = assertValidRegexFlags;
12
12
  const errors_1 = require("./errors");
13
13
  const internal_1 = require("./internal");
14
14
  const typeGuards_1 = require("./typeGuards");
15
+ const utils_1 = require("./utils");
15
16
  const flagTransformers = {};
16
17
  class ExtRegExp extends RegExp {
17
18
  constructor(pattern, flags = '') {
@@ -20,7 +21,7 @@ class ExtRegExp extends RegExp {
20
21
  const customFlags = extractCustomRegexFlags(flags);
21
22
  const source = pattern instanceof RegExp ? pattern.source : pattern;
22
23
  const alreadyAppliedFlags = pattern instanceof RegExp ? pattern.flags : '';
23
- const { source: transformedSource, flags: transformedFlags } = applyFlagTransformers(new RegExp(source, vanillaFlags), customFlags, alreadyAppliedFlags);
24
+ const { source: transformedSource, flags: transformedFlags } = applyFlagTransformers((0, utils_1.createRegex)(source, vanillaFlags), customFlags, alreadyAppliedFlags);
24
25
  super(transformedSource, transformedFlags);
25
26
  this.extFlags = customFlags;
26
27
  }
@@ -56,9 +57,29 @@ function unregisterFlagTransformer(key) {
56
57
  delete flagTransformers[key];
57
58
  }
58
59
  function applyFlagTransformers(regex, flags, alreadyAppliedFlags = '') {
60
+ if (Object.getPrototypeOf(regex) !== RegExp.prototype) {
61
+ throw new errors_1.RGXNotDirectRegExpError("Cannot apply flag transformers to non-direct RegExp instances", regex.constructor.name);
62
+ }
59
63
  for (const flag in flagTransformers) {
60
64
  if (flags.includes(flag) && !alreadyAppliedFlags.includes(flag)) {
61
- regex = flagTransformers[flag](regex);
65
+ const [newSource, newFlags] = flagTransformers[flag](regex);
66
+ try {
67
+ if ((0, typeGuards_1.isValidVanillaRegexFlags)(newFlags)) {
68
+ regex = new RegExp(newSource, newFlags);
69
+ }
70
+ else {
71
+ throw new errors_1.RGXInvalidVanillaRegexFlagsError(`Flag transformer for flag "${flag}" produced invalid vanilla regex flags.`, newFlags);
72
+ }
73
+ }
74
+ catch (e) {
75
+ if (e instanceof SyntaxError) {
76
+ throw new errors_1.RGXInvalidRegexStringError(`Flag transformer for flag "${flag}" produced an invalid regex.`, newSource, e);
77
+ }
78
+ // This is ignored because I don't know what kind of
79
+ // unexpected errors might happen.
80
+ /* istanbul ignore next */
81
+ throw e;
82
+ }
62
83
  alreadyAppliedFlags += flag;
63
84
  }
64
85
  }
@@ -52,7 +52,7 @@ class RGXGroupToken extends base_1.RGXClassToken {
52
52
  result = `(?:${result})`;
53
53
  else
54
54
  result = `(${result})`;
55
- return new RegExp(result);
55
+ return (0, utils_1.createRegex)(result);
56
56
  }
57
57
  clone(depth = "max") {
58
58
  if (depth === 0)
@@ -19,7 +19,7 @@ class RGXLookaheadToken extends lookaround_1.RGXLookaroundToken {
19
19
  result = `(?=${result})`;
20
20
  else
21
21
  result = `(?!${result})`;
22
- return new RegExp(result);
22
+ return (0, utils_1.createRegex)(result);
23
23
  }
24
24
  clone(depth = "max") {
25
25
  if (depth === 0)
@@ -19,7 +19,7 @@ class RGXLookbehindToken extends lookaround_1.RGXLookaroundToken {
19
19
  result = `(?<=${result})`;
20
20
  else
21
21
  result = `(?<!${result})`;
22
- return new RegExp(result);
22
+ return (0, utils_1.createRegex)(result);
23
23
  }
24
24
  clone(depth = "max") {
25
25
  if (depth === 0)
@@ -90,7 +90,7 @@ class RGXRepeatToken extends base_1.RGXClassToken {
90
90
  if (this.min === 0 && this.max === 0)
91
91
  return null;
92
92
  const resolvedSource = (0, resolve_1.resolveRGXToken)(this.token);
93
- return new RegExp(`${resolvedSource}${this.repeaterSuffix}`);
93
+ return (0, utils_1.createRegex)(`${resolvedSource}${this.repeaterSuffix}`);
94
94
  }
95
95
  clone(depth = "max") {
96
96
  if (depth === 0)
@@ -26,10 +26,10 @@ class RGXSubpatternToken extends base_1.RGXClassToken {
26
26
  }
27
27
  toRgx() {
28
28
  if (typeof this.pattern === "string") {
29
- return new RegExp(`\\k<${this.pattern}>`);
29
+ return (0, utils_1.createRegex)(`\\k<${this.pattern}>`);
30
30
  }
31
31
  else {
32
- return new RegExp(`\\${this.pattern}`);
32
+ return (0, utils_1.createRegex)(`\\${this.pattern}`);
33
33
  }
34
34
  }
35
35
  clone(depth = "max") {
@@ -33,7 +33,7 @@ class RGXTokenCollection {
33
33
  else {
34
34
  pattern = (0, concat_1.rgxConcat)(this.tokens);
35
35
  }
36
- return new RegExp(pattern);
36
+ return (0, utils_1.createRegex)(pattern);
37
37
  }
38
38
  getTokens() {
39
39
  return (0, immutability_utils_1.extClone)(this.tokens, "max");
package/dist/concat.js CHANGED
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.rgxConcat = rgxConcat;
4
+ const ExtRegExp_1 = require("./ExtRegExp");
4
5
  const internal_1 = require("./internal");
5
6
  const resolve_1 = require("./resolve");
6
7
  // Wrapper for letting an array of tokens be resolved as a concatenation instead of a union.
7
8
  function rgxConcat(tokens, groupWrap = true, currentFlags = '') {
9
+ (0, ExtRegExp_1.assertValidRegexFlags)(currentFlags);
8
10
  const result = tokens.map(t => (0, resolve_1.resolveRGXToken)(t, groupWrap, true, currentFlags)).join('');
9
11
  (0, internal_1.assureAcceptance)(tokens, currentFlags);
10
12
  return result;
@@ -1,4 +1,4 @@
1
- export type RGXErrorCode = 'UNKNOWN' | 'INVALID_RGX_TOKEN' | 'INVALID_REGEX_STRING' | 'INVALID_REGEX_FLAGS' | 'INVALID_VANILLA_REGEX_FLAGS' | 'NOT_IMPLEMENTED' | 'NOT_SUPPORTED' | 'INVALID_IDENTIFIER' | 'OUT_OF_BOUNDS' | 'INVALID_FLAG_TRANSFORMER_KEY' | 'FLAG_TRANSFORMER_CONFLICT' | 'CONSTANT_CONFLICT' | 'INVALID_CONSTANT_KEY' | 'INSERTION_REJECTED' | 'REGEX_NOT_MATCHED_AT_POSITION' | 'PART_VALIDATION_FAILED' | 'INVALID_LEXER_MODE' | 'LEXEME_NOT_MATCHED_AT_POSITION' | 'INVALID_RGX_LEXER' | 'INVALID_RGX_WALKER' | 'INVALID_RGX_PART';
1
+ export type RGXErrorCode = 'UNKNOWN' | 'INVALID_RGX_TOKEN' | 'INVALID_REGEX_STRING' | 'INVALID_REGEX_FLAGS' | 'INVALID_VANILLA_REGEX_FLAGS' | 'NOT_IMPLEMENTED' | 'NOT_SUPPORTED' | 'INVALID_IDENTIFIER' | 'OUT_OF_BOUNDS' | 'INVALID_FLAG_TRANSFORMER_KEY' | 'FLAG_TRANSFORMER_CONFLICT' | 'CONSTANT_CONFLICT' | 'INVALID_CONSTANT_KEY' | 'INSERTION_REJECTED' | 'REGEX_NOT_MATCHED_AT_POSITION' | 'PART_VALIDATION_FAILED' | 'INVALID_LEXER_MODE' | 'LEXEME_NOT_MATCHED_AT_POSITION' | 'INVALID_RGX_LEXER' | 'INVALID_RGX_WALKER' | 'INVALID_RGX_PART' | 'NOT_DIRECT_REGEXP';
2
2
  export declare class RGXError extends Error {
3
3
  _message: string;
4
4
  code: RGXErrorCode;
@@ -19,3 +19,4 @@ export * from './lexemeNotMatchedAtPosition';
19
19
  export * from './invalidLexer';
20
20
  export * from './invalidWalker';
21
21
  export * from './invalidPart';
22
+ export * from './notDirectRegexp';
@@ -35,3 +35,4 @@ __exportStar(require("./lexemeNotMatchedAtPosition"), exports);
35
35
  __exportStar(require("./invalidLexer"), exports);
36
36
  __exportStar(require("./invalidWalker"), exports);
37
37
  __exportStar(require("./invalidPart"), exports);
38
+ __exportStar(require("./notDirectRegexp"), exports);
@@ -1,6 +1,7 @@
1
1
  import { RGXError } from "./";
2
2
  export declare class RGXInvalidRegexStringError extends RGXError {
3
3
  got: string;
4
- constructor(message: string, got: string);
4
+ cause: SyntaxError;
5
+ constructor(message: string, got: string, cause: SyntaxError);
5
6
  calcMessage(message: string): string;
6
7
  }
@@ -3,13 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RGXInvalidRegexStringError = void 0;
4
4
  const errors_1 = require("./");
5
5
  class RGXInvalidRegexStringError extends errors_1.RGXError {
6
- constructor(message, got) {
6
+ constructor(message, got, cause) {
7
7
  super(message, 'INVALID_REGEX_STRING');
8
8
  this.name = 'RGXInvalidRegexStringError';
9
9
  this.got = got;
10
+ this.cause = cause;
10
11
  }
11
12
  calcMessage(message) {
12
- return `${message}; Got: ${JSON.stringify(this.got)}`;
13
+ return `${message}; Got: ${JSON.stringify(this.got)}; Cause: ${this.cause.message}`;
13
14
  }
14
15
  }
15
16
  exports.RGXInvalidRegexStringError = RGXInvalidRegexStringError;
@@ -0,0 +1,6 @@
1
+ import { RGXError } from "./base";
2
+ export declare class RGXNotDirectRegExpError extends RGXError {
3
+ gotConstructorName: string;
4
+ constructor(message: string, gotConstructorName: string);
5
+ calcMessage(message: string): string;
6
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RGXNotDirectRegExpError = void 0;
4
+ const base_1 = require("./base");
5
+ class RGXNotDirectRegExpError extends base_1.RGXError {
6
+ constructor(message, gotConstructorName) {
7
+ super(message, 'NOT_DIRECT_REGEXP');
8
+ this.name = 'RGXNotDirectRegExpError';
9
+ this.gotConstructorName = gotConstructorName;
10
+ }
11
+ calcMessage(message) {
12
+ return `${message}; Expected direct instance of RegExp; Got instance of ${this.gotConstructorName}`;
13
+ }
14
+ }
15
+ exports.RGXNotDirectRegExpError = RGXNotDirectRegExpError;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.accentInsensitiveFlagTransformer = void 0;
4
4
  const constants_1 = require("../constants");
5
5
  const resolve_1 = require("../resolve");
6
+ const utils_1 = require("../utils");
6
7
  const accentPatterns = [
7
8
  "(a|á|à|ä|â|ã)", "(A|Á|À|Ä|Â|Ã)",
8
9
  "(e|é|è|ë|ê)", "(E|É|È|Ë|Ê)",
@@ -26,8 +27,8 @@ const accentInsensitiveFlagTransformer = function (exp) {
26
27
  const flags = exp.flags;
27
28
  accentPatterns.forEach((pattern) => {
28
29
  // Replace any of the characters in the pattern with the pattern itself
29
- source = source.replaceAll(new RegExp(nonEscapeBound + nonLocalizedFlagBound + nonCharacterClassBound + pattern, "g"), pattern);
30
+ source = source.replaceAll((0, utils_1.createRegex)(nonEscapeBound + nonLocalizedFlagBound + nonCharacterClassBound + pattern, "g"), pattern);
30
31
  });
31
- return new RegExp(source, flags);
32
+ return [source, flags];
32
33
  };
33
34
  exports.accentInsensitiveFlagTransformer = accentInsensitiveFlagTransformer;
package/dist/index.d.ts CHANGED
@@ -16,6 +16,6 @@ export * from "./constants";
16
16
  export * from "./walker";
17
17
  export * from "./lexer";
18
18
  export declare function rgxa(tokens: t.RGXToken[], flags?: string): ExtRegExp;
19
- export default function rgx(flags?: string, multiline?: boolean): (strings: TemplateStringsArray, ...tokens: t.RGXToken[]) => ExtRegExp;
19
+ export default function rgx(flags?: string, multiline?: boolean, verbatim?: boolean): (strings: TemplateStringsArray, ...tokens: t.RGXToken[]) => ExtRegExp;
20
20
  export declare function rgxwa<R = unknown, S = unknown, T = unknown>(source: string, tokens: RGXTokenOrPart<R, S, T>[], options?: Omit<t.RGXWOptions<R, S>, "multiline">): RGXWalker<R, S>;
21
- export declare function rgxw<R = unknown, S = unknown, T = unknown>(source: string, { multiline, ...options }?: t.RGXWOptions<R, S>): (strings: TemplateStringsArray, ...tokens: RGXTokenOrPart<R, S, T>[]) => RGXWalker<R, S>;
21
+ export declare function rgxw<R = unknown, S = unknown, T = unknown>(source: string, { multiline, verbatim, ...options }?: t.RGXWOptions<R, S>): (strings: TemplateStringsArray, ...tokens: RGXTokenOrPart<R, S, T>[]) => RGXWalker<R, S>;
package/dist/index.js CHANGED
@@ -24,6 +24,7 @@ const concat_1 = require("./concat");
24
24
  const internal_1 = require("./internal");
25
25
  const ExtRegExp_1 = require("./ExtRegExp");
26
26
  const walker_1 = require("./walker");
27
+ const utils_1 = require("./utils");
27
28
  __exportStar(require("./errors"), exports);
28
29
  __exportStar(require("./types"), exports);
29
30
  __exportStar(require("./typeGuards"), exports);
@@ -46,20 +47,20 @@ function rgxa(tokens, flags = '') {
46
47
  (0, ExtRegExp_1.assertValidRegexFlags)(flags);
47
48
  (0, internal_1.assureAcceptance)(tokens, flags);
48
49
  const pattern = (0, concat_1.rgxConcat)(tokens, true, flags);
49
- return (0, ExtRegExp_1.extRegExp)(pattern, flags);
50
+ return (0, utils_1.createRegex)(pattern, flags, true);
50
51
  }
51
- function rgx(flags = '', multiline = true) {
52
- (0, ExtRegExp_1.assertValidRegexFlags)(flags);
52
+ function rgx(flags = '', multiline = true, verbatim = true) {
53
53
  return (strings, ...tokens) => {
54
- return rgxa((0, internal_1.taggedTemplateToArray)(strings, tokens, multiline), flags);
54
+ // It is safe to assert the result because we know there will be no parts passed in here.
55
+ return rgxa((0, internal_1.rgxTaggedTemplateToArray)(strings, tokens, multiline, verbatim), flags);
55
56
  };
56
57
  }
57
58
  function rgxwa(source, tokens, options = {}) {
58
59
  (0, internal_1.assureAcceptance)(tokens.map(t => walker_1.RGXPart.check(t) ? t.token : t), '');
59
60
  return new walker_1.RGXWalker(source, tokens, options);
60
61
  }
61
- function rgxw(source, { multiline = true, ...options } = {}) {
62
+ function rgxw(source, { multiline = true, verbatim = true, ...options } = {}) {
62
63
  return (strings, ...tokens) => {
63
- return rgxwa(source, (0, internal_1.taggedTemplateToArray)(strings, tokens, multiline), options);
64
+ return rgxwa(source, (0, internal_1.rgxTaggedTemplateToArray)(strings, tokens, multiline, verbatim), options);
64
65
  };
65
66
  }
@@ -1 +1,2 @@
1
- export declare function taggedTemplateToArray<T>(strings: TemplateStringsArray, tokens: T[], multiline: boolean): (string | T)[];
1
+ import { RGXTokenOrPart } from "../walker";
2
+ export declare function rgxTaggedTemplateToArray<R = unknown, S = unknown, T = unknown>(strings: TemplateStringsArray, tokens: RGXTokenOrPart<R, S, T>[], multiline: boolean, verbatim: boolean): RGXTokenOrPart<R, S, T>[];
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.taggedTemplateToArray = taggedTemplateToArray;
4
- function taggedTemplateToArray(strings, tokens, multiline) {
3
+ exports.rgxTaggedTemplateToArray = rgxTaggedTemplateToArray;
4
+ function rgxTaggedTemplateToArray(strings, tokens, multiline, verbatim) {
5
5
  function isNullOrUndefined(value) {
6
6
  return value === null || value === undefined;
7
7
  }
@@ -12,13 +12,27 @@ function taggedTemplateToArray(strings, tokens, multiline) {
12
12
  // Strings always come before tokens
13
13
  if (!isNullOrUndefined(string)) {
14
14
  if (!multiline) {
15
- array.push(string);
15
+ if (verbatim)
16
+ array.push(string);
17
+ else
18
+ array.push({ rgxInterpolate: true, toRgx: () => string });
16
19
  }
17
20
  else {
21
+ const startsNewLine = string.startsWith("\n");
18
22
  // Remove all empty lines, remove comments, and trim whitespace from the start of each line.
19
23
  let lines = string
20
24
  .split("\n")
21
- .map(line => line.trimStart())
25
+ /*
26
+ This check makes sure
27
+ `
28
+ foo |
29
+ ${'bar'} |
30
+ baz
31
+ `
32
+
33
+ resolves to "foo | bar |baz" instead of "foo | bar|baz"
34
+ */
35
+ .map((line, i) => (i !== 0 || startsNewLine) ? line.trimStart() : line)
22
36
  // Remove comments both for the start of the line.
23
37
  .filter(line => !line.startsWith("//"))
24
38
  .filter(line => line.length > 0)
@@ -31,7 +45,10 @@ function taggedTemplateToArray(strings, tokens, multiline) {
31
45
  return line;
32
46
  })
33
47
  .join("");
34
- array.push(lines);
48
+ if (verbatim)
49
+ array.push(lines);
50
+ else
51
+ array.push({ rgxInterpolate: true, toRgx: () => lines });
35
52
  }
36
53
  }
37
54
  if (!isNullOrUndefined(token))
@@ -44,7 +44,7 @@ class RGXLexer {
44
44
  let dataPart = {};
45
45
  if (lexemeDefinition.type === "resolve") {
46
46
  const { token } = lexemeDefinition;
47
- const regex = new RegExp((0, resolve_1.resolveRGXToken)(token));
47
+ const regex = (0, utils_1.createRegex)((0, resolve_1.resolveRGXToken)(token));
48
48
  // Since the assertion will throw an error if there is no match, this is safe.
49
49
  const match = (0, utils_1.assertRegexMatchesAtPosition)(regex, this.source, startPosition, 10, true);
50
50
  raw = match[0];
@@ -159,7 +159,7 @@ class RGXLexer {
159
159
  skipWhitespace() {
160
160
  if (this.isAtEnd())
161
161
  return;
162
- const regex = new RegExp((0, resolve_1.resolveRGXToken)((0, constants_1.rgxConstant)("whitespace-block")));
162
+ const regex = (0, utils_1.createRegex)((0, resolve_1.resolveRGXToken)((0, constants_1.rgxConstant)("whitespace-block")));
163
163
  const match = (0, utils_1.regexMatchAtPosition)(regex, this.source, this.position, true);
164
164
  if (match !== null) {
165
165
  this.position += match[0].length;
package/dist/resolve.js CHANGED
@@ -37,9 +37,12 @@ exports.escapeRegex = escapeRegex;
37
37
  exports.resolveRGXToken = resolveRGXToken;
38
38
  const class_1 = require("./class");
39
39
  const e = __importStar(require("./errors"));
40
+ const ExtRegExp_1 = require("./ExtRegExp");
40
41
  const tg = __importStar(require("./typeGuards"));
41
42
  function escapeRegex(value) {
42
- return value.replaceAll(/[\-\^\$.*+?^${}()|[\]\\]/g, '\\$&');
43
+ const result = value.replaceAll(/[\-\^\$.*+?^${}()|[\]\\]/g, '\\$&');
44
+ tg.assertValidRegexString(result);
45
+ return result;
43
46
  }
44
47
  function localizableVanillaRegexFlagDiff(prev, next) {
45
48
  // Remove anything other than the "ims" flags from both strings, as
@@ -56,44 +59,55 @@ function localizableVanillaRegexFlagDiff(prev, next) {
56
59
  return `${added}-${removed}`;
57
60
  }
58
61
  function resolveRGXToken(token, groupWrap = true, topLevel = true, currentFlags = '') {
59
- if (tg.isRGXNoOpToken(token))
60
- return '';
61
- if (tg.isRGXLiteralToken(token)) {
62
- const localizableFlagDiff = localizableVanillaRegexFlagDiff(currentFlags, token.flags);
63
- currentFlags = token.flags;
64
- if (!localizableFlagDiff) {
65
- if (groupWrap)
66
- return '(?:' + token.source + ')';
67
- else
68
- return token.source;
62
+ (0, ExtRegExp_1.assertValidRegexFlags)(currentFlags);
63
+ const innerResolve = () => {
64
+ if (tg.isRGXNoOpToken(token))
65
+ return '';
66
+ if (tg.isRGXLiteralToken(token)) {
67
+ const localizableFlagDiff = localizableVanillaRegexFlagDiff(currentFlags, token.flags);
68
+ currentFlags = token.flags;
69
+ if (!localizableFlagDiff) {
70
+ if (groupWrap)
71
+ return '(?:' + token.source + ')';
72
+ else
73
+ return token.source;
74
+ }
75
+ else {
76
+ return `(?${localizableFlagDiff}:${token.source})`;
77
+ }
69
78
  }
70
- else {
71
- return `(?${localizableFlagDiff}:${token.source})`;
79
+ if (tg.isRGXNativeToken(token))
80
+ return escapeRegex(String(token));
81
+ if (tg.isRGXConvertibleToken(token)) {
82
+ // If it's an interpolation, we want to just return it as-is.
83
+ // This will mean the result might be an invalid regex string,
84
+ // but that's why we have a check.
85
+ if (token.rgxInterpolate)
86
+ return String(token.toRgx());
87
+ // The top-level group-wrapping preference propogates to a direct convertible token, but after that
88
+ // the preference falls back to true whenever a token doesn't explicitly specify a preference.
89
+ return resolveRGXToken(token.toRgx(), token.rgxGroupWrap ?? (topLevel ? groupWrap : true), false, currentFlags);
72
90
  }
73
- }
74
- if (tg.isRGXNativeToken(token))
75
- return escapeRegex(String(token));
76
- if (tg.isRGXConvertibleToken(token)) {
77
- // The top-level group-wrapping preference propogates to a direct convertible token, but after that
78
- // the preference falls back to true whenever a token doesn't explicitly specify a preference.
79
- return resolveRGXToken(token.toRgx(), token.rgxGroupWrap ?? (topLevel ? groupWrap : true), false, currentFlags);
80
- }
81
- // Interpret arrays as unions
82
- if (tg.isRGXArrayToken(token, false)) {
83
- if (token.length === 0)
84
- return '';
85
- if (token.length > 1) {
86
- // Remove duplicates
87
- token = [...(0, class_1.removeRgxUnionDuplicates)(...token)];
88
- // Don't preserve group wrapping preference for the recursive calls
89
- if (groupWrap)
90
- return '(?:' + token.map(t => resolveRGXToken(t, true, false, currentFlags)).join('|') + ')';
91
- else
92
- return token.map(t => resolveRGXToken(t, true, false, currentFlags)).join('|');
91
+ // Interpret arrays as unions
92
+ if (tg.isRGXArrayToken(token, false)) {
93
+ if (token.length === 0)
94
+ return '';
95
+ if (token.length > 1) {
96
+ // Remove duplicates
97
+ token = [...(0, class_1.removeRgxUnionDuplicates)(...token)];
98
+ // Don't preserve group wrapping preference for the recursive calls
99
+ if (groupWrap)
100
+ return '(?:' + token.map(t => resolveRGXToken(t, true, false, currentFlags)).join('|') + ')';
101
+ else
102
+ return token.map(t => resolveRGXToken(t, true, false, currentFlags)).join('|');
103
+ }
104
+ return resolveRGXToken(token[0], true, false, currentFlags);
93
105
  }
94
- return resolveRGXToken(token[0], true, false, currentFlags);
95
- }
96
- // Ignoring this line since it should be impossible to reach if the types are correct, but we need it to satisfy the return type
97
- /* istanbul ignore next */
98
- throw new e.RGXInvalidTokenError(`Invalid RGX token: ${token}`, null, token);
106
+ // Ignoring this line since it should be impossible to reach if the types are correct, but we need it to satisfy the return type
107
+ /* istanbul ignore next */
108
+ throw new e.RGXInvalidTokenError(`Invalid RGX token: ${token}`, null, token);
109
+ };
110
+ const result = innerResolve();
111
+ tg.assertValidRegexString(result);
112
+ return result;
99
113
  }
@@ -93,13 +93,15 @@ function assertRGXNativeToken(value) {
93
93
  }
94
94
  function isRGXConvertibleToken(value, returnCheck = true) {
95
95
  if (typeof value === 'object' && value !== null && 'toRgx' in value) {
96
- // The rgxGroupWrap, rgxIsRepeatable, and rgxIsGroup properties are optional, but if they exist they must be booleans.
96
+ // The rgxGroupWrap, interpolate, rgxIsRepeatable, and rgxIsGroup properties are optional, but if they exist they must be booleans.
97
97
  if ('rgxGroupWrap' in value && typeof value.rgxGroupWrap !== 'boolean')
98
98
  return false;
99
99
  if ('rgxIsRepeatable' in value && typeof value.rgxIsRepeatable !== 'boolean')
100
100
  return false;
101
101
  if ('rgxIsGroup' in value && typeof value.rgxIsGroup !== 'boolean')
102
102
  return false;
103
+ if ('rgxInterpolate' in value && typeof value.rgxInterpolate !== 'boolean')
104
+ return false;
103
105
  // If the rgxAcceptInsertion property exists, it must be a function that returns a string or boolean.
104
106
  if ('rgxAcceptInsertion' in value) {
105
107
  if (!(0, is_callable_1.default)(value.rgxAcceptInsertion))
@@ -219,6 +221,11 @@ function isRGXToken(value, type = null, matchLength = true) {
219
221
  function assertRGXToken(value, type = null, matchLength = true) {
220
222
  if (!isRGXToken(value, type, matchLength)) {
221
223
  const flatType = rgxTokenTypeGuardInputToFlat(type);
224
+ /*
225
+ ts-ignore
226
+ This line sometimes throws a weird error in VSCode, but it doesn't prevent a build, and I don't know how to fix it.
227
+ It's an excessively deep type error on the `value` variable, which is typed as `unknown`.
228
+ */
222
229
  throw new e.RGXInvalidTokenError("Invalid RGX token", flatType === null ? null : { type: "tokenType", values: [flatType] }, value);
223
230
  }
224
231
  }
@@ -240,13 +247,30 @@ function isValidRegexString(value) {
240
247
  new RegExp(value);
241
248
  return true;
242
249
  }
243
- catch {
244
- return false;
250
+ catch (e) {
251
+ if (e instanceof SyntaxError) {
252
+ return false;
253
+ }
254
+ // This is ignored because I don't know what kind of
255
+ // unexpected errors might happen.
256
+ /* istanbul ignore next */
257
+ throw e;
245
258
  }
246
259
  }
247
260
  function assertValidRegexString(value) {
248
261
  if (!isValidRegexString(value)) {
249
- throw new e.RGXInvalidRegexStringError("Invalid regex string", value);
262
+ try {
263
+ new RegExp(value);
264
+ }
265
+ catch (err) {
266
+ if (err instanceof SyntaxError) {
267
+ throw new e.RGXInvalidRegexStringError("Invalid regex string", value, err);
268
+ }
269
+ // This is ignored because I don't know what king of
270
+ // unexpected errors might happen.
271
+ /* istanbul ignore next */
272
+ throw err;
273
+ }
250
274
  }
251
275
  }
252
276
  function isValidVanillaRegexFlags(value) {
package/dist/types.d.ts CHANGED
@@ -12,6 +12,7 @@ export type RGXConvertibleToken = {
12
12
  readonly rgxGroupWrap?: boolean;
13
13
  readonly rgxIsGroup?: boolean;
14
14
  readonly rgxIsRepeatable?: boolean;
15
+ readonly rgxInterpolate?: boolean;
15
16
  };
16
17
  export type RGXToken = RGXNativeToken | RGXLiteralToken | RGXConvertibleToken | RGXToken[];
17
18
  export type RGXClassTokenConstructor = new (...args: unknown[]) => RGXClassToken;
@@ -49,6 +50,8 @@ export type ValidRegexFlags = Branded<string, [ValidRegexFlagsBrandSymbol]> | Va
49
50
  export declare const validIdentifierSymbol: unique symbol;
50
51
  export type ValidIdentifierBrandSymbol = typeof validIdentifierSymbol;
51
52
  export type ValidIdentifier = Branded<string, [ValidIdentifierBrandSymbol]>;
52
- export type RGXWOptions<R = unknown, S = unknown> = RGXWalkerOptions<R, S> & {
53
+ export type RGXOptions = {
53
54
  multiline?: boolean;
55
+ verbatim?: boolean;
54
56
  };
57
+ export type RGXWOptions<R = unknown, S = unknown> = RGXWalkerOptions<R, S> & RGXOptions;
@@ -0,0 +1,4 @@
1
+ import { ExtRegExp } from "../ExtRegExp";
2
+ export declare function createRegex(pattern: string, flags?: string): RegExp;
3
+ export declare function createRegex(pattern: string, flags: string, extended: false): RegExp;
4
+ export declare function createRegex(pattern: string, flags: string, extended: true): ExtRegExp;
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createRegex = createRegex;
4
+ const errors_1 = require("../errors");
5
+ const ExtRegExp_1 = require("../ExtRegExp");
6
+ const typeGuards_1 = require("../typeGuards");
7
+ function createRegex(pattern, flags = "", extended = false) {
8
+ // ExtRegExp will handle validation of flags if extended is true, otherwise we validate them here
9
+ if (!extended)
10
+ (0, typeGuards_1.assertValidVanillaRegexFlags)(flags);
11
+ try {
12
+ if (extended)
13
+ return new ExtRegExp_1.ExtRegExp(pattern, flags);
14
+ else
15
+ return new RegExp(pattern, flags);
16
+ }
17
+ catch (e) {
18
+ if (e instanceof SyntaxError) {
19
+ throw new errors_1.RGXInvalidRegexStringError("Invalid regex string", pattern, e);
20
+ }
21
+ // The invalid flags error thrown by ExtRegExp
22
+ // will be rethrown here.
23
+ throw e;
24
+ }
25
+ }
@@ -2,3 +2,4 @@ export * from "./regexMatchAtPosition";
2
2
  export * from "./regexWithFlags";
3
3
  export * from "./normalizeRegexFlags";
4
4
  export * from "./createRGXClassGuardFunction";
5
+ export * from "./createRegex";
@@ -18,3 +18,4 @@ __exportStar(require("./regexMatchAtPosition"), exports);
18
18
  __exportStar(require("./regexWithFlags"), exports);
19
19
  __exportStar(require("./normalizeRegexFlags"), exports);
20
20
  __exportStar(require("./createRGXClassGuardFunction"), exports);
21
+ __exportStar(require("./createRegex"), exports);
@@ -1,14 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.regexWithFlags = regexWithFlags;
4
- const ExtRegExp_1 = require("../ExtRegExp");
5
4
  const normalizeRegexFlags_1 = require("./normalizeRegexFlags");
6
- const ExtRegExp_2 = require("../ExtRegExp");
5
+ const createRegex_1 = require("./createRegex");
7
6
  function regexWithFlags(exp, flags, replace = false) {
8
- (0, ExtRegExp_1.assertValidRegexFlags)(flags);
9
7
  if (replace)
10
- return new ExtRegExp_2.ExtRegExp(exp.source, flags);
8
+ return (0, createRegex_1.createRegex)(exp.source, flags, true);
11
9
  const existingFlags = exp.flags;
12
10
  const newFlags = existingFlags + flags;
13
- return new ExtRegExp_2.ExtRegExp(exp.source, (0, normalizeRegexFlags_1.normalizeRegexFlags)(newFlags));
11
+ return (0, createRegex_1.createRegex)(exp.source, (0, normalizeRegexFlags_1.normalizeRegexFlags)(newFlags), true);
14
12
  }
@@ -31,7 +31,7 @@ export declare class RGXWalker<R, S = unknown> {
31
31
  constructor(source: string, tokens: RGXTokenOrPart<R, S>[], options?: RGXWalkerOptions<R, S>);
32
32
  stop(): this;
33
33
  atTokenEnd(): boolean;
34
- hasNextToken(predicate?: (token: RGXToken | RGXPart<R, S>) => boolean): boolean;
34
+ hasNextToken(predicate?: (token: RGXToken | RGXPart<R, S, unknown>) => boolean): boolean;
35
35
  atSourceEnd(): boolean;
36
36
  hasNextSource(predicate?: (rest: string) => boolean): boolean;
37
37
  lastCapture(): RGXCapture | null;
@@ -42,7 +42,7 @@ export declare class RGXWalker<R, S = unknown> {
42
42
  step(): RGXCapture | null;
43
43
  stepToToken(predicate: (token: RGXTokenOrPart<R>) => boolean): this;
44
44
  stepToPart(predicate?: (part: RGXPart<R, S, unknown>) => boolean): this;
45
- walk(): this;
45
+ walk(): R;
46
46
  clone(depth?: CloneDepth): RGXWalker<R, S>;
47
47
  }
48
48
  export declare function rgxWalker<R, S = unknown>(...args: ConstructorParameters<typeof RGXWalker<R, S>>): RGXWalker<R, S>;
@@ -88,7 +88,7 @@ class RGXWalker {
88
88
  return this.source.slice(this.sourcePosition);
89
89
  }
90
90
  capture(token, includeMatch = false) {
91
- const regex = new RegExp((0, resolve_1.resolveRGXToken)(part_1.RGXPart.check(token) ? token.token : token));
91
+ const regex = (0, utils_1.createRegex)((0, resolve_1.resolveRGXToken)(part_1.RGXPart.check(token) ? token.token : token));
92
92
  const match = (0, utils_1.assertRegexMatchesAtPosition)(regex, this.source, this.sourcePosition, 10, true);
93
93
  this.sourcePosition += match[0].length;
94
94
  return includeMatch ? match : match[0];
@@ -108,7 +108,7 @@ class RGXWalker {
108
108
  let silent = false;
109
109
  // Ask Part what to do — control flow via return values, not flags.
110
110
  if (isPart) {
111
- const control = token.beforeCapture?.(this.share, token, this);
111
+ const control = token.beforeCapture?.({ part: token, walker: this });
112
112
  if (control === "stop") {
113
113
  this._stopped = true;
114
114
  return null;
@@ -134,7 +134,7 @@ class RGXWalker {
134
134
  }
135
135
  catch (e) {
136
136
  if (isPart && e instanceof errors_1.RGXRegexNotMatchedAtPositionError) {
137
- token.afterFailure?.(e, this.share, token, this);
137
+ token.afterFailure?.(e, { part: token, walker: this });
138
138
  }
139
139
  throw e;
140
140
  }
@@ -159,11 +159,11 @@ class RGXWalker {
159
159
  // Validate the part. If validation fails, it will throw an error, so nothing below will run.
160
160
  if (isPart) {
161
161
  try {
162
- token.validate(captureResult, this.share, this);
162
+ token.validate(captureResult, { part: token, walker: this });
163
163
  }
164
164
  catch (e) {
165
165
  if (e instanceof errors_1.RGXPartValidationFailedError) {
166
- token.afterValidationFailure?.(e, this.share, token, this);
166
+ token.afterValidationFailure?.(e, { part: token, walker: this });
167
167
  }
168
168
  throw e;
169
169
  }
@@ -179,7 +179,7 @@ class RGXWalker {
179
179
  }
180
180
  // Notify Part after capture
181
181
  if (isPart) {
182
- token.afterCapture?.(captureResult, this.share, token, this);
182
+ token.afterCapture?.(captureResult, { part: token, walker: this });
183
183
  }
184
184
  if (!this.infinite || this.tokenPosition < this.tokens.length - 1) {
185
185
  this.tokenPosition++;
@@ -213,7 +213,8 @@ class RGXWalker {
213
213
  return this;
214
214
  }
215
215
  walk() {
216
- return this.stepToToken(() => false);
216
+ this.stepToToken(() => false);
217
+ return this.reduced;
217
218
  }
218
219
  // Clone method
219
220
  clone(depth = "max") {
@@ -12,15 +12,19 @@ export type RGXCapture<T = unknown> = {
12
12
  branch: number;
13
13
  groups: Record<string, string> | null;
14
14
  };
15
+ export type RGXPartContext<R, S = unknown, T = string> = {
16
+ part: RGXPart<R, S, T>;
17
+ walker: RGXWalker<R, S>;
18
+ };
15
19
  export type RGXPartOptions<R, S = unknown, T = string> = {
16
20
  id: string;
17
21
  rawTransform: (captured: string) => string;
18
22
  transform: (captured: string) => T;
19
- validate: (captured: RGXCapture<T>, share: S, part: RGXPart<R, S, T>, walker: RGXWalker<R, S>) => boolean | string;
20
- beforeCapture: ((share: S, part: RGXPart<R, S, T>, walker: RGXWalker<R, S>) => RGXPartControl) | null;
21
- afterCapture: ((capture: RGXCapture<T>, share: S, part: RGXPart<R, S, T>, walker: RGXWalker<R, S>) => void) | null;
22
- afterFailure: ((e: RGXRegexNotMatchedAtPositionError, share: S, part: RGXPart<R, S, T>, walker: RGXWalker<R, S>) => void) | null;
23
- afterValidationFailure: ((e: RGXPartValidationFailedError, share: S, part: RGXPart<R, S, T>, walker: RGXWalker<R, S>) => void) | null;
23
+ validate: (captured: RGXCapture<T>, context: RGXPartContext<R, S, T>) => boolean | string;
24
+ beforeCapture: ((context: RGXPartContext<R, S, T>) => RGXPartControl) | null;
25
+ afterCapture: ((capture: RGXCapture<T>, context: RGXPartContext<R, S, T>) => void) | null;
26
+ afterFailure: ((e: RGXRegexNotMatchedAtPositionError, context: RGXPartContext<R, S, T>) => void) | null;
27
+ afterValidationFailure: ((e: RGXPartValidationFailedError, context: RGXPartContext<R, S, T>) => void) | null;
24
28
  };
25
29
  export declare class RGXPart<R, S = unknown, T = string> {
26
30
  id: string | null;
@@ -38,7 +42,7 @@ export declare class RGXPart<R, S = unknown, T = string> {
38
42
  hasId(): this is RGXPart<R, S, T> & {
39
43
  id: string;
40
44
  };
41
- validate(capture: RGXCapture<T>, share: S, walker: RGXWalker<R, S>): void;
45
+ validate(capture: RGXCapture<T>, context: RGXPartContext<R, S, T>): void;
42
46
  clone(depth?: CloneDepth): RGXPart<R, S, T>;
43
47
  }
44
48
  export declare function rgxPart<R, S = unknown, T = string>(...args: ConstructorParameters<typeof RGXPart<R, S, T>>): RGXPart<R, S, T>;
@@ -21,8 +21,8 @@ class RGXPart {
21
21
  hasId() {
22
22
  return this.id !== null;
23
23
  }
24
- validate(capture, share, walker) {
25
- const result = this._validate(capture, share, this, walker);
24
+ validate(capture, context) {
25
+ const result = this._validate(capture, context);
26
26
  if (result === true)
27
27
  return;
28
28
  const message = typeof result === "string" ? result : "Part Validation Failed";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ptolemy2002/rgx",
3
- "version": "9.0.0",
3
+ "version": "11.0.0",
4
4
  "private": false,
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",