|
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 parseDisjunctionInsideQuoatedString() { |
| 288 | + SourceLocation loc = new SourceLocation(pos()); |
| 289 | + List<RegExpTerm> disjuncts = new ArrayList<>(); |
| 290 | + disjuncts.add(this.parseAlternativeInsideQuoatedString()); |
| 291 | + while (this.match("|")) { |
| 292 | + disjuncts.add(this.parseAlternativeInsideQuoatedString()); |
| 293 | + } |
| 294 | + if (disjuncts.size() == 1) return disjuncts.get(0); |
| 295 | + return this.finishTerm(new Disjunction(loc, disjuncts)); |
| 296 | + } |
| 297 | + |
| 298 | + private RegExpTerm parseAlternativeInsideQuoatedString() { |
| 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,14 @@ 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 | + System.out.println("Parsing string disjunction at position " + pos); |
| 472 | + RegExpTerm term = parseDisjunctionInsideQuoatedString(); |
| 473 | + this.expectRBrace(); |
| 474 | + System.out.println("Finished Parsing string disjunction at position " + pos); |
| 475 | + return this.finishTerm(new CharacterClassQuotedString(loc, term)); |
| 476 | + } |
| 477 | + |
430 | 478 | if (this.match("p{", "P{")) { |
431 | 479 | String name = this.readIdentifier(); |
432 | 480 | if (this.match("=")) { |
|
0 commit comments