Skip to content
This repository was archived by the owner on Oct 11, 2021. It is now read-only.

Commit d6bbe84

Browse files
Split util into different parts
1 parent 8b53e6a commit d6bbe84

31 files changed

+1867
-1853
lines changed

lib/ast-util.ts

+1,643
Large diffs are not rendered by default.

lib/char-util.ts

+114
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import { CharSet, JS } from "refa";
2+
import { CharacterClass, CharacterClassElement, CharacterSet, Flags } from "regexpp/ast";
3+
import { Simple, assertNever } from "./util";
4+
5+
export function toCharSet(
6+
elements: (Simple<CharacterClassElement> | Simple<CharacterSet>)[] | CharacterClass,
7+
flags: Partial<Flags>
8+
): CharSet {
9+
if (Array.isArray(elements)) {
10+
return JS.createCharSet(
11+
elements.map(e => {
12+
switch (e.type) {
13+
case "Character":
14+
return e.value;
15+
case "CharacterClassRange":
16+
return { min: e.min.value, max: e.max.value };
17+
case "CharacterSet":
18+
return e;
19+
default:
20+
throw assertNever(e);
21+
}
22+
}),
23+
flags
24+
);
25+
} else {
26+
const chars = toCharSet(elements.elements, flags);
27+
if (elements.negate) {
28+
return chars.negate();
29+
}
30+
return chars;
31+
}
32+
}
33+
34+
const EMPTY_UTF16_CHARSET = CharSet.empty(0xffff);
35+
const EMPTY_UNICODE_CHARSET = CharSet.empty(0x10ffff);
36+
/**
37+
* Returns an empty character set for the given flags.
38+
*/
39+
export function emptyCharSet(flags: Partial<Flags>): CharSet {
40+
if (flags.unicode) {
41+
return EMPTY_UNICODE_CHARSET;
42+
} else {
43+
return EMPTY_UTF16_CHARSET;
44+
}
45+
}
46+
const ALL_UTF16_CHARSET = CharSet.all(0xffff);
47+
const ALL_UNICODE_CHARSET = CharSet.all(0x10ffff);
48+
/**
49+
* Returns a full character set for the given flags.
50+
*/
51+
export function allCharSet(flags: Partial<Flags>): CharSet {
52+
if (flags.unicode) {
53+
return ALL_UNICODE_CHARSET;
54+
} else {
55+
return ALL_UTF16_CHARSET;
56+
}
57+
}
58+
const LINE_TERMINATOR_UTF16_CHARSET = JS.createCharSet([{ kind: "any" }], { unicode: false }).negate();
59+
const LINE_TERMINATOR_UNICODE_CHARSET = JS.createCharSet([{ kind: "any" }], { unicode: true }).negate();
60+
export function lineTerminatorCharSet(flags: Partial<Flags>): CharSet {
61+
if (flags.unicode) {
62+
return LINE_TERMINATOR_UNICODE_CHARSET;
63+
} else {
64+
return LINE_TERMINATOR_UTF16_CHARSET;
65+
}
66+
}
67+
68+
/**
69+
* Returns whether the given character class/set matches all characters.
70+
*/
71+
export function isMatchAll(char: CharacterClass | CharacterSet, flags: Partial<Flags>): boolean {
72+
if (char.type === "CharacterSet") {
73+
if (char.kind === "property") {
74+
return JS.createCharSet([char], flags).isAll;
75+
} else if (char.kind === "any") {
76+
return !!flags.dotAll;
77+
} else {
78+
return false;
79+
}
80+
} else {
81+
if (char.negate && char.elements.length === 0) {
82+
return true;
83+
} else {
84+
if (char.negate) {
85+
return toCharSet(char.elements, flags).isEmpty;
86+
} else {
87+
return toCharSet(char.elements, flags).isAll;
88+
}
89+
}
90+
}
91+
}
92+
93+
/**
94+
* Returns whether the given character class/set matches no characters.
95+
*/
96+
export function isMatchNone(char: CharacterClass | CharacterSet, flags: Partial<Flags>): boolean {
97+
if (char.type === "CharacterSet") {
98+
if (char.kind === "property") {
99+
return JS.createCharSet([char], flags).isEmpty;
100+
} else {
101+
return false;
102+
}
103+
} else {
104+
if (!char.negate && char.elements.length === 0) {
105+
return true;
106+
} else {
107+
if (char.negate) {
108+
return toCharSet(char.elements, flags).isAll;
109+
} else {
110+
return toCharSet(char.elements, flags).isEmpty;
111+
}
112+
}
113+
}
114+
}

