Funnily, I found myself mucking around with the same bit of code since a
directive prologue that ends a program causes a ParseException.
My patch is below. Maybe we should synchronize.
Index: src/com/google/caja/parser/js/Parser.java
===================================================================
--- src/com/google/caja/parser/js/Parser.java (revision 3854)
+++ src/com/google/caja/parser/js/Parser.java (working copy)
@@ -73,16 +73,15 @@
*
* <xmp>
* // Terminal productions
- * Program => <UseSubsetDirective>?
<TerminatedStatement>*
+ * Program => <DirectivePrologue>?
<TerminatedStatement>*
* Statement => <TerminalStatement>
* | <NonTerminalStatement>
* Expression => <CommaOperator>
*
* // Non-terminal productions. Indentation indicates that a
nonterminal is
* // used only by the unindented nonterminal above it.
- * UseSubsetDirective => '"' 'use' <UseSubsetList>? '"'
- * UseSubsetList => <UseSubset> <UseSubsetTail>*
- * UseSubsetTail => ',' <UseSubset>
+ * DirectivePrologue => <Directive> <DirectivePrologue>?
+ * Directive => <StringLiteral> ';'
* TerminatedStatement => <BlockStatement>
* | <SimpleStatement> <StatementTerminator>
* StatementTerminator => ';'
@@ -323,62 +322,51 @@
Mark startOfPrologue = tq.mark();
List<Directive> directives = Lists.newArrayList();
- while (tq.peek().type == JsTokenType.STRING) {
+ while (!tq.isEmpty() && tq.peek().type == JsTokenType.STRING) {
Mark startOfDirective = tq.mark();
Token<JsTokenType> quotedString = tq.pop();
- boolean isDirective = false;
- switch(tq.peek().type) {
- case STRING:
- case REGEXP:
- case WORD:
- case INTEGER:
- case FLOAT:
- case KEYWORD:
- case COMMENT:
- isDirective = true;
- mq.addMessage(
- MessageType.SEMICOLON_INSERTED,
- FilePosition.endOf(quotedString.pos));
+ if (!tq.checkToken(Punctuation.SEMI)) {
+ Token<JsTokenType> t = !tq.isEmpty() ? tq.peek() : null;
+ if ((t == null || !continuesExpr(t.text)) &&
semicolonInserted()) {
+ FilePosition semiPoint =
FilePosition.endOf(tq.lastPosition());
+ MessageLevel lvl = tq.isEmpty()
+ || tq.lookaheadToken(Punctuation.RCURLY)
+ ? MessageLevel.LOG : MessageLevel.LINT;
+ mq.addMessage(MessageType.SEMICOLON_INSERTED, lvl,
semiPoint);
+ } else {
+ tq.rewind(startOfDirective);
break;
- case PUNCTUATION:
- if (tq.lookaheadToken(Punctuation.SEMI)) {
- isDirective = true;
- tq.pop();
- } else if (tq.lookaheadToken(Punctuation.RCURLY)) {
- isDirective = true;
- mq.addMessage(
- MessageType.SEMICOLON_INSERTED,
- FilePosition.endOf(quotedString.pos));
- }
- break;
- case LINE_CONTINUATION:
- break;
+ }
}
- if (!isDirective) {
- tq.rewind(startOfDirective);
- break;
- }
-
+ String unquoted = quotedString.text.substring(
+ 1, quotedString.text.length() - 1);
String decoded =
StringLiteral.getUnquotedValueOf(quotedString.text);
- if
(!Directive.RecognizedValue.isDirectiveStringRecognized(decoded)) {
+ if (!unquoted.equals(decoded)
+ ||
!Directive.RecognizedValue.isDirectiveStringRecognized(unquoted)) {
mq.addMessage(
MessageType.UNRECOGNIZED_DIRECTIVE_IN_PROLOGUE,
quotedString.pos, MessagePart.Factory.valueOf(decoded));
}
- directives.add(new Directive(
- FilePosition.startOf(quotedString.pos), decoded));
+ Directive d = new Directive(posFrom(quotedString.pos), decoded);
+ finish(d, startOfDirective);
+ directives.add(d);
}
if (directives.isEmpty()) { return null; }
- DirectivePrologue prologue =
- new DirectivePrologue(posFrom(startOfPrologue), directives);
+ DirectivePrologue prologue = new DirectivePrologue(
+ posFrom(startOfPrologue), directives);
finish(prologue, startOfPrologue);
return prologue;
}
+ private static boolean continuesExpr(String tokenText) {
+ return Operator.lookupOperation(tokenText, OperatorType.INFIX) !=
null
+ || Operator.lookupOperation(tokenText, OperatorType.BRACKET) !=
null
+ || Operator.lookupOperation(tokenText, OperatorType.TERNARY) !=
null;
+ }
private LabeledStatement parseLoopOrSwitch(FilePosition start, String
label)
throws ParseException {
@@ -1256,6 +1244,9 @@
}
private static boolean isTerminal(Statement s) {
+ if (s instanceof LabeledStmtWrapper) {
+ return isTerminal(((LabeledStmtWrapper) s).getBody());
+ }
return ((s instanceof Loop && !(s instanceof DoWhileLoop))
|| s instanceof Conditional || s instanceof
FunctionDeclaration
|| s instanceof Block || s instanceof TryStmt
@@ -1265,9 +1256,7 @@
private Statement parseTerminatedStatement() throws ParseException {
Statement s = parseStatement();
- if (!isTerminal(s)) {
- checkSemicolon();
- }
+ if (!isTerminal(s)) { checkSemicolon(); }
return s;
}
http://codereview.appspot.com/156090