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

Reply via email to