lib/fa-util.ts

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { CharSet, DFA, NFA, ReadonlyNFA } from "refa";
2+
3+
/**
4+
* Returns whether the languages of the given NFA are equal.
5+
*
6+
* This assumes that both NFA do not have unreachable or trap states.
7+
*/
8+
export function nfaEquals(a: ReadonlyNFA, b: ReadonlyNFA): boolean {
9+
if (a.isEmpty || b.isEmpty) {
10+
return a.isEmpty === b.isEmpty;
11+
}
12+
if (a.options.maxCharacter !== b.options.maxCharacter) {
13+
return false;
14+
}
15+
16+
const { maxCharacter } = a.options;
17+
const x = a.nodes;
18+
const y = b.nodes;
19+
20+
if (x.finals.has(x.initial) !== y.finals.has(y.initial)) {
21+
// one accepts the empty word, the other one doesn't
22+
return false;
23+
}
24+
25+
function unionAll(iter: Iterable<CharSet>): CharSet {
26+
let total: CharSet | undefined = undefined;
27+
28+
for (const item of iter) {
29+
if (total === undefined) {
30+
total = item;
31+
} else {
32+
total = total.union(item);
33+
}
34+
}
35+
36+
return total || CharSet.empty(maxCharacter);
37+
}
38+
39+
if (!unionAll(x.initial.out.values()).equals(unionAll(y.initial.out.values()))) {
40+
// first characters of the accepted languages are different
41+
return false;
42+
}
43+
44+
const aDfa = DFA.fromFA(a);
45+
aDfa.minimize();
46+
const bDfa = DFA.fromFA(b);
47+
bDfa.minimize();
48+
49+
return aDfa.structurallyEqual(bDfa);
50+
}
51+
export function nfaIsSupersetOf(superset: ReadonlyNFA, subset: ReadonlyNFA): boolean {
52+
const union = superset.copy();
53+
union.union(subset);
54+
return nfaEquals(union, superset);
55+
}
56+
export function nfaIsSubsetOf(subset: ReadonlyNFA, superset: ReadonlyNFA): boolean {
57+
return nfaIsSupersetOf(superset, subset);
58+
}
59+
export function nfaUnionAll(nfas: Iterable<ReadonlyNFA>, options: Readonly<NFA.Options>): NFA {
60+
const total = NFA.empty(options);
61+
for (const nfa of nfas) {
62+
total.union(nfa);
63+
}
64+
return total;
65+
}

lib/format.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { FiniteAutomaton, JS } from "refa";
22
import { CharacterClassElement, Node } from "regexpp/ast";
3-
import { assertNever, minimalHexEscape as hexEscape } from "./util";
3+
import { assertNever } from "./util";
4+
import { minimalHexEscape as hexEscape } from "./ast-util";
45

