1
1
import type { EncodeResult } from '../encoder/types' ;
2
2
import type { RegexConstruct } from '../types' ;
3
3
4
- export interface CharacterClass extends RegexConstruct {
5
- type : 'characterClass' ;
6
- escape ?: string ;
7
- chars : string [ ] ;
8
- ranges : CharacterRange [ ] ;
9
- isNegated : boolean ;
10
- encode : ( ) => EncodeResult ;
11
- }
12
-
13
4
/**
14
5
* Character range from start to end (inclusive).
15
6
*/
@@ -18,6 +9,14 @@ export interface CharacterRange {
18
9
end : string ;
19
10
}
20
11
12
+ export interface CharacterClass extends RegexConstruct {
13
+ type : 'characterClass' ;
14
+ escape ?: string ;
15
+ chars ?: string [ ] ;
16
+ ranges ?: CharacterRange [ ] ;
17
+ isNegated ?: boolean ;
18
+ }
19
+
21
20
/**
22
21
* Matches any single character.
23
22
* Cannot be used as a part of character class.
@@ -30,54 +29,36 @@ export const any: EncodeResult = {
30
29
export const digit : CharacterClass = {
31
30
type : 'characterClass' ,
32
31
escape : '\\d' ,
33
- chars : [ ] ,
34
- ranges : [ ] ,
35
- isNegated : false ,
36
32
encode : encodeCharacterClass ,
37
33
} ;
38
34
39
35
export const nonDigit : CharacterClass = {
40
36
type : 'characterClass' ,
41
37
escape : '\\D' ,
42
- chars : [ ] ,
43
- ranges : [ ] ,
44
- isNegated : false ,
45
38
encode : encodeCharacterClass ,
46
39
} ;
47
40
48
41
export const word : CharacterClass = {
49
42
type : 'characterClass' ,
50
43
escape : '\\w' ,
51
- chars : [ ] ,
52
- ranges : [ ] ,
53
- isNegated : false ,
54
44
encode : encodeCharacterClass ,
55
45
} ;
56
46
57
47
export const nonWord : CharacterClass = {
58
48
type : 'characterClass' ,
59
49
escape : '\\W' ,
60
- chars : [ ] ,
61
- ranges : [ ] ,
62
- isNegated : false ,
63
50
encode : encodeCharacterClass ,
64
51
} ;
65
52
66
53
export const whitespace : CharacterClass = {
67
54
type : 'characterClass' ,
68
55
escape : '\\s' ,
69
- chars : [ ] ,
70
- ranges : [ ] ,
71
- isNegated : false ,
72
56
encode : encodeCharacterClass ,
73
57
} ;
74
58
75
59
export const nonWhitespace : CharacterClass = {
76
60
type : 'characterClass' ,
77
61
escape : '\\S' ,
78
- chars : [ ] ,
79
- ranges : [ ] ,
80
- isNegated : false ,
81
62
encode : encodeCharacterClass ,
82
63
} ;
83
64
@@ -107,9 +88,8 @@ export function charClass(...elements: CharacterClass[]): CharacterClass {
107
88
108
89
return {
109
90
type : 'characterClass' ,
110
- chars : elements . map ( ( c ) => getAllChars ( c ) ) . flat ( ) ,
111
- ranges : elements . map ( ( c ) => c . ranges ) . flat ( ) ,
112
- isNegated : false ,
91
+ chars : elements . map ( ( c ) => getAllChars ( c ) ?? [ ] ) . flat ( ) ,
92
+ ranges : elements . map ( ( c ) => c . ranges ?? [ ] ) . flat ( ) ,
113
93
encode : encodeCharacterClass ,
114
94
} ;
115
95
}
@@ -129,9 +109,7 @@ export function charRange(start: string, end: string): CharacterClass {
129
109
130
110
return {
131
111
type : 'characterClass' ,
132
- chars : [ ] ,
133
112
ranges : [ { start, end } ] ,
134
- isNegated : false ,
135
113
encode : encodeCharacterClass ,
136
114
} ;
137
115
}
@@ -146,15 +124,14 @@ export function anyOf(characters: string): CharacterClass {
146
124
return {
147
125
type : 'characterClass' ,
148
126
chars,
149
- ranges : [ ] ,
150
- isNegated : false ,
151
127
encode : encodeCharacterClass ,
152
128
} ;
153
129
}
154
130
155
131
export function negated ( element : CharacterClass ) : CharacterClass {
156
132
return {
157
133
type : 'characterClass' ,
134
+ escape : element . escape ,
158
135
chars : element . chars ,
159
136
ranges : element . ranges ,
160
137
isNegated : ! element . isNegated ,
@@ -168,27 +145,27 @@ export function negated(element: CharacterClass): CharacterClass {
168
145
export const inverted = negated ;
169
146
170
147
function encodeCharacterClass ( this : CharacterClass ) : EncodeResult {
171
- if ( this . escape === undefined && this . chars . length === 0 && this . ranges . length === 0 ) {
148
+ if ( this . escape === undefined && ! this . chars ? .length && ! this . ranges ? .length ) {
172
149
throw new Error ( 'Character class should contain at least one character or character range' ) ;
173
150
}
174
151
175
- // Direct rendering for single-character class
176
- if ( this . escape !== undefined && ! this . chars . length && ! this . ranges . length && ! this . isNegated ) {
152
+ // Direct rendering for escapes
153
+ if ( this . escape !== undefined && ! this . chars ? .length && ! this . ranges ? .length && ! this . isNegated ) {
177
154
return {
178
155
precedence : 'atom' ,
179
156
pattern : this . escape ,
180
157
} ;
181
158
}
182
159
183
- const allChars = getAllChars ( this ) ;
160
+ const allChars = getAllChars ( this ) ?? [ ] ;
184
161
185
162
// If passed characters includes hyphen (`-`) it need to be moved to
186
163
// first (or last) place in order to treat it as hyphen character and not a range.
187
164
// See: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_expressions/Character_classes#types
188
165
const hyphen = allChars . includes ( '-' ) ? '-' : '' ;
189
166
const caret = allChars . includes ( '^' ) ? '^' : '' ;
190
167
const otherChars = allChars . filter ( ( c ) => c !== '-' && c !== '^' ) . join ( '' ) ;
191
- const ranges = this . ranges . map ( ( { start, end } ) => `${ start } -${ end } ` ) . join ( '' ) ;
168
+ const ranges = this . ranges ? .map ( ( { start, end } ) => `${ start } -${ end } ` ) . join ( '' ) ?? '' ;
192
169
const negation = this . isNegated ? '^' : '' ;
193
170
194
171
let pattern = `[${ negation } ${ ranges } ${ otherChars } ${ caret } ${ hyphen } ]` ;
@@ -209,5 +186,5 @@ function getAllChars(characterClass: CharacterClass) {
209
186
return characterClass . chars ;
210
187
}
211
188
212
- return [ characterClass . escape , ...characterClass . chars ] ;
189
+ return [ characterClass . escape , ...( characterClass . chars ?? [ ] ) ] ;
213
190
}
0 commit comments