Repository: asterixdb
Updated Branches:
  refs/heads/master 1d5412dc5 -> 7199a5697


Easier to read parser errors for SQL++

Change-Id: I13fd54fe2b1237b937a1706cf83fb47ce536b546
Reviewed-on: https://asterix-gerrit.ics.uci.edu/1182
Tested-by: Jenkins <jenk...@fulliautomatix.ics.uci.edu>
Integration-Tests: Jenkins <jenk...@fulliautomatix.ics.uci.edu>
Reviewed-by: abdullah alamoudi <bamou...@gmail.com>


Project: http://git-wip-us.apache.org/repos/asf/asterixdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/asterixdb/commit/7199a569
Tree: http://git-wip-us.apache.org/repos/asf/asterixdb/tree/7199a569
Diff: http://git-wip-us.apache.org/repos/asf/asterixdb/diff/7199a569

Branch: refs/heads/master
Commit: 7199a5697b7f70f4d9feb0a3d267a0d8281b72af
Parents: 1d5412d
Author: Till Westmann <ti...@apache.org>
Authored: Sat Sep 17 16:07:32 2016 -0700
Committer: Till Westmann <ti...@apache.org>
Committed: Sun Sep 18 09:51:07 2016 -0700

----------------------------------------------------------------------
 .../resources/runtimets/testsuite_sqlpp.xml     |  2 +-
 .../lang/common/parser/ScopeChecker.java        | 95 +++++++++++++++++++-
 .../asterix-lang-sqlpp/src/main/javacc/SQLPP.jj | 47 +++++++++-
 3 files changed, 135 insertions(+), 9 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/asterixdb/blob/7199a569/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml 
b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
index 54f06e6..c19593b 100644
--- a/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
+++ b/asterixdb/asterix-app/src/test/resources/runtimets/testsuite_sqlpp.xml
@@ -3163,7 +3163,7 @@
       <test-case FilePath="open-index-enforced/error-checking">
         <compilation-unit name="missing-optionality">
           <output-dir compare="Text">missing-optionality</output-dir>
