|
6 | 6 | import com.semmle.js.ast.regexp.Caret;
|
7 | 7 | import com.semmle.js.ast.regexp.CharacterClass;
|
8 | 8 | import com.semmle.js.ast.regexp.CharacterClassEscape;
|
| 9 | +import com.semmle.js.ast.regexp.CharacterClassQuotedString; |
9 | 10 | import com.semmle.js.ast.regexp.CharacterClassRange;
|
10 | 11 | import com.semmle.js.ast.regexp.Constant;
|
11 | 12 | import com.semmle.js.ast.regexp.ControlEscape;
|
@@ -283,6 +284,45 @@ private RegExpTerm parseTerm() {
|
283 | 284 | return this.finishTerm(this.parseQuantifierOpt(loc, this.parseAtom()));
|
284 | 285 | }
|
285 | 286 |
|
| 287 | + private RegExpTerm parseDisjunctionInsideQuotedString() { |
| 288 | + SourceLocation loc = new SourceLocation(pos()); |
| 289 | + List<RegExpTerm> disjuncts = new ArrayList<>(); |
| 290 | + disjuncts.add(this.parseAlternativeInsideQuotedString()); |
| 291 | + while (this.match("|")) { |
| 292 | + disjuncts.add(this.parseAlternativeInsideQuotedString()); |
| 293 | + } |
| 294 | + if (disjuncts.size() == 1) return disjuncts.get(0); |
| 295 | + return this.finishTerm(new Disjunction(loc, disjuncts)); |
| 296 | + } |
| 297 | + |
| 298 | + private RegExpTerm parseAlternativeInsideQuotedString() { |
| 299 | + SourceLocation loc = new SourceLocation(pos()); |
| 300 | + StringBuilder sb = new StringBuilder(); |
| 301 | + boolean escaped = false; |
| 302 | + while (true) { |
| 303 | + // If we're at the end of the string, something went wrong. |
| 304 | + if (this.atEOS()) { |
| 305 | + this.error(Error.UNEXPECTED_EOS); |
| 306 | + break; |
| 307 | + } |
| 308 | + // We can end parsing if we're not escaped and we see a `|` which would mean Alternation |
| 309 | + // or `}` which would mean the end of the Quoted String. |
| 310 | + if(!escaped && this.lookahead(null, "|", "}")){ |
| 311 | + break; |
| 312 | + } |
| 313 | + char c = this.nextChar(); |
| 314 | + // Track whether the character is an escape character. |
| 315 | + escaped = !escaped && (c == '\\'); |
| 316 | + sb.append(c); |
| 317 | + } |
| 318 | + |
| 319 | + String literal = sb.toString(); |
| 320 | + loc.setEnd(pos()); |
| 321 | + loc.setSource(literal); |
| 322 | + |
| 323 | + return new Constant(loc, literal); |
| 324 | + } |
| 325 | + |
286 | 326 | private RegExpTerm parseQuantifierOpt(SourceLocation loc, RegExpTerm atom) {
|
287 | 327 | if (this.match("*")) return this.finishTerm(new Star(loc, atom, !this.match("?")));
|
288 | 328 | if (this.match("+")) return this.finishTerm(new Plus(loc, atom, !this.match("?")));
|
@@ -427,6 +467,12 @@ private RegExpTerm parseAtomEscape(SourceLocation loc, boolean inCharClass) {
|
427 | 467 | return this.finishTerm(new NamedBackReference(loc, name, "\\k<" + name + ">"));
|
428 | 468 | }
|
429 | 469 |
|
| 470 | + if (this.match("q{")) { |
| 471 | + RegExpTerm term = parseDisjunctionInsideQuotedString(); |
| 472 | + this.expectRBrace(); |
| 473 | + return this.finishTerm(new CharacterClassQuotedString(loc, term)); |
| 474 | + } |
| 475 | + |
430 | 476 | if (this.match("p{", "P{")) {
|
431 | 477 | String name = this.readIdentifier();
|
432 | 478 | if (this.match("=")) {
|
|
0 commit comments