You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by us...@apache.org on 2015/08/07 09:10:41 UTC

svn commit: r1694614 [1/4] - in /lucene/dev/trunk: lucene/ lucene/expressions/ lucene/expressions/src/java/org/apache/lucene/expressions/js/ lucene/expressions/src/test/org/apache/lucene/expressions/js/ lucene/licenses/ solr/core/ solr/licenses/

Author: uschindler
Date: Fri Aug  7 07:10:40 2015
New Revision: 1694614

URL: http://svn.apache.org/r1694614
Log:
LUCENE-6417: Upgrade ANTLR used in expressions module to version 4.5

Added:
    lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptBaseVisitor.java   (with props)
    lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptErrorHandlingLexer.java   (with props)
    lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptLexer.tokens   (with props)
    lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptParserErrorStrategy.java   (with props)
    lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptVisitor.java   (with props)
    lucene/dev/trunk/lucene/licenses/antlr4-runtime-4.5.jar.sha1   (with props)
    lucene/dev/trunk/lucene/licenses/antlr4-runtime-LICENSE-BSD.txt   (with props)
    lucene/dev/trunk/lucene/licenses/antlr4-runtime-NOTICE.txt   (with props)
Removed:
    lucene/dev/trunk/lucene/licenses/antlr-runtime-3.5.jar.sha1
    lucene/dev/trunk/lucene/licenses/antlr-runtime-LICENSE-BSD_LIKE.txt
    lucene/dev/trunk/lucene/licenses/antlr-runtime-NOTICE.txt
    lucene/dev/trunk/solr/licenses/antlr-runtime-3.5.jar.sha1
    lucene/dev/trunk/solr/licenses/antlr-runtime-LICENSE-BSD_LIKE.txt
    lucene/dev/trunk/solr/licenses/antlr-runtime-NOTICE.txt
Modified:
    lucene/dev/trunk/lucene/CHANGES.txt
    lucene/dev/trunk/lucene/expressions/build.xml
    lucene/dev/trunk/lucene/expressions/ivy.xml
    lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/Javascript.g
    lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/Javascript.tokens
    lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java
    lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptLexer.java
    lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptParser.java
    lucene/dev/trunk/lucene/expressions/src/test/org/apache/lucene/expressions/js/TestJavascriptCompiler.java
    lucene/dev/trunk/lucene/ivy-versions.properties
    lucene/dev/trunk/solr/core/ivy.xml

Modified: lucene/dev/trunk/lucene/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/CHANGES.txt?rev=1694614&r1=1694613&r2=1694614&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/CHANGES.txt (original)
+++ lucene/dev/trunk/lucene/CHANGES.txt Fri Aug  7 07:10:40 2015
@@ -58,6 +58,11 @@ Optimizations
 * LUCENE-6720: ValueSourceScorer, returned from
   FunctionValues.getRangeScorer(), now uses TwoPhaseIterator. (David Smiley)
 
+Other
+
+* LUCENE-6417: Upgrade ANTLR used in expressions module to version 4.5.
+  (Jack Conradson via Uwe Schindler)
+
 ======================= Lucene 5.3.0 =======================
 
 New Features

Modified: lucene/dev/trunk/lucene/expressions/build.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/expressions/build.xml?rev=1694614&r1=1694613&r2=1694614&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/expressions/build.xml (original)
+++ lucene/dev/trunk/lucene/expressions/build.xml Fri Aug  7 07:10:40 2015
@@ -51,7 +51,7 @@
   <target name="regenerate" depends="run-antlr"/>
 
   <target name="resolve-antlr" xmlns:ivy="antlib:org.apache.ivy.ant">
-    <ivy:cachepath organisation="org.antlr" module="antlr" revision="3.5"
+    <ivy:cachepath organisation="org.antlr" module="antlr4" revision="4.5"
                   inline="true" conf="default" type="jar" pathid="antlr.classpath"/>
   </target>
 
@@ -97,13 +97,15 @@
       <replace-value property="grammar.matchpath" value="${grammar.path}${file.separator}" from="\" to="\\"/>
       <property name="-grammar.relative.path" location="${grammar.path}" relative="true"/>
       <replace-value property="grammar.relative.path" value="${-grammar.relative.path}${file.separator}" from="${file.separator}" to="/"/>
-      <java classname="org.antlr.Tool" fork="true" failonerror="true" classpathref="antlr.classpath" taskname="antlr">
+      <java classname="org.antlr.v4.Tool" fork="true" failonerror="true" classpathref="antlr.classpath" taskname="antlr">
         <sysproperty key="file.encoding" value="UTF-8"/>
         <sysproperty key="user.language" value="en"/>
         <sysproperty key="user.country" value="US"/>
         <sysproperty key="user.variant" value=""/>
-        <arg value="-verbose"/>
-        <arg value="-make"/>
+        <arg value="-package"/>
+        <arg value="org.apache.lucene.expressions.@{package}"/>
+        <arg value="-no-listener"/>
+        <arg value="-visitor"/>
         <arg value="-o"/>
         <arg path="${grammar.path}"/>
         <arg path="${grammar.path}/@{grammar}.g"/>
@@ -111,18 +113,28 @@
       <!-- replace absolute paths by relative ones -->
       <replace file="${grammar.path}/@{grammar}Parser.java" token="${grammar.matchpath}" value="${grammar.relative.path}" encoding="UTF-8"/>
       <replace file="${grammar.path}/@{grammar}Lexer.java" token="${grammar.matchpath}" value="${grammar.relative.path}" encoding="UTF-8"/>