-          <expected-error>"?"</expected-error>
+          <expected-error>string) enforced</expected-error>
         </compilation-unit>
       </test-case>
       <test-case FilePath="open-index-enforced/error-checking">

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/7199a569/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/parser/ScopeChecker.java
----------------------------------------------------------------------
diff --git 
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/parser/ScopeChecker.java
 
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/parser/ScopeChecker.java
index dc0fa93..07aa473 100644
--- 
a/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/parser/ScopeChecker.java
+++ 
b/asterixdb/asterix-lang-common/src/main/java/org/apache/asterix/lang/common/parser/ScopeChecker.java
@@ -29,6 +29,10 @@ import 
org.apache.hyracks.algebricks.core.algebra.base.Counter;
 
 public class ScopeChecker {
 
+    protected static String quot = "\"";
+
+    protected String eol = System.getProperty("line.separator", "\n");
+
     protected Counter varCounter = new Counter(-1);
 
     protected Stack<Scope> scopeStack = new Stack<Scope>();
@@ -57,7 +61,6 @@ public class ScopeChecker {
     /**
      * Create a new scope, using the top scope in scopeStack as parent scope
      *
-     * @param scopeStack
      * @return new scope
      */
     public final Scope createNewScope() {
@@ -70,7 +73,6 @@ public class ScopeChecker {
     /**
      * Extend the current scope
      *
-     * @param scopeStack
      * @return
      */
     public final Scope extendCurrentScope() {
@@ -172,7 +174,88 @@ public class ScopeChecker {
         return false;
     }
 
-    public static final String removeQuotesAndEscapes(String s) {
+    protected int appendExpected(StringBuilder expected, int[][] 
expectedTokenSequences, String[] tokenImage) {
+        int maxSize = 0;
+        for (int i = 0; i < expectedTokenSequences.length; i++) {
+            if (maxSize < expectedTokenSequences[i].length) {
+                maxSize = expectedTokenSequences[i].length;
+            }
+            for (int j = 0; j < expectedTokenSequences[i].length; j++) {
+                append(expected, 
fixQuotes(tokenImage[expectedTokenSequences[i][j]]));
+                append(expected, " ");
+            }
+            if (expectedTokenSequences[i][expectedTokenSequences[i].length - 
1] != 0) {
+                append(expected, "...");
+            }
+            append(expected, eol);
+            append(expected, "    ");
+        }
+        return maxSize;
+    }
+
+    private void append(StringBuilder expected, String str) {
+        if (expected != null) {
+            expected.append(str);
+        }
+    }
+
+    protected String fixQuotes(String token) {
+        final int last = token.length() - 1;
+        if (token.charAt(0) == '"' && token.charAt(last) == '"') {
+            return "'" + token.substring(1, last) + "'";
+        } else {
+            return token;
+        }
+    }
+
+    protected static String addEscapes(String str) {
+        StringBuilder escaped = new StringBuilder();
+        for (int i = 0; i < str.length(); i++) {
+            appendChar(escaped, str.charAt(i));
+        }
+        return escaped.toString();
+    }
+
+    private static void appendChar(StringBuilder escaped, char c) {
+        char ch;
+        switch (c) {
+            case 0:
+                return;
+            case '\b':
+                escaped.append("\\b");
+                return;
+            case '\t':
+                escaped.append("\\t");
+                return;
+            case '\n':
+                escaped.append("\\n");
+                return;
+            case '\f':
+                escaped.append("\\f");
+                return;
+            case '\r':
+                escaped.append("\\r");
+                return;
+            case '\"':
+                escaped.append("\\\"");
+                return;
+            case '\'':
+                escaped.append("\\\'");
+                return;
+            case '\\':
+                escaped.append("\\\\");
+                return;
+            default:
+                if ((ch = c) < 0x20 || ch > 0x7e) {
+                    String s = "0000" + Integer.toString(ch, 16);
+                    escaped.append("\\u").append(s.substring(s.length() - 4, 
s.length()));
+                } else {
+                    escaped.append(ch);
+                }
+        }
+    }
+
+    public static String removeQuotesAndEscapes(String s) {
         char q = s.charAt(0); // simple or double quote
         String stripped = s.substring(1, s.length() - 1);
         int pos = stripped.indexOf('\\');
@@ -220,7 +303,11 @@ public class ScopeChecker {
         return res.toString();
     }
 
-    public String extractFragment(int beginLine, int beginColumn, int endLine, 
int endColumn) {
+    protected String getLine(int line) {
+        return inputLines[line - 1];
+    }
+
+    protected String extractFragment(int beginLine, int beginColumn, int 
endLine, int endColumn) {
         StringBuilder extract = new StringBuilder();
         if (beginLine == endLine) {
             // special case that we need to handle separately

http://git-wip-us.apache.org/repos/asf/asterixdb/blob/7199a569/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
----------------------------------------------------------------------
diff --git a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj 
b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
index 9cabf84..dab178b 100644
--- a/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
+++ b/asterixdb/asterix-lang-sqlpp/src/main/javacc/SQLPP.jj
@@ -151,8 +151,6 @@ import 
org.apache.hyracks.algebricks.core.algebra.expressions.IExpressionAnnotat
 import 
org.apache.hyracks.algebricks.core.algebra.expressions.IndexedNLJoinExpressionAnnotation;
 import org.apache.hyracks.algebricks.core.algebra.functions.FunctionIdentifier;
 
-
-
 class SQLPPParser extends ScopeChecker implements IParser {
 
     // optimizer hints
@@ -179,6 +177,9 @@ class SQLPPParser extends ScopeChecker implements IParser {
     // data generator hints
     private static final String DGEN_HINT = "dgen";
 
+    // error configuration
+    protected static final boolean REPORT_EXPECTED_TOKENS = false;
+
     private static class IndexParams {
       public IndexType type;
       public int gramLength;
@@ -251,7 +252,7 @@ class SQLPPParser extends ScopeChecker implements IParser {
       return rfdg;
     }
 
-    public SQLPPParser(String s){
+    public SQLPPParser(String s) {
         this(new StringReader(s));
         super.setInput(s);
     }
@@ -272,9 +273,47 @@ class SQLPPParser extends ScopeChecker implements IParser {
             // by the ANTLR-generated lexer or parser (e.g it does this for 
invalid backslash u + 4 hex digits escapes)
             throw new AsterixException(new ParseException(e.getMessage()));
         } catch (ParseException e) {
-            throw new AsterixException(e.getMessage());
+            throw new AsterixException("Syntax error: " + getMessage(e));
+        }
+    }
+
+    protected String getMessage(ParseException pe) {
+        Token currentToken = pe.currentToken;
+        if (currentToken == null) {
+            return pe.getMessage();
         }
+        int[][] expectedTokenSequences = pe.expectedTokenSequences;
+        String[] tokenImage = pe.tokenImage;
+        String sep = REPORT_EXPECTED_TOKENS ? eol : " ";
+        StringBuilder expected = REPORT_EXPECTED_TOKENS ? new StringBuilder() 
: null;
+        int maxSize = appendExpected(expected, expectedTokenSequences, 
tokenImage);
+        Token tok = currentToken.next;
+        int line = tok.beginLine;
+        String message = "In line " + line + " >>" + getLine(line) + "<<" + 
sep + "Encountered ";
+        for (int i = 0; i < maxSize; i++) {
+            if (i != 0) {
+                message += " ";
+            }
+            if (tok.kind == 0) {
+                message += fixQuotes(tokenImage[0]);
+                break;
+            }
+            message += fixQuotes(tokenImage[tok.kind]) + " ";
+            message += quot + addEscapes(tok.image) + quot;
+            tok = tok.next;
+        }
+        message += " at column " + currentToken.next.beginColumn + "." + sep;
+        if (REPORT_EXPECTED_TOKENS) {
+            if (expectedTokenSequences.length == 1) {
+                message += "Was expecting:" + sep + "    ";
+            } else {
+                message += "Was expecting one of:" + sep + "    ";
+            }
+            message += expected.toString();
+        }
+        return message;
     }
+
 }
 
 PARSER_END(SQLPPParser)

Reply via email to