56
interface Literal {
67
readonly source: string;

lib/rules/confusing-quantifier.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { mention } from "../format";
22
import { createRuleListener, getDocUrl, CleanRegexRule } from "../rules-util";
3-
import { isPotentiallyEmpty, quantToString } from "../util";
3+
import { isPotentiallyEmpty, quantToString } from "../ast-util";
44

55
export default {
66
meta: {

lib/rules/consistent-match-all-characters.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { AnyCharacterSet, CharacterClass, Flags, Node } from "regexpp/ast";
22
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
33
import { many, mention } from "../format";
4-
import { assertNever, isMatchAll } from "../util";
4+
import { assertNever } from "../util";
5+
import { isMatchAll } from "../char-util";
56

67
function removeDescendantNodes<T extends { node: Node }>(nodes: (T & { node: Node })[]): T[] {
78
// this is a O(n^2) implementation

lib/rules/disjoint-alternatives.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ import { JS, NFA, ReadonlyNFA } from "refa";
22
import { Alternative, CapturingGroup, Group, LookaroundAssertion, Node, Pattern } from "regexpp/ast";
33
import { mention, toRegExpString } from "../format";
44
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
5-
import { hasSomeDescendant, nfaEquals, nfaIsSupersetOf, nfaUnionAll, underAStar } from "../util";
5+
import { hasSomeDescendant, underAStar } from "../ast-util";
6+
import { nfaEquals, nfaIsSupersetOf, nfaUnionAll } from "../fa-util";
67

78
export default {
89
meta: {

lib/rules/identity-escape.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { RegExpParser } from "regexpp";
22
import { Character, CharacterClass, CharacterSet, Flags } from "regexpp/ast";
33
import { mentionCharElement } from "../format";
44
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
5-
import { toCharSet } from "../util";
5+
import { toCharSet } from "../char-util";
66

77
type LiteralEscapeMode = "require" | "disallow" | "allow";
88
type LiteralContext = "inside" | "outside" | "all";

lib/rules/no-constant-capturing-group.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { mention } from "../format";
22
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
3-
import { getConstant } from "../util";
3+
import { getConstant } from "../ast-util";
44

55
export default {
66
meta: {

lib/rules/no-empty-backreference.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
2-
import { isEmptyBackreference, isZeroLength } from "../util";
2+
import { isEmptyBackreference, isZeroLength } from "../ast-util";
33

44
export default {
55
meta: {

lib/rules/no-empty-lookaround.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
22
import { mention } from "../format";
3-
import { isPotentiallyEmpty } from "../util";
3+
import { isPotentiallyEmpty } from "../ast-util";
44

55
export default {
66
meta: {

lib/rules/no-obscure-range.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { CharRange } from "refa";
22
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
3-
import { isControlEscapeSequence, isHexadecimalEscapeSequence, isOctalEscapeSequence } from "../util";
3+
import { isControlEscapeSequence, isHexadecimalEscapeSequence, isOctalEscapeSequence } from "../ast-util";
44

55
const allowedRanges: readonly CharRange[] = [
66
// digits 0-9

lib/rules/no-octal-escape.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
2-
import { isOctalEscapeSequence } from "../util";
2+
import { isOctalEscapeSequence } from "../ast-util";
33

44
export default {
55
meta: {

lib/rules/no-optional-assertion.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
22
import { mention } from "../format";
33
import { Assertion, CapturingGroup, Group, Node, Quantifier } from "regexpp/ast";
4-
import { assertNever, isZeroLength } from "../util";
4+
import { assertNever } from "../util";
5+
import { isZeroLength } from "../ast-util";
56

67
/**
78
* Returns the closest ascendant quantifier with a minimum of 0.

lib/rules/no-potentially-empty-backreference.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
2-
import { backreferenceAlwaysAfterGroup, isEmptyBackreference } from "../util";
2+
import { backreferenceAlwaysAfterGroup, isEmptyBackreference } from "../ast-util";
33

44
export default {
55
meta: {

lib/rules/no-trivially-nested-quantifier.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Quantifier, Node } from "regexpp/ast";
22
import { mention, shorten } from "../format";
33
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
4-
import { hasSomeAncestor, quantToString, Quant } from "../util";
4+
import { hasSomeAncestor, quantToString, Quant } from "../ast-util";
55

66
function getCombinedQuant(node: Quantifier, nested: Quantifier): Quant | null {
77
if (node.max === 0 || nested.max === 0) {

lib/rules/no-unnecessary-assertions.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,15 @@ import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
22
import { mention } from "../format";
33
import { JS } from "refa";
44
import {
5-
assertNever,
65
getFirstCharAfter,
76
getFirstCharConsumedBy,
87
getLengthRange,
98
hasSomeDescendant,
109
isPotentiallyEmpty,
1110
assertionKindToMatchingDirection,
12-
} from "../util";
11+
} from "../ast-util";
1312
import { EdgeAssertion, LookaroundAssertion, WordBoundaryAssertion } from "regexpp/ast";
13+
import { assertNever } from "../util";
1414

1515
export default {
1616
meta: {

lib/rules/no-unnecessary-character-class.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
2-
import { areEqual, isEscapeSequence, isOctalEscapeSequence, negateCharacterSetRaw } from "../util";
2+
import { areEqual, isEscapeSequence, isOctalEscapeSequence, negateCharacterSetRaw } from "../ast-util";
33

44
const SPECIAL_CHARACTERS = new Set(".*+?|()[]{}^$/");
55

lib/rules/no-unnecessary-group.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Group, QuantifiableElement } from "regexpp/ast";
22
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
3-
import { areEqual } from "../util";
3+
import { areEqual } from "../ast-util";
44

55
/**
66
* Returns whether the given group is the top-level group of its pattern.

lib/rules/no-unnecessary-lazy.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { Quantifier } from "regexpp/ast";
22
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
3-
import { getFirstCharAfter, getFirstCharConsumedBy, getQuantifierRaw, matchingDirection } from "../util";
3+
import { getFirstCharAfter, getFirstCharConsumedBy, getQuantifierRaw, matchingDirection } from "../ast-util";
44

55
function withoutLazy(node: Quantifier): string {
66
let raw = getQuantifierRaw(node);

lib/rules/no-unnecessary-quantifier.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
2-
import { isEmpty, isPotentiallyEmpty, isZeroLength } from "../util";
2+
import { isEmpty, isPotentiallyEmpty, isZeroLength } from "../ast-util";
33

44
export default {
55
meta: {

lib/rules/no-zero-quantifier.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
2-
import { hasSomeDescendant } from "../util";
2+
import { hasSomeDescendant } from "../ast-util";
33

44
export default {
55
meta: {

lib/rules/optimal-concatenation-quantifier.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ import {
1111
} from "regexpp/ast";
1212
import { mention, shorten } from "../format";
1313
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
14-
import { emptyCharSet, hasCapturingGroup, hasSomeDescendant, Quant, quantAdd, quantToString, toCharSet } from "../util";
14+
import { hasCapturingGroup, hasSomeDescendant, Quant, quantAdd, quantToString } from "../ast-util";
15+
import { emptyCharSet, toCharSet } from "../char-util";
1516

1617
interface Chars {
1718
readonly chars: CharSet;

lib/rules/optimized-character-class.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { CharacterClassElement, CharacterClassRange } from "regexpp/ast";
22
import { mentionCharElement } from "../format";
33
import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
4-
import { emptyCharSet, toCharSet } from "../util";
4+
import { emptyCharSet, toCharSet } from "../char-util";
55

66
export default {
77
meta: {

lib/rules/prefer-character-class.ts

+3-5
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,12 @@ import { CleanRegexRule, createRuleListener, getDocUrl } from "../rules-util";
33
import { mention } from "../format";
44
import {
55
elementsToCharacterClass,
6-
findIndex,
7-
findLastIndex,
86
FirstConsumedChar,
97
getFirstCharConsumedBy,
108
getParentPrefixAndSuffix,
119
matchingDirection,
12-
Simple,
13-
toCharSet,
1410
underAStar,
15-
} from "../util";
11+
} from "../ast-util";
1612
import {
1713
Alternative,
1814
Assertion,
@@ -24,6 +20,8 @@ import {
2420
Pattern,
2521
} from "regexpp/ast";
2622
import { CharSet } from "refa";
23+
import { findIndex, findLastIndex, Simple } from "../util";
24+
import { toCharSet } from "../char-util";
2725

2826
type CharElementArray = Readonly<Simple<CharacterClassElement>>[];
2927
interface CharacterAlternative {

0 commit comments

Comments
 (0)