-      <!-- make the generated classes package private (it's an antlr option with 4.0) -->
+      <replace file="${grammar.path}/@{grammar}Visitor.java" token="${grammar.matchpath}" value="${grammar.relative.path}" encoding="UTF-8"/>
+      <replace file="${grammar.path}/@{grammar}BaseVisitor.java" token="${grammar.matchpath}" value="${grammar.relative.path}" encoding="UTF-8"/>
+      <!-- make the generated classes package private -->
       <replace file="${grammar.path}/@{grammar}Parser.java" token="public class @{grammar}Parser" value="class @{grammar}Parser" encoding="UTF-8"/>
       <replace file="${grammar.path}/@{grammar}Lexer.java" token="public class @{grammar}Lexer" value="class @{grammar}Lexer" encoding="UTF-8"/>
+      <replace file="${grammar.path}/@{grammar}Visitor.java" token="public interface @{grammar}Visitor" value="interface @{grammar}Visitor" encoding="UTF-8"/>
+      <replace file="${grammar.path}/@{grammar}BaseVisitor.java" token="public class @{grammar}BaseVisitor" value="class @{grammar}BaseVisitor" encoding="UTF-8"/>
       <!-- nuke timestamps in generated files -->
       <replaceregexp file="${grammar.path}/@{grammar}Parser.java" match=".*" replace="\/\/ ANTLR GENERATED CODE: DO NOT EDIT" encoding="UTF-8"/>
       <replaceregexp file="${grammar.path}/@{grammar}Lexer.java" match=".*" replace="\/\/ ANTLR GENERATED CODE: DO NOT EDIT" encoding="UTF-8"/>
+      <replaceregexp file="${grammar.path}/@{grammar}Visitor.java" match=".*" replace="\/\/ ANTLR GENERATED CODE: DO NOT EDIT" encoding="UTF-8"/>
+      <replaceregexp file="${grammar.path}/@{grammar}BaseVisitor.java" match=".*" replace="\/\/ ANTLR GENERATED CODE: DO NOT EDIT" encoding="UTF-8"/>
       <!-- remove tabs in antlr generated files -->
       <replaceregexp file="${grammar.path}/@{grammar}Parser.java" match="\t" flags="g" replace="  " encoding="UTF-8"/>
       <replaceregexp file="${grammar.path}/@{grammar}Lexer.java" match="\t" flags="g" replace="  " encoding="UTF-8"/>
+      <replaceregexp file="${grammar.path}/@{grammar}Visitor.java" match="\t" flags="g" replace="  " encoding="UTF-8"/>
+      <replaceregexp file="${grammar.path}/@{grammar}BaseVisitor.java" match="\t" flags="g" replace="  " encoding="UTF-8"/>
       <!-- fix line endings -->
       <fixcrlf file="${grammar.path}/@{grammar}Parser.java"/>
       <fixcrlf file="${grammar.path}/@{grammar}Lexer.java"/>
+      <fixcrlf file="${grammar.path}/@{grammar}Visitor.java"/>
+      <fixcrlf file="${grammar.path}/@{grammar}BaseVisitor.java"/>
     </sequential>
   </macrodef>
 </project>

Modified: lucene/dev/trunk/lucene/expressions/ivy.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/expressions/ivy.xml?rev=1694614&r1=1694613&r2=1694614&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/expressions/ivy.xml (original)
+++ lucene/dev/trunk/lucene/expressions/ivy.xml Fri Aug  7 07:10:40 2015
@@ -22,7 +22,7 @@
     <conf name="compile" transitive="false"/>
   </configurations>
   <dependencies>
-    <dependency org="org.antlr" name="antlr-runtime" rev="${/org.antlr/antlr-runtime}" conf="compile"/>
+    <dependency org="org.antlr" name="antlr4-runtime" rev="${/org.antlr/antlr4-runtime}" conf="compile"/>
     <dependency org="org.ow2.asm" name="asm" rev="${/org.ow2.asm/asm}" conf="compile"/>
     <dependency org="org.ow2.asm" name="asm-commons" rev="${/org.ow2.asm/asm-commons}" conf="compile"/>
     <exclude org="*" ext="*" matcher="regexp" type="${ivy.exclude.types}"/>

Modified: lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/Javascript.g
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/Javascript.g?rev=1694614&r1=1694613&r2=1694614&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/Javascript.g (original)
+++ lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/Javascript.g Fri Aug  7 07:10:40 2015
@@ -1,417 +1,89 @@
 /*
-    Javascript.g
-    An expression syntax based on ECMAScript/Javascript.
- 
-    This file was adapted from a general ECMAScript language definition at http://research.xebic.com/es3.
-    The major changes are the following:
-        * Stripped grammar of all parts not relevant for expression syntax.
-        * Stripped grammar of unicode character support.
-        * Added override function for customized error handling.
-        * Renaming of many grammar rules.
-        * Removal of annotations no longer relevant for stripped pieces.
-     
-    The Original Copyright Notice is the following: 
-    
-        Copyrights 2008-2009 Xebic Reasearch BV. All rights reserved..
-        Original work by Patrick Hulsmeijer.
- 
-        This ANTLR 3 LL(*) grammar is based on Ecma-262 3rd edition (JavaScript 1.5, JScript 5.5). 
-        The annotations refer to the "A Grammar Summary" section (e.g. A.1 Lexical Grammar)
-        and the numbers in parenthesis to the paragraph numbers (e.g. (7.8) ).
-        This document is best viewed with ANTLRWorks (www.antlr.org).
- 
-        Software License Agreement (BSD License)
- 
-        Copyright (c) 2008-2010, Xebic Research B.V.
-        All rights reserved.
- 
-        Redistribution and use of this software in source and binary forms, with or without modification, are
-        permitted provided that the following conditions are met:
- 
-            * Redistributions of source code must retain the above
-              copyright notice, this list of conditions and the
-              following disclaimer.
- 
-            * Redistributions in binary form must reproduce the above
-              copyright notice, this list of conditions and the
-              following disclaimer in the documentation and/or other
-              materials provided with the distribution.
- 
-            * Neither the name of Xebic Research B.V. nor the names of its
-              contributors may be used to endorse or promote products
-              derived from this software without specific prior
-              written permission of Xebic Research B.V.
- 
-        THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED
-        WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
-        PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
-        ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-        LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
-        INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
-        TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
-        ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
  */
 
-// ***********************************************************************
-// * ANTLRv3 grammar for Lucene expression language.
-// ***********************************************************************
- 
-grammar Javascript;
-
-options {
-    language = Java;
-    output = AST;
-    ASTLabelType=CommonTree;
-}
-
-tokens
-{
-    AT_LPAREN          = '('  ;
-    AT_RPAREN          = ')'  ;
-    AT_DOT             = '.'  ;
-    AT_COMMA           = ','  ;
-    AT_COLON           = ':'  ;
-
-    AT_COMP_LT         = '<'  ;
-    AT_COMP_LTE        = '<=' ;
-    AT_COMP_EQ         = '==' ;
-    AT_COMP_NEQ        = '!=' ;
-    AT_COMP_GTE        = '>=' ;
-    AT_COMP_GT         = '>'  ;
-
-    AT_BOOL_NOT        = '!'  ;
-    AT_BOOL_AND        = '&&' ;
-    AT_BOOL_OR         = '||' ;
-    AT_COND_QUE        = '?'  ;
-
-    AT_NEGATE                 ;
-    AT_ADD             = '+'  ;
-    AT_SUBTRACT        = '-'  ;
-    AT_MULTIPLY        = '*'  ;
-    AT_DIVIDE          = '/'  ;
-    AT_MODULO          = '%'  ;
-
-    AT_BIT_SHL         = '<<' ;
-    AT_BIT_SHR         = '>>' ;
-    AT_BIT_SHU         = '>>>';
-    AT_BIT_AND         = '&'  ;
-    AT_BIT_OR          = '|'  ;
-    AT_BIT_XOR         = '^'  ;
-    AT_BIT_NOT         = '~'  ;
-    
-    AT_CALL                   ;
-}
-
-// ***********************************************************************
-// * Java Package
-// ***********************************************************************
-
-@lexer::header {
-package org.apache.lucene.expressions.js;
-
-import java.text.ParseException;
-}
-
-@parser::header {
-package org.apache.lucene.expressions.js;
-    
-import java.text.ParseException;
-}
- 
-// ***********************************************************************
-// * Error Handling
-// ***********************************************************************
-
-@lexer::members {
-
-@Override
-public void displayRecognitionError(String[] tokenNames, RecognitionException re) {  
-    String message = " unexpected character '" + (char)re.c 
-                   + "' at position (" + re.charPositionInLine + ").";
-    ParseException parseException = new ParseException(message, re.charPositionInLine);
-    parseException.initCause(re);
-    throw new RuntimeException(parseException);
-}
-
-}
-
-@parser::members {
-
-@Override
-public void displayRecognitionError(String[] tokenNames, RecognitionException re) {
-    String message;
-
-    if (re.token == null) {
-        message = " unknown error (missing token).";
-    }
-    else if (re instanceof UnwantedTokenException) {
-        message = " extraneous " + getReadableTokenString(re.token)
-                +  " at position (" + re.charPositionInLine + ").";
-    }
-    else if (re instanceof MissingTokenException) {
-        message = " missing " + getReadableTokenString(re.token)
-                +  " at position (" + re.charPositionInLine + ").";
-    }
-    else if (re instanceof NoViableAltException) {
-        switch (re.token.getType()) {
-        case EOF:
-            message = " unexpected end of expression.";
-            break;
-        default:
-            message = " invalid sequence of tokens near " + getReadableTokenString(re.token)
-                    +  " at position (" + re.charPositionInLine + ").";
-            break;
-        }
-    }
-    else {
-        message = " unexpected token " + getReadableTokenString(re.token) 
-                +  " at position (" + re.charPositionInLine + ").";
-    }
-    ParseException parseException = new ParseException(message, re.charPositionInLine);
-    parseException.initCause(re);
-    throw new RuntimeException(parseException);
-}
-
-public static String getReadableTokenString(Token token) {
-    if (token == null) {
-        return "unknown token";
-    }
-
-    switch (token.getType()) {
-    case AT_LPAREN:
-        return "open parenthesis '('";
-    case AT_RPAREN:
-        return "close parenthesis ')'";
-    case AT_COMP_LT:
-        return "less than '<'";
-    case AT_COMP_LTE:
-        return "less than or equal '<='";
-    case AT_COMP_GT:
-        return "greater than '>'";
-    case AT_COMP_GTE:
-        return "greater than or equal '>='";
-    case AT_COMP_EQ:
-        return "equal '=='";
-    case AT_NEGATE:
-        return "negate '!='";
-    case AT_BOOL_NOT:
-        return "boolean not '!'";
-    case AT_BOOL_AND:
-        return "boolean and '&&'";
-    case AT_BOOL_OR:
-        return "boolean or '||'";
-    case AT_COND_QUE:
-        return "conditional '?'";
-    case AT_ADD:
-        return "addition '+'";
-    case AT_SUBTRACT:
-        return "subtraction '-'";
-    case AT_MULTIPLY:
-        return "multiplication '*'";
-    case AT_DIVIDE:
-        return "division '/'";
-    case AT_MODULO:
-        return "modulo '\%'";
-    case AT_BIT_SHL:
-        return "bit shift left '<<'";
-    case AT_BIT_SHR:
-        return "bit shift right '>>'";
-    case AT_BIT_SHU:
-        return "unsigned bit shift right '>>>'";
-    case AT_BIT_AND:
-        return "bitwise and '&'";
-    case AT_BIT_OR:
-        return "bitwise or '|'";
-    case AT_BIT_XOR:
-        return "bitwise xor '^'";
-    case AT_BIT_NOT:
-        return "bitwise not '~'";
-    case ID:
-        return "identifier '" + token.getText() + "'";
-    case DECIMAL:
-        return "decimal '" + token.getText() + "'";
-    case OCTAL:
-        return "octal '" + token.getText() + "'";
-    case HEX:
-        return "hex '" + token.getText() + "'";
-    case EOF:
-        return "end of expression";
-    default:
-        return "'" + token.getText() + "'";
-    }
-}
-
-}
-
-// ***********************************************************************
-// * Parser Rules
-// ***********************************************************************
-
-expression
-    : conditional EOF!
-    ;
-
-conditional
-    : logical_or (AT_COND_QUE^ conditional AT_COLON! conditional)?
-    ;
-
-logical_or
-    : logical_and (AT_BOOL_OR^ logical_and)*
-    ;
-
-logical_and
-    : bitwise_or (AT_BOOL_AND^ bitwise_or)*
-    ;
-
-bitwise_or
-    : bitwise_xor (AT_BIT_OR^ bitwise_xor)*
-    ;
-
-bitwise_xor
-    : bitwise_and (AT_BIT_XOR^ bitwise_and)*
-    ;
-
-bitwise_and
-    :  equality (AT_BIT_AND^ equality)*
-    ;
-
-equality
-    : relational ((AT_COMP_EQ | AT_COMP_NEQ)^ relational)*
-    ;
-
-relational
-    : shift ((AT_COMP_LT | AT_COMP_GT | AT_COMP_LTE | AT_COMP_GTE)^ shift)*
-    ;
-
-shift
-    : additive ((AT_BIT_SHL | AT_BIT_SHR | AT_BIT_SHU)^ additive)*
-    ;
-
-additive
-    : multiplicative ((AT_ADD | AT_SUBTRACT)^ multiplicative)*
-    ;
-
-multiplicative
-    : unary ((AT_MULTIPLY | AT_DIVIDE | AT_MODULO)^ unary)*
-    ;
-
-unary
-    : postfix
-    | AT_ADD! unary
-    | unary_operator^ unary
-    ;
-
-unary_operator
-    : AT_SUBTRACT -> AT_NEGATE
-    | AT_BIT_NOT
-    | AT_BOOL_NOT
-    ;
-
-postfix
-    : primary
-    | VARIABLE arguments -> ^(AT_CALL VARIABLE arguments?)
-    ;
-
-primary
-    : VARIABLE
-    | numeric
-    | AT_LPAREN! conditional AT_RPAREN!
-    ;
-
-arguments
-    : AT_LPAREN! (conditional (AT_COMMA! conditional)*)? AT_RPAREN!
-    ;
-
-numeric
-    : HEX | OCTAL | DECIMAL
-    ;
-
-// ***********************************************************************
-// * Lexer Rules
-// ***********************************************************************
-
-VARIABLE
-    : OBJECT (AT_DOT OBJECT)*
-    ;
-
-fragment
-OBJECT
-    : ID ARRAY*
-    ;
-
-fragment
-ARRAY
-    : '[' STRING ']'
-    | '[' DECIMALINTEGER ']'
-    ;
-
-fragment
-ID
-    : ('a'..'z'|'A'..'Z'|'_'|'$') ('a'..'z'|'A'..'Z'|'0'..'9'|'_'|'$')*
-    ;
-
-fragment
-STRING
-    : '\'' SINGLE_STRING_CHAR* '\'' { }
-    | '"' DOUBLE_STRING_CHAR* '"'
-    ;
-
-fragment
-SINGLE_STRING_CHAR
-    : '\\\''
-    | '\\\\'
-    | ~('\\'|'\'')
-    ;
-
-fragment
-DOUBLE_STRING_CHAR
-    : '\\"'
-    | '\\\\'
-    | ~('\\'|'"')
-    ;
-
-WS
-    : (' '|'\t'|'\n'|'\r')+ {skip();}
-    ;
-
-DECIMAL
-    : DECIMALINTEGER AT_DOT DECIMALDIGIT* EXPONENT?
-    | AT_DOT DECIMALDIGIT+ EXPONENT?
-    | DECIMALINTEGER EXPONENT?
-    ;
-
-OCTAL
-    : '0' OCTALDIGIT+
-    ;
-
-HEX
-    : ('0x'|'0X') HEXDIGIT+
-    ;
-
-fragment 
-DECIMALINTEGER
-    : '0'
-    | '1'..'9' DECIMALDIGIT*
-    ;
-
-fragment 
-EXPONENT
-    : ('e'|'E') ('+'|'-')? DECIMALDIGIT+
-    ;
+/*
+ * ANTLRv4 grammar for the Lucene expressions language
+ */
 
-fragment
-DECIMALDIGIT
-    : '0'..'9'
-    ;
+grammar Javascript;
 
-fragment 
-HEXDIGIT
-    : DECIMALDIGIT 
-    | 'a'..'f' 
-    | 'A'..'F'
+compile
+    : expression EOF
     ;
 
-fragment 
-OCTALDIGIT
-    : '0'..'7'
+expression
+    : LP expression RP                                          # precedence
+    | ( OCTAL | HEX | DECIMAL )                                 # numeric
+    | VARIABLE ( LP (expression (COMMA expression)*)? RP )?     # external
+    | ( BOOLNOT | BWNOT | ADD | SUB ) expression                # unary
+    | expression ( MUL | DIV | REM ) expression                 # muldiv
+    | expression ( ADD | SUB ) expression                       # addsub
+    | expression ( LSH | RSH | USH ) expression                 # bwshift
+    | expression ( LT | LTE | GT | GTE ) expression             # boolcomp
+    | expression ( EQ | NE ) expression                         # booleqne
+    | expression BWAND expression                               # bwand
+    | expression BWXOR expression                               # bwxor
+    | expression BWOR expression                                # bwor
+    | expression BOOLAND expression                             # booland
+    | expression BOOLOR expression                              # boolor
+    | <assoc=right> expression COND expression COLON expression # conditional
+    ;
+
+LP:      [(];
+RP:      [)];
+COMMA:   [,];
+BOOLNOT: [!];
+BWNOT:   [~];
+MUL:     [*];
+DIV:     [/];
+REM:     [%];
+ADD:     [+];
+SUB:     [\-];
+LSH:     '<<';
+RSH:     '>>';
+USH:     '>>>';
+LT:      [<];
+LTE:     '<=';
+GT:      [>];
+GTE:     '>=';
+EQ:      '==';
+NE:      '!=';
+BWAND:   [&];
+BWXOR:   [^];
+BWOR:    [|];
+BOOLAND: '&&';
+BOOLOR:  '||';
+COND:    [?];
+COLON:   [:];
+
+WS: [ \t\n\r]+ -> skip;
+
+VARIABLE: ID ARRAY* ( [.] ID ARRAY* )*;
+fragment ARRAY: [[] ( STRING | INTEGER ) [\]];
+fragment ID: [_$a-zA-Z] [_$a-zA-Z0-9]*;
+fragment STRING
+    : ['] ( '\\\'' | '\\\\' | ~[\\'] )*? [']
+    | ["] ( '\\"' | '\\\\' | ~[\\"] )*? ["]
+    ;
+
+OCTAL: [0] [0-7]+;
+HEX: [0] [xX] [0-9a-fA-F]+;
+DECIMAL: ( INTEGER ( [.] [0-9]* )? | [.] [0-9]+ ) ( [eE] [+\-]? [0-9]+ )?;
+fragment INTEGER
+    : [0]
+    | [1-9] [0-9]*
     ;

Modified: lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/Javascript.tokens
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/Javascript.tokens?rev=1694614&r1=1694613&r2=1694614&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/Javascript.tokens (original)
+++ lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/Javascript.tokens Fri Aug  7 07:10:40 2015
@@ -1,72 +1,40 @@
-ARRAY=4
-AT_ADD=5
-AT_BIT_AND=6
-AT_BIT_NOT=7
-AT_BIT_OR=8
-AT_BIT_SHL=9
-AT_BIT_SHR=10
-AT_BIT_SHU=11
-AT_BIT_XOR=12
-AT_BOOL_AND=13
-AT_BOOL_NOT=14
-AT_BOOL_OR=15
-AT_CALL=16
-AT_COLON=17
-AT_COMMA=18
-AT_COMP_EQ=19
-AT_COMP_GT=20
-AT_COMP_GTE=21
-AT_COMP_LT=22
-AT_COMP_LTE=23
-AT_COMP_NEQ=24
-AT_COND_QUE=25
-AT_DIVIDE=26
-AT_DOT=27
-AT_LPAREN=28
-AT_MODULO=29
-AT_MULTIPLY=30
-AT_NEGATE=31
-AT_RPAREN=32
-AT_SUBTRACT=33
-DECIMAL=34
-DECIMALDIGIT=35
-DECIMALINTEGER=36
-DOUBLE_STRING_CHAR=37
-EXPONENT=38
-HEX=39
-HEXDIGIT=40
-ID=41
-OBJECT=42
-OCTAL=43
-OCTALDIGIT=44
-SINGLE_STRING_CHAR=45
-STRING=46
-VARIABLE=47
-WS=48
-'!'=14
-'!='=24
-'%'=29
-'&&'=13
-'&'=6
-'('=28
-')'=32
-'*'=30
-'+'=5
-','=18
-'-'=33
-'.'=27
-'/'=26
-':'=17
-'<'=22
-'<<'=9
-'<='=23
-'=='=19
-'>'=20
-'>='=21
-'>>'=10
-'>>>'=11
-'?'=25
-'^'=12
-'|'=8
-'||'=15
-'~'=7
+LP=1
+RP=2
+COMMA=3
+BOOLNOT=4
+BWNOT=5
+MUL=6
+DIV=7
+REM=8
+ADD=9
+SUB=10
+LSH=11
+RSH=12
+USH=13
+LT=14
+LTE=15
+GT=16
+GTE=17
+EQ=18
+NE=19
+BWAND=20
+BWXOR=21
+BWOR=22
+BOOLAND=23
+BOOLOR=24
+COND=25
+COLON=26
+WS=27
+VARIABLE=28
+OCTAL=29
+HEX=30
+DECIMAL=31
+'<<'=11
+'>>'=12
+'>>>'=13
+'<='=15
+'>='=17
+'=='=18
+'!='=19
+'&&'=23
+'||'=24

Added: lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptBaseVisitor.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptBaseVisitor.java?rev=1694614&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptBaseVisitor.java (added)
+++ lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptBaseVisitor.java Fri Aug  7 07:10:40 2015
@@ -0,0 +1,127 @@
+// ANTLR GENERATED CODE: DO NOT EDIT
+package org.apache.lucene.expressions.js;
+import org.antlr.v4.runtime.misc.NotNull;
+import org.antlr.v4.runtime.tree.AbstractParseTreeVisitor;
+
+/**
+ * This class provides an empty implementation of {@link JavascriptVisitor},
+ * which can be extended to create a visitor which only needs to handle a subset
+ * of the available methods.
+ *
+ * @param <T> The return type of the visit operation. Use {@link Void} for
+ * operations with no return type.
+ */
+class JavascriptBaseVisitor<T> extends AbstractParseTreeVisitor<T> implements JavascriptVisitor<T> {
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitCompile(JavascriptParser.CompileContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitConditional(JavascriptParser.ConditionalContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitBoolor(JavascriptParser.BoolorContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitBoolcomp(JavascriptParser.BoolcompContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitNumeric(JavascriptParser.NumericContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitAddsub(JavascriptParser.AddsubContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitUnary(JavascriptParser.UnaryContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitPrecedence(JavascriptParser.PrecedenceContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitMuldiv(JavascriptParser.MuldivContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitExternal(JavascriptParser.ExternalContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitBwshift(JavascriptParser.BwshiftContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitBwor(JavascriptParser.BworContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitBooland(JavascriptParser.BoolandContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitBwxor(JavascriptParser.BwxorContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitBwand(JavascriptParser.BwandContext ctx) { return visitChildren(ctx); }
+  /**
+   * {@inheritDoc}
+   *
+   * <p>The default implementation returns the result of calling
+   * {@link #visitChildren} on {@code ctx}.</p>
+   */
+  @Override public T visitBooleqne(JavascriptParser.BooleqneContext ctx) { return visitChildren(ctx); }
+}

Modified: lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java?rev=1694614&r1=1694613&r2=1694614&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java (original)
+++ lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java Fri Aug  7 07:10:40 2015
@@ -24,18 +24,18 @@ import java.lang.reflect.Method;
 import java.lang.reflect.Modifier;
 import java.nio.charset.StandardCharsets;
 import java.text.ParseException;
+import java.util.ArrayDeque;
 import java.util.Arrays;
 import java.util.Collections;
+import java.util.Deque;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.Map;
 import java.util.Properties;
 
-import org.antlr.runtime.ANTLRStringStream;
-import org.antlr.runtime.CharStream;
-import org.antlr.runtime.CommonTokenStream;
-import org.antlr.runtime.RecognitionException;
-import org.antlr.runtime.tree.Tree;
+import org.antlr.v4.runtime.ANTLRInputStream;
+import org.antlr.v4.runtime.CommonTokenStream;
+import org.antlr.v4.runtime.tree.ParseTree;
 import org.apache.lucene.expressions.Expression;
 import org.apache.lucene.queries.function.FunctionValues;
 import org.apache.lucene.util.IOUtils;
@@ -45,6 +45,8 @@ import org.objectweb.asm.Opcodes;
 import org.objectweb.asm.Type;
 import org.objectweb.asm.commons.GeneratorAdapter;
 
+import static org.apache.lucene.expressions.js.JavascriptParser.ExpressionContext;
+
 /**
  * An expression compiler for javascript expressions.
  * <p>
@@ -73,7 +75,6 @@ import org.objectweb.asm.commons.Generat
  * @lucene.experimental
  */
 public class JavascriptCompiler {
-
   static final class Loader extends ClassLoader {
     Loader(ClassLoader parent) {
       super(parent);
@@ -91,13 +92,14 @@ public class JavascriptCompiler {
   private static final String COMPILED_EXPRESSION_CLASS = JavascriptCompiler.class.getName() + "$CompiledExpression";
   private static final String COMPILED_EXPRESSION_INTERNAL = COMPILED_EXPRESSION_CLASS.replace('.', '/');
   
-  private static final Type EXPRESSION_TYPE = Type.getType(Expression.class);
-  private static final Type FUNCTION_VALUES_TYPE = Type.getType(FunctionValues.class);
+  static final Type EXPRESSION_TYPE = Type.getType(Expression.class);
+  static final Type FUNCTION_VALUES_TYPE = Type.getType(FunctionValues.class);
 
   private static final org.objectweb.asm.commons.Method
     EXPRESSION_CTOR = getMethod("void <init>(String, String[])"),
-    EVALUATE_METHOD = getMethod("double evaluate(int, " + FunctionValues.class.getName() + "[])"),
-    DOUBLE_VAL_METHOD = getMethod("double doubleVal(int)");
+    EVALUATE_METHOD = getMethod("double evaluate(int, " + FunctionValues.class.getName() + "[])");
+
+  static final org.objectweb.asm.commons.Method DOUBLE_VAL_METHOD = getMethod("double doubleVal(int)");
   
   // to work around import clash:
   private static org.objectweb.asm.commons.Method getMethod(String method) {
@@ -108,12 +110,12 @@ public class JavascriptCompiler {
   // rcmuir: "If your ranking function is that large you need to check yourself into a mental institution!"
   private static final int MAX_SOURCE_LENGTH = 16384;
   
-  private final String sourceText;
-  private final Map<String, Integer> externalsMap = new LinkedHashMap<>();
-  private final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
-  private GeneratorAdapter gen;
+  final String sourceText;
+  final Map<String, Integer> externalsMap = new LinkedHashMap<>();
+  final ClassWriter classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS);
+  GeneratorAdapter gen;
   
-  private final Map<String,Method> functions;
+  final Map<String,Method> functions;
   
   /**
    * Compiles the given expression.
@@ -188,21 +190,45 @@ public class JavascriptCompiler {
    */
   private Expression compileExpression(ClassLoader parent) throws ParseException {
     try {
-      Tree antlrTree = getAntlrComputedExpressionTree();
-      
+      ParseTree parseTree = getAntlrParseTree();
+
       beginCompile();
-      recursiveCompile(antlrTree, Type.DOUBLE_TYPE);
+      internalCompile(parseTree);
       endCompile();
       
-      Class<? extends Expression> evaluatorClass = new Loader(parent)
+      final Class<? extends Expression> evaluatorClass = new Loader(parent)
         .define(COMPILED_EXPRESSION_CLASS, classWriter.toByteArray());
-      Constructor<? extends Expression> constructor = evaluatorClass.getConstructor(String.class, String[].class);
+      final Constructor<? extends Expression> constructor = evaluatorClass.getConstructor(String.class, String[].class);
+
       return constructor.newInstance(sourceText, externalsMap.keySet().toArray(new String[externalsMap.size()]));
     } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException exception) {
       throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + sourceText + ").", exception);
     }
   }
-  
+
+  /**
+   * Parses the sourceText into an ANTLR 4 parse tree
+   *
+   * @return The ANTLR parse tree
+   * @throws ParseException on failure to parse
+   */
+  private ParseTree getAntlrParseTree() throws ParseException {
+    try {
+      final ANTLRInputStream antlrInputStream = new ANTLRInputStream(sourceText);
+      final JavascriptErrorHandlingLexer javascriptLexer = new JavascriptErrorHandlingLexer(antlrInputStream);
+      javascriptLexer.removeErrorListeners();
+      final JavascriptParser javascriptParser = new JavascriptParser(new CommonTokenStream(javascriptLexer));
+      javascriptParser.removeErrorListeners();
+      javascriptParser.setErrorHandler(new JavascriptParserErrorStrategy());
+      return javascriptParser.compile();
+    } catch (RuntimeException re) {
+      if (re.getCause() instanceof ParseException) {
+        throw (ParseException)re.getCause();
+      }
+      throw re;
+    }
+  }
+
   private void beginCompile() {
     classWriter.visit(CLASSFILE_VERSION,
         Opcodes.ACC_PUBLIC | Opcodes.ACC_SUPER | Opcodes.ACC_FINAL | Opcodes.ACC_SYNTHETIC,
@@ -223,253 +249,431 @@ public class JavascriptCompiler {
     gen = new GeneratorAdapter(Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC,
         EVALUATE_METHOD, null, null, classWriter);
   }
-  
-  private void recursiveCompile(Tree current, Type expected) {
-    int type = current.getType();
-    String text = current.getText();
-    
-    switch (type) {
-      case JavascriptParser.AT_CALL:
-        Tree identifier = current.getChild(0);
-        String call = identifier.getText();
-        int arguments = current.getChildCount() - 1;
-        
-        Method method = functions.get(call);
-        if (method == null && (arguments > 0 || !call.contains("."))) {
-          throw new IllegalArgumentException("Unrecognized function call (" + call + ").");
-        } else if (method != null) {
+
+  // internalCompile is used to create an anonymous inner class around the ANTLR listener
+  // to completely hide the implementation details of expression compilation
+  private void internalCompile(ParseTree parseTree) {
+    new JavascriptBaseVisitor<Void>() {
+      private final Deque<Type> typeStack = new ArrayDeque<>();
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitCompile(JavascriptParser.CompileContext ctx) {
+        typeStack.push(Type.DOUBLE_TYPE);
+        visit(ctx.expression());
+        typeStack.pop();
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitPrecedence(JavascriptParser.PrecedenceContext ctx) {
+        visit(ctx.expression());
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitNumeric(JavascriptParser.NumericContext ctx) {
+        if (ctx.HEX() != null) {
+          pushLong(Long.parseLong(ctx.HEX().getText().substring(2), 16));
+        } else if (ctx.OCTAL() != null) {
+          pushLong(Long.parseLong(ctx.OCTAL().getText().substring(1), 8));
+        } else if (ctx.DECIMAL() != null) {
+          gen.push(Double.parseDouble(ctx.DECIMAL().getText()));
+          gen.cast(Type.DOUBLE_TYPE, typeStack.peek());
+        } else {
+          throw new IllegalStateException("Unknown operation specified: " + ctx.getText());
+        }
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitExternal(JavascriptParser.ExternalContext ctx) {
+        String text = ctx.VARIABLE().getText();
+        int arguments = ctx.expression().size();
+        boolean parens = ctx.LP() != null && ctx.RP() != null;
+        Method method = parens ? functions.get(text) : null;
+
+        if (method != null) {
           int arity = method.getParameterTypes().length;
+
           if (arguments != arity) {
-            throw new IllegalArgumentException("Expected (" + arity + ") arguments for function call (" +
-                call + "), but found (" + arguments + ").");
+            throw new IllegalArgumentException(
+                "Expected (" + arity + ") arguments for function call (" + text + "), but found (" + arguments + ").");
           }
 
-          for (int argument = 1; argument <= arguments; ++argument) {
-            recursiveCompile(current.getChild(argument), Type.DOUBLE_TYPE);
+          typeStack.push(Type.DOUBLE_TYPE);
+
+          for (int argument = 0; argument < arguments; ++argument) {
+            visit(ctx.expression(argument));
           }
 
+          typeStack.pop();
+
           gen.invokeStatic(Type.getType(method.getDeclaringClass()),
               org.objectweb.asm.commons.Method.getMethod(method));
 
-          gen.cast(Type.DOUBLE_TYPE, expected);
-          break;
+          gen.cast(Type.DOUBLE_TYPE, typeStack.peek());
+        } else if (!parens || arguments == 0 && text.contains(".")) {
+          int index;
+
+          text = normalizeQuotes(ctx.getText());
+
+          if (externalsMap.containsKey(text)) {
+            index = externalsMap.get(text);
+          } else {
+            index = externalsMap.size();
+            externalsMap.put(text, index);
+          }
+
+          gen.loadArg(1);
+          gen.push(index);
+          gen.arrayLoad(FUNCTION_VALUES_TYPE);
+          gen.loadArg(0);
+          gen.invokeVirtual(FUNCTION_VALUES_TYPE, DOUBLE_VAL_METHOD);
+          gen.cast(Type.DOUBLE_TYPE, typeStack.peek());
+        } else {
+          throw new IllegalArgumentException("Unrecognized function call (" + text + ").");
+        }
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitUnary(JavascriptParser.UnaryContext ctx) {
+        if (ctx.BOOLNOT() != null) {
+          Label labelNotTrue = new Label();
+          Label labelNotReturn = new Label();
+
+          typeStack.push(Type.INT_TYPE);
+          visit(ctx.expression());
+          typeStack.pop();
+          gen.visitJumpInsn(Opcodes.IFEQ, labelNotTrue);
+          pushBoolean(false);
+          gen.goTo(labelNotReturn);
+          gen.visitLabel(labelNotTrue);
+          pushBoolean(true);
+          gen.visitLabel(labelNotReturn);
+
+        } else if (ctx.BWNOT() != null) {
+          typeStack.push(Type.LONG_TYPE);
+          visit(ctx.expression());
+          typeStack.pop();
+          gen.push(-1L);
+          gen.visitInsn(Opcodes.LXOR);
+          gen.cast(Type.LONG_TYPE, typeStack.peek());
+
+        } else if (ctx.ADD() != null) {
+          visit(ctx.expression());
+
+        } else if (ctx.SUB() != null) {
+          typeStack.push(Type.DOUBLE_TYPE);
+          visit(ctx.expression());
+          typeStack.pop();
+          gen.visitInsn(Opcodes.DNEG);
+          gen.cast(Type.DOUBLE_TYPE, typeStack.peek());
+
         } else {
-          text = call + "()";
-          // intentionally fall through to the variable case to allow this non-static
-          // method to be forwarded to the bindings for processing
-        }
-      case JavascriptParser.VARIABLE:
-        int index;
-
-        text = normalizeQuotes(text);
-        
-        if (externalsMap.containsKey(text)) {
-          index = externalsMap.get(text);
+          throw new IllegalStateException("Unknown operation specified: " + ctx.getText());
+        }
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitMuldiv(JavascriptParser.MuldivContext ctx) {
+        int opcode;
+
+        if (ctx.MUL() != null) {
+          opcode = Opcodes.DMUL;
+        } else if (ctx.DIV() != null) {
+          opcode = Opcodes.DDIV;
+        } else if (ctx.REM() != null) {
+          opcode = Opcodes.DREM;
+        } else {
+          throw new IllegalStateException("Unknown operation specified: " + ctx.getText());
+        }
+
+        pushArith(opcode, ctx.expression(0), ctx.expression(1));
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitAddsub(JavascriptParser.AddsubContext ctx) {
+        int opcode;
+
+        if (ctx.ADD() != null) {
+          opcode = Opcodes.DADD;
+        } else if (ctx.SUB() != null) {
+          opcode = Opcodes.DSUB;
+        } else {
+          throw new IllegalStateException("Unknown operation specified: " + ctx.getText());
+        }
+
+        pushArith(opcode, ctx.expression(0), ctx.expression(1));
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitBwshift(JavascriptParser.BwshiftContext ctx) {
+        int opcode;
+
+        if (ctx.LSH() != null) {
+          opcode = Opcodes.LSHL;
+        } else if (ctx.RSH() != null) {
+          opcode = Opcodes.LSHR;
+        } else if (ctx.USH() != null) {
+          opcode = Opcodes.LUSHR;
+        } else {
+          throw new IllegalStateException("Unknown operation specified: " + ctx.getText());
+        }
+
+        pushShift(opcode, ctx.expression(0), ctx.expression(1));
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitBoolcomp(JavascriptParser.BoolcompContext ctx) {
+        int opcode;
+
+        if (ctx.LT() != null) {
+          opcode = GeneratorAdapter.LT;
+        } else if (ctx.LTE() != null) {
+          opcode = GeneratorAdapter.LE;
+        } else if (ctx.GT() != null) {
+          opcode = GeneratorAdapter.GT;
+        } else if (ctx.GTE() != null) {
+          opcode = GeneratorAdapter.GE;
+        } else {
+          throw new IllegalStateException("Unknown operation specified: " + ctx.getText());
+        }
+
+        pushCond(opcode, ctx.expression(0), ctx.expression(1));
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitBooleqne(JavascriptParser.BooleqneContext ctx) {
+        int opcode;
+
+        if (ctx.EQ() != null) {
+          opcode = GeneratorAdapter.EQ;
+        } else if (ctx.NE() != null) {
+          opcode = GeneratorAdapter.NE;
         } else {
-          index = externalsMap.size();
-          externalsMap.put(text, index);
+          throw new IllegalStateException("Unknown operation specified: " + ctx.getText());
         }
-        
-        gen.loadArg(1);
-        gen.push(index);
-        gen.arrayLoad(FUNCTION_VALUES_TYPE);
-        gen.loadArg(0);
-        gen.invokeVirtual(FUNCTION_VALUES_TYPE, DOUBLE_VAL_METHOD);
-        gen.cast(Type.DOUBLE_TYPE, expected);
-        break;
-      case JavascriptParser.HEX:
-        pushLong(expected, Long.parseLong(text.substring(2), 16));
-        break;
-      case JavascriptParser.OCTAL:
-        pushLong(expected, Long.parseLong(text.substring(1), 8));
-        break;
-      case JavascriptParser.DECIMAL:
-        gen.push(Double.parseDouble(text));
-        gen.cast(Type.DOUBLE_TYPE, expected);
-        break;
-      case JavascriptParser.AT_NEGATE:
-        recursiveCompile(current.getChild(0), Type.DOUBLE_TYPE);
-        gen.visitInsn(Opcodes.DNEG);
-        gen.cast(Type.DOUBLE_TYPE, expected);
-        break;
-      case JavascriptParser.AT_ADD:
-        pushArith(Opcodes.DADD, current, expected);
-        break;
-      case JavascriptParser.AT_SUBTRACT:
-        pushArith(Opcodes.DSUB, current, expected);
-        break;
-      case JavascriptParser.AT_MULTIPLY:
-        pushArith(Opcodes.DMUL, current, expected);
-        break;
-      case JavascriptParser.AT_DIVIDE:
-        pushArith(Opcodes.DDIV, current, expected);
-        break;
-      case JavascriptParser.AT_MODULO:
-        pushArith(Opcodes.DREM, current, expected);
-        break;
-      case JavascriptParser.AT_BIT_SHL:
-        pushShift(Opcodes.LSHL, current, expected);
-        break;
-      case JavascriptParser.AT_BIT_SHR:
-        pushShift(Opcodes.LSHR, current, expected);
-        break;
-      case JavascriptParser.AT_BIT_SHU:
-        pushShift(Opcodes.LUSHR, current, expected);
-        break;
-      case JavascriptParser.AT_BIT_AND:
-        pushBitwise(Opcodes.LAND, current, expected);
-        break;
-      case JavascriptParser.AT_BIT_OR:
-        pushBitwise(Opcodes.LOR, current, expected);           
-        break;
-      case JavascriptParser.AT_BIT_XOR:
-        pushBitwise(Opcodes.LXOR, current, expected);           
-        break;
-      case JavascriptParser.AT_BIT_NOT:
-        recursiveCompile(current.getChild(0), Type.LONG_TYPE);
-        gen.push(-1L);
-        gen.visitInsn(Opcodes.LXOR);
-        gen.cast(Type.LONG_TYPE, expected);
-        break;
-      case JavascriptParser.AT_COMP_EQ:
-        pushCond(GeneratorAdapter.EQ, current, expected);
-        break;
-      case JavascriptParser.AT_COMP_NEQ:
-        pushCond(GeneratorAdapter.NE, current, expected);
-        break;
-      case JavascriptParser.AT_COMP_LT:
-        pushCond(GeneratorAdapter.LT, current, expected);
-        break;
-      case JavascriptParser.AT_COMP_GT:
-        pushCond(GeneratorAdapter.GT, current, expected);
-        break;
-      case JavascriptParser.AT_COMP_LTE:
-        pushCond(GeneratorAdapter.LE, current, expected);
-        break;
-      case JavascriptParser.AT_COMP_GTE:
-        pushCond(GeneratorAdapter.GE, current, expected);
-        break;
-      case JavascriptParser.AT_BOOL_NOT:
-        Label labelNotTrue = new Label();
-        Label labelNotReturn = new Label();
-        
-        recursiveCompile(current.getChild(0), Type.INT_TYPE);
-        gen.visitJumpInsn(Opcodes.IFEQ, labelNotTrue);
-        pushBoolean(expected, false);
-        gen.goTo(labelNotReturn);
-        gen.visitLabel(labelNotTrue);
-        pushBoolean(expected, true);
-        gen.visitLabel(labelNotReturn);
-        break;
-      case JavascriptParser.AT_BOOL_AND:
+
+        pushCond(opcode, ctx.expression(0), ctx.expression(1));
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitBwand(JavascriptParser.BwandContext ctx) {
+        pushBitwise(Opcodes.LAND, ctx.expression(0), ctx.expression(1));
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitBwxor(JavascriptParser.BwxorContext ctx) {
+        pushBitwise(Opcodes.LXOR, ctx.expression(0), ctx.expression(1));
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitBwor(JavascriptParser.BworContext ctx) {
+        pushBitwise(Opcodes.LOR, ctx.expression(0), ctx.expression(1));
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitBooland(JavascriptParser.BoolandContext ctx) {
         Label andFalse = new Label();
         Label andEnd = new Label();
-        
-        recursiveCompile(current.getChild(0), Type.INT_TYPE);
+
+        typeStack.push(Type.INT_TYPE);
+        visit(ctx.expression(0));
         gen.visitJumpInsn(Opcodes.IFEQ, andFalse);
-        recursiveCompile(current.getChild(1), Type.INT_TYPE);
+        visit(ctx.expression(1));
         gen.visitJumpInsn(Opcodes.IFEQ, andFalse);
-        pushBoolean(expected, true);
+        typeStack.pop();
+        pushBoolean(true);
         gen.goTo(andEnd);
         gen.visitLabel(andFalse);
-        pushBoolean(expected, false);
+        pushBoolean(false);
         gen.visitLabel(andEnd);
-        break;
-      case JavascriptParser.AT_BOOL_OR:
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitBoolor(JavascriptParser.BoolorContext ctx) {
         Label orTrue = new Label();
         Label orEnd = new Label();
-        
-        recursiveCompile(current.getChild(0), Type.INT_TYPE);
+
+        typeStack.push(Type.INT_TYPE);
+        visit(ctx.expression(0));
         gen.visitJumpInsn(Opcodes.IFNE, orTrue);
-        recursiveCompile(current.getChild(1), Type.INT_TYPE);
+        visit(ctx.expression(1));
         gen.visitJumpInsn(Opcodes.IFNE, orTrue);
-        pushBoolean(expected, false);
+        typeStack.pop();
+        pushBoolean(false);
         gen.goTo(orEnd);
         gen.visitLabel(orTrue);
-        pushBoolean(expected, true);
+        pushBoolean(true);
         gen.visitLabel(orEnd);
-        break;
-      case JavascriptParser.AT_COND_QUE:
+
+        return null;
+      }
+
+      /**
+       * For internal compiler use only, do NOT use
+       */
+      @Override
+      public Void visitConditional(JavascriptParser.ConditionalContext ctx) {
         Label condFalse = new Label();
         Label condEnd = new Label();
-        
-        recursiveCompile(current.getChild(0), Type.INT_TYPE);
+
+        typeStack.push(Type.INT_TYPE);
+        visit(ctx.expression(0));
+        typeStack.pop();
         gen.visitJumpInsn(Opcodes.IFEQ, condFalse);
-        recursiveCompile(current.getChild(1), expected);
+        visit(ctx.expression(1));
         gen.goTo(condEnd);
         gen.visitLabel(condFalse);
-        recursiveCompile(current.getChild(2), expected);
+        visit(ctx.expression(2));
         gen.visitLabel(condEnd);
-        break;
-      default:
-        throw new IllegalStateException("Unknown operation specified: (" + current.getText() + ").");
-    }
-  }
 
-  private void pushArith(int operator, Tree current, Type expected) {
-    pushBinaryOp(operator, current, expected, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE);
-  }
-  
-  private void pushShift(int operator, Tree current, Type expected) {
-    pushBinaryOp(operator, current, expected, Type.LONG_TYPE, Type.INT_TYPE, Type.LONG_TYPE);
-  }
-  
-  private void pushBitwise(int operator, Tree current, Type expected) {
-    pushBinaryOp(operator, current, expected, Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
-  }
-  
-  private void pushBinaryOp(int operator, Tree current, Type expected, Type arg1, Type arg2, Type returnType) {
-    recursiveCompile(current.getChild(0), arg1);
-    recursiveCompile(current.getChild(1), arg2);
-    gen.visitInsn(operator);
-    gen.cast(returnType, expected);
-  }
-  
-  private void pushCond(int operator, Tree current, Type expected) {
-    Label labelTrue = new Label();
-    Label labelReturn = new Label();
-    
-    recursiveCompile(current.getChild(0), Type.DOUBLE_TYPE);
-    recursiveCompile(current.getChild(1), Type.DOUBLE_TYPE);
-    
-    gen.ifCmp(Type.DOUBLE_TYPE, operator, labelTrue);
-    pushBoolean(expected, false);
-    gen.goTo(labelReturn);
-    gen.visitLabel(labelTrue);
-    pushBoolean(expected, true);
-    gen.visitLabel(labelReturn);    
-  }
-  
-  private void pushBoolean(Type expected, boolean truth) {
-    switch (expected.getSort()) {
-      case Type.INT:
-        gen.push(truth);
-        break;
-      case Type.LONG:
-        gen.push(truth ? 1L : 0L);
-        break;
-      case Type.DOUBLE:
-        gen.push(truth ? 1. : 0.);
-        break;
-      default:
-        throw new IllegalStateException("Invalid expected type: " + expected);
-    }
-  }
-  
-  private void pushLong(Type expected, long i) {
-    switch (expected.getSort()) {
-      case Type.INT:
-        gen.push((int) i);
-        break;
-      case Type.LONG:
-        gen.push(i);
-        break;
-      case Type.DOUBLE:
-        gen.push((double) i);
-        break;
-      default:
-        throw new IllegalStateException("Invalid expected type: " + expected);
-    }
+        return null;
+      }
+
+      private void pushArith(int operator, ExpressionContext left, ExpressionContext right) {
+        pushBinaryOp(operator, left, right, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE, Type.DOUBLE_TYPE);
+      }
+
+      private void pushShift(int operator, ExpressionContext left, ExpressionContext right) {
+        pushBinaryOp(operator, left, right, Type.LONG_TYPE, Type.INT_TYPE, Type.LONG_TYPE);
+      }
+
+      private void pushBitwise(int operator, ExpressionContext left, ExpressionContext right) {
+        pushBinaryOp(operator, left, right, Type.LONG_TYPE, Type.LONG_TYPE, Type.LONG_TYPE);
+      }
+
+      private void pushBinaryOp(int operator, ExpressionContext left, ExpressionContext right,
+                                Type leftType, Type rightType, Type returnType) {
+        typeStack.push(leftType);
+        visit(left);
+        typeStack.pop();
+        typeStack.push(rightType);
+        visit(right);
+        typeStack.pop();
+        gen.visitInsn(operator);
+        gen.cast(returnType, typeStack.peek());
+      }
+
+      private void pushCond(int operator, ExpressionContext left, ExpressionContext right) {
+        Label labelTrue = new Label();
+        Label labelReturn = new Label();
+
+        typeStack.push(Type.DOUBLE_TYPE);
+        visit(left);
+        visit(right);
+        typeStack.pop();
+
+        gen.ifCmp(Type.DOUBLE_TYPE, operator, labelTrue);
+        pushBoolean(false);
+        gen.goTo(labelReturn);
+        gen.visitLabel(labelTrue);
+        pushBoolean(true);
+        gen.visitLabel(labelReturn);
+      }
+
+      private void pushBoolean(boolean truth) {
+        switch (typeStack.peek().getSort()) {
+          case Type.INT:
+            gen.push(truth);
+            break;
+          case Type.LONG:
+            gen.push(truth ? 1L : 0L);
+            break;
+          case Type.DOUBLE:
+            gen.push(truth ? 1. : 0.);
+            break;
+          default:
+            throw new IllegalStateException("Invalid expected type: " + typeStack.peek());
+        }
+      }
+
+      private void pushLong(long i) {
+        switch (typeStack.peek().getSort()) {
+          case Type.INT:
+            gen.push((int) i);
+            break;
+          case Type.LONG:
+            gen.push(i);
+            break;
+          case Type.DOUBLE:
+            gen.push((double) i);
+            break;
+          default:
+            throw new IllegalStateException("Invalid expected type: " + typeStack.peek());
+        }
+      }
+    }.visit(parseTree);
   }
   
   private void endCompile() {
@@ -479,26 +683,7 @@ public class JavascriptCompiler {
     classWriter.visitEnd();
   }
 
-  private Tree getAntlrComputedExpressionTree() throws ParseException {
-    CharStream input = new ANTLRStringStream(sourceText);
-    JavascriptLexer lexer = new JavascriptLexer(input);
-    CommonTokenStream tokens = new CommonTokenStream(lexer);
-    JavascriptParser parser = new JavascriptParser(tokens);
-
-    try {
-      return parser.expression().tree;
-
-    } catch (RecognitionException exception) {
-      throw new IllegalArgumentException(exception);
-    } catch (RuntimeException exception) {
-      if (exception.getCause() instanceof ParseException) {
-        throw (ParseException)exception.getCause();
-      }
-      throw exception;
-    }
-  }
-
-  private static String normalizeQuotes(String text) {
+  static String normalizeQuotes(String text) {
     StringBuilder out = new StringBuilder(text.length());
     boolean inDoubleQuotes = false;
     for (int i = 0; i < text.length(); ++i) {
@@ -527,7 +712,7 @@ public class JavascriptCompiler {
     return out.toString();
   }
 
-  private static int findSingleQuoteStringEnd(String text, int start) {
+  static int findSingleQuoteStringEnd(String text, int start) {
     ++start; // skip beginning
     while (text.charAt(start) != '\'') {
       if (text.charAt(start) == '\\') {

Added: lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptErrorHandlingLexer.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptErrorHandlingLexer.java?rev=1694614&view=auto
==============================================================================
--- lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptErrorHandlingLexer.java (added)
+++ lucene/dev/trunk/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptErrorHandlingLexer.java Fri Aug  7 07:10:40 2015
@@ -0,0 +1,53 @@
+package org.apache.lucene.expressions.js;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.text.ParseException;
+
+import org.antlr.v4.runtime.CharStream;
+import org.antlr.v4.runtime.LexerNoViableAltException;
+import org.antlr.v4.runtime.misc.Interval;
+
+/**
+ * Overrides the ANTLR 4 generated JavascriptLexer to allow for proper error handling
+ */
+class JavascriptErrorHandlingLexer extends JavascriptLexer {
+  /**
+   * Constructor for JavascriptErrorHandlingLexer
+   * @param charStream the stream for the source text
+   */
+  public JavascriptErrorHandlingLexer(CharStream charStream) {
+    super(charStream);
+  }
+
+  /**
+   * Ensures the ANTLR lexer will throw an exception after the first error
+   * @param lnvae the lexer exception
+   */
+  @Override
+  public void recover(LexerNoViableAltException lnvae) {
+    CharStream charStream = lnvae.getInputStream();
+    int startIndex = lnvae.getStartIndex();
+    String text = charStream.getText(Interval.of(startIndex, charStream.index()));
+
+    ParseException parseException = new ParseException("unexpected character '" + getErrorDisplay(text) + "'" +
+        " on line (" + _tokenStartLine + ") position (" + _tokenStartCharPositionInLine + ")", _tokenStartCharIndex);
+    parseException.initCause(lnvae);
+    throw new RuntimeException(parseException);
+  }
+}