1
1
/*
2
- * Copyright (c) 2018, 2022 , Oracle and/or its affiliates. All rights reserved.
2
+ * Copyright (c) 2018, 2024 , Oracle and/or its affiliates. All rights reserved.
3
3
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4
4
*
5
5
* The Universal Permissive License (UPL), Version 1.0
50
50
import com .oracle .truffle .regex .tregex .util .json .Json ;
51
51
import com .oracle .truffle .regex .tregex .util .json .JsonConvertible ;
52
52
import com .oracle .truffle .regex .tregex .util .json .JsonValue ;
53
+ import com .oracle .truffle .regex .util .TBitSet ;
53
54
import com .oracle .truffle .regex .util .TruffleReadOnlyKeysArray ;
54
55
55
56
@ ExportLibrary (InteropLibrary .class )
@@ -76,6 +77,9 @@ public final class RegexFlags extends AbstractConstantKeysObject implements Json
76
77
PROP_HAS_INDICES ,
77
78
PROP_UNICODE_SETS );
78
79
80
+ private static final TBitSet ALL_FLAG_CHARS = TBitSet .valueOf ('d' , 'g' , 'i' , 'm' , 's' , 'u' , 'v' , 'y' );
81
+ private static final TBitSet LOCAL_FLAG_CHARS = TBitSet .valueOf ('i' , 'm' , 's' );
82
+
79
83
private static final int NONE = 0 ;
80
84
private static final int IGNORE_CASE = 1 ;
81
85
private static final int MULTILINE = 1 << 1 ;
@@ -86,6 +90,10 @@ public final class RegexFlags extends AbstractConstantKeysObject implements Json
86
90
private static final int HAS_INDICES = 1 << 6 ;
87
91
private static final int UNICODE_SETS = 1 << 7 ;
88
92
93
+ private static final int [] FLAG_LOOKUP = {
94
+ HAS_INDICES , 0 , 0 , GLOBAL , 0 , IGNORE_CASE , 0 , 0 , 0 , MULTILINE , 0 , 0 , 0 , 0 , 0 , DOT_ALL , 0 , UNICODE , UNICODE_SETS , 0 , 0 , STICKY
95
+ };
96
+
89
97
public static final RegexFlags DEFAULT = new RegexFlags ("" , NONE );
90
98
91
99
private final String source ;
@@ -96,6 +104,17 @@ private RegexFlags(String source, int value) {
96
104
this .value = value ;
97
105
}
98
106
107
+ private RegexFlags (int value ) {
108
+ this .source = generateSource (value );
109
+ this .value = value ;
110
+ }
111
+
112
+ private static int maskForFlag (char flagChar ) {
113
+ assert ALL_FLAG_CHARS .get (flagChar );
114
+ // flagChar must be one of [d-y].
115
+ return FLAG_LOOKUP [flagChar - 'd' ];
116
+ }
117
+
99
118
public static Builder builder () {
100
119
return new Builder ();
101
120
}
@@ -109,51 +128,21 @@ public static RegexFlags parseFlags(RegexSource source) throws RegexSyntaxExcept
109
128
int flags = NONE ;
110
129
for (int i = 0 ; i < flagsStr .length (); i ++) {
111
130
char ch = flagsStr .charAt (i );
112
- switch (ch ) {
113
- case 'i' :
114
- flags = addFlag (source , flags , i , IGNORE_CASE );
115
- break ;
116
- case 'm' :
117
- flags = addFlag (source , flags , i , MULTILINE );
118
- break ;
119
- case 'g' :
120
- flags = addFlag (source , flags , i , GLOBAL );
121
- break ;
122
- case 'y' :
123
- flags = addFlag (source , flags , i , STICKY );
124
- break ;
125
- case 'u' :
126
- if ((flags & UNICODE_SETS ) != 0 ) {
127
- throw RegexSyntaxException .createFlags (source , JsErrorMessages .BOTH_FLAGS_SET_U_V , i );
128
- }
129
- flags = addFlag (source , flags , i , UNICODE );
130
- break ;
131
- case 's' :
132
- flags = addFlag (source , flags , i , DOT_ALL );
133
- break ;
134
- case 'd' :
135
- flags = addFlag (source , flags , i , HAS_INDICES );
136
- break ;
137
- case 'v' :
138
- if ((flags & UNICODE ) != 0 ) {
139
- throw RegexSyntaxException .createFlags (source , JsErrorMessages .BOTH_FLAGS_SET_U_V , i );
140
- }
141
- flags = addFlag (source , flags , i , UNICODE_SETS );
142
- break ;
143
- default :
144
- throw RegexSyntaxException .createFlags (source , JsErrorMessages .UNSUPPORTED_FLAG , i );
131
+ if (!isValidFlagChar (ch )) {
132
+ throw RegexSyntaxException .createFlags (source , JsErrorMessages .UNSUPPORTED_FLAG , i );
133
+ }
134
+ int flag = maskForFlag (ch );
135
+ if ((flags & flag ) != 0 ) {
136
+ throw RegexSyntaxException .createFlags (source , JsErrorMessages .REPEATED_FLAG , i );
137
+ }
138
+ flags |= flag ;
139
+ if ((flags & (UNICODE | UNICODE_SETS )) == (UNICODE | UNICODE_SETS )) {
140
+ throw RegexSyntaxException .createFlags (source , JsErrorMessages .BOTH_FLAGS_SET_U_V , i );
145
141
}
146
142
}
147
143
return new RegexFlags (flagsStr , flags );
148
144
}
149
145
150
- private static int addFlag (RegexSource source , int flags , int i , int flag ) {
151
- if ((flags & flag ) != 0 ) {
152
- throw RegexSyntaxException .createFlags (source , JsErrorMessages .REPEATED_FLAG , i );
153
- }
154
- return flags | flag ;
155
- }
156
-
157
146
public String getSource () {
158
147
return source ;
159
148
}
@@ -202,6 +191,34 @@ private boolean isSet(int flag) {
202
191
return (value & flag ) != NONE ;
203
192
}
204
193
194
+ public static boolean isValidFlagChar (char candidateChar ) {
195
+ return ALL_FLAG_CHARS .get (candidateChar );
196
+ }
197
+
198
+ public static boolean isValidLocalFlagChar (char candidateChar ) {
199
+ return LOCAL_FLAG_CHARS .get (candidateChar );
200
+ }
201
+
202
+ public RegexFlags addNewFlagModifier (RegexSource regexSource , char flagChar ) {
203
+ int flag = maskForFlag (flagChar );
204
+ if (isSet (flag )) {
205
+ throw RegexSyntaxException .createFlags (regexSource , JsErrorMessages .REPEATED_FLAG_IN_MODIFIER );
206
+ }
207
+ return new RegexFlags (this .value | flag );
208
+ }
209
+
210
+ public RegexFlags addFlags (RegexFlags otherFlags ) {
211
+ return new RegexFlags (this .value | otherFlags .value );
212
+ }
213
+
214
+ public RegexFlags delFlags (RegexFlags otherFlags ) {
215
+ return new RegexFlags (this .value & ~otherFlags .value );
216
+ }
217
+
218
+ public boolean overlaps (RegexFlags otherFlags ) {
219
+ return (this .value & otherFlags .value ) != 0 ;
220
+ }
221
+
205
222
@ Override
206
223
public String toString () {
207
224
return source ;
@@ -287,6 +304,35 @@ public Object toDisplayString(@SuppressWarnings("unused") boolean allowSideEffec
287
304
return "TRegexJSFlags{flags=" + toString () + '}' ;
288
305
}
289
306
307
+ private static String generateSource (int value ) {
308
+ StringBuilder sb = new StringBuilder (8 );
309
+ if ((value & IGNORE_CASE ) != 0 ) {
310
+ sb .append ("i" );
311
+ }
312
+ if ((value & MULTILINE ) != 0 ) {
313
+ sb .append ("m" );
314
+ }
315
+ if ((value & STICKY ) != 0 ) {
316
+ sb .append ("y" );
317
+ }
318
+ if ((value & GLOBAL ) != 0 ) {
319
+ sb .append ("g" );
320
+ }
321
+ if ((value & UNICODE ) != 0 ) {
322
+ sb .append ("u" );
323
+ }
324
+ if ((value & DOT_ALL ) != 0 ) {
325
+ sb .append ("s" );
326
+ }
327
+ if ((value & HAS_INDICES ) != 0 ) {
328
+ sb .append ("d" );
329
+ }
330
+ if ((value & UNICODE_SETS ) != 0 ) {
331
+ sb .append ("v" );
332
+ }
333
+ return sb .toString ();
334
+ }
335
+
290
336
public static final class Builder {
291
337
292
338
private int value ;
@@ -342,7 +388,7 @@ public Builder unicodeSets(boolean enabled) {
342
388
343
389
@ TruffleBoundary
344
390
public RegexFlags build () {
345
- return new RegexFlags (generateSource (), this .value );
391
+ return new RegexFlags (generateSource (this . value ), this .value );
346
392
}
347
393
348
394
private void updateFlag (boolean enabled , int bitMask ) {
@@ -352,38 +398,5 @@ private void updateFlag(boolean enabled, int bitMask) {
352
398
this .value &= ~bitMask ;
353
399
}
354
400
}
355
-
356
- private boolean isSet (int flag ) {
357
- return (value & flag ) != NONE ;
358
- }
359
-
360
- private String generateSource () {
361
- StringBuilder sb = new StringBuilder (7 );
362
- if (isSet (IGNORE_CASE )) {
363
- sb .append ("i" );
364
- }
365
- if (isSet (MULTILINE )) {
366
- sb .append ("m" );
367
- }
368
- if (isSet (STICKY )) {
369
- sb .append ("y" );
370
- }
371
- if (isSet (GLOBAL )) {
372
- sb .append ("g" );
373
- }
374
- if (isSet (UNICODE )) {
375
- sb .append ("u" );
376
- }
377
- if (isSet (DOT_ALL )) {
378
- sb .append ("s" );
379
- }
380
- if (isSet (HAS_INDICES )) {
381
- sb .append ("d" );
382
- }
383
- if (isSet (UNICODE_SETS )) {
384
- sb .append ("v" );
385
- }
386
- return sb .toString ();
387
- }
388
401
}
389
402
}
0 commit comments