LUCENE-6417: Upgrade ANTLR used in expressions module to version 4.5

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1694614 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Uwe Schindler 2015-08-07 07:10:40 +00:00
parent b3e0a10336
commit 7c9d78a2c4
25 changed files with 1942 additions and 4894 deletions

View File

@ -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

View File

@ -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>

View File

@ -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}"/>

View File

@ -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.
*/
/*
* ANTLRv4 grammar for the Lucene expressions language
*/
// ***********************************************************************
// * 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
// ***********************************************************************
compile
: expression EOF
;
expression
: conditional EOF!
: 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
;
conditional
: logical_or (AT_COND_QUE^ conditional AT_COLON! 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
: ['] ( '\\\'' | '\\\\' | ~[\\'] )*? [']
| ["] ( '\\"' | '\\\\' | ~[\\"] )*? ["]
;
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+
;
fragment
DECIMALDIGIT
: '0'..'9'
;
fragment
HEXDIGIT
: DECIMALDIGIT
| 'a'..'f'
| 'A'..'F'
;
fragment
OCTALDIGIT
: '0'..'7'
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]*
;

View File

@ -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

View File

@ -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); }
}

View File

@ -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.GeneratorAdapter;
* @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;
} 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;
gen.cast(Type.DOUBLE_TYPE, typeStack.peek());
} else if (!parens || arguments == 0 && text.contains(".")) {
int index;
text = normalizeQuotes(text);
if (externalsMap.containsKey(text)) {
index = externalsMap.get(text);
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 {
index = externalsMap.size();
externalsMap.put(text, index);
throw new IllegalArgumentException("Unrecognized function call (" + text + ").");
}
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:
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 {
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 {
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 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) == '\\') {

View File

@ -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);
}
}

View File

@ -0,0 +1,40 @@
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

View File

@ -0,0 +1,92 @@
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.DefaultErrorStrategy;
import org.antlr.v4.runtime.InputMismatchException;
import org.antlr.v4.runtime.NoViableAltException;
import org.antlr.v4.runtime.Parser;
import org.antlr.v4.runtime.RecognitionException;
import org.antlr.v4.runtime.Token;
/**
* Allows for proper error handling in the ANTLR 4 parser
*/
class JavascriptParserErrorStrategy extends DefaultErrorStrategy {
/**
* Ensures the ANTLR parser will throw an exception after the first error
*
* @param recognizer the parser being used
* @param re the original exception from the parser
*/
@Override
public void recover(Parser recognizer, RecognitionException re) {
Token token = re.getOffendingToken();
String message;
if (token == null) {
message = "error " + getTokenErrorDisplay(token);
} else if (re instanceof InputMismatchException) {
message = "unexpected token " + getTokenErrorDisplay(token) +
" on line (" + token.getLine() + ") position (" + token.getCharPositionInLine() + ")" +
" was expecting one of " + re.getExpectedTokens().toString(recognizer.getVocabulary());
} else if (re instanceof NoViableAltException) {
if (token.getType() == JavascriptParser.EOF) {
message = "unexpected end of expression";
} else {
message = "invalid sequence of tokens near " + getTokenErrorDisplay(token) +
" on line (" + token.getLine() + ") position (" + token.getCharPositionInLine() + ")";
}
} else {
message = " unexpected token near " + getTokenErrorDisplay(token) +
" on line (" + token.getLine() + ") position (" + token.getCharPositionInLine() + ")";
}
ParseException parseException = new ParseException(message, token.getStartIndex());
parseException.initCause(re);
throw new RuntimeException(parseException);
}
/**
* Ensures the ANTLR parser will throw an exception after the first error
*
* @param recognizer the parser being used
* @return no actual return value
* @throws RecognitionException not used as a ParseException wrapped in a RuntimeException is thrown instead
*/
@Override
public Token recoverInline(Parser recognizer) throws RecognitionException {
Token token = recognizer.getCurrentToken();
String message = "unexpected token " + getTokenErrorDisplay(token) +
" on line (" + token.getLine() + ") position (" + token.getCharPositionInLine() + ")" +
" was expecting one of " + recognizer.getExpectedTokens().toString(recognizer.getVocabulary());
ParseException parseException = new ParseException(message, token.getStartIndex());
throw new RuntimeException(parseException);
}
/**
* Do not allow syncing after errors to ensure the ANTLR parser will throw an exception
*
* @param recognizer the parser being used
*/
@Override
public void sync(Parser recognizer) {
}
}

View File

@ -0,0 +1,125 @@
// 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.ParseTreeVisitor;
/**
* This interface defines a complete generic visitor for a parse tree produced
* by {@link JavascriptParser}.
*
* @param <T> The return type of the visit operation. Use {@link Void} for
* operations with no return type.
*/
interface JavascriptVisitor<T> extends ParseTreeVisitor<T> {
/**
* Visit a parse tree produced by {@link JavascriptParser#compile}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitCompile(JavascriptParser.CompileContext ctx);
/**
* Visit a parse tree produced by the {@code conditional}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitConditional(JavascriptParser.ConditionalContext ctx);
/**
* Visit a parse tree produced by the {@code boolor}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBoolor(JavascriptParser.BoolorContext ctx);
/**
* Visit a parse tree produced by the {@code boolcomp}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBoolcomp(JavascriptParser.BoolcompContext ctx);
/**
* Visit a parse tree produced by the {@code numeric}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitNumeric(JavascriptParser.NumericContext ctx);
/**
* Visit a parse tree produced by the {@code addsub}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitAddsub(JavascriptParser.AddsubContext ctx);
/**
* Visit a parse tree produced by the {@code unary}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitUnary(JavascriptParser.UnaryContext ctx);
/**
* Visit a parse tree produced by the {@code precedence}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitPrecedence(JavascriptParser.PrecedenceContext ctx);
/**
* Visit a parse tree produced by the {@code muldiv}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitMuldiv(JavascriptParser.MuldivContext ctx);
/**
* Visit a parse tree produced by the {@code external}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitExternal(JavascriptParser.ExternalContext ctx);
/**
* Visit a parse tree produced by the {@code bwshift}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBwshift(JavascriptParser.BwshiftContext ctx);
/**
* Visit a parse tree produced by the {@code bwor}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBwor(JavascriptParser.BworContext ctx);
/**
* Visit a parse tree produced by the {@code booland}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBooland(JavascriptParser.BoolandContext ctx);
/**
* Visit a parse tree produced by the {@code bwxor}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBwxor(JavascriptParser.BwxorContext ctx);
/**
* Visit a parse tree produced by the {@code bwand}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBwand(JavascriptParser.BwandContext ctx);
/**
* Visit a parse tree produced by the {@code booleqne}
* labeled alternative in {@link JavascriptParser#expression}.
* @param ctx the parse tree
* @return the visitor result
*/
T visitBooleqne(JavascriptParser.BooleqneContext ctx);
}

View File

@ -18,11 +18,12 @@ package org.apache.lucene.expressions.js;
import java.text.ParseException;
import org.antlr.v4.runtime.RecognitionException;
import org.apache.lucene.expressions.Expression;
import org.apache.lucene.util.LuceneTestCase;
public class TestJavascriptCompiler extends LuceneTestCase {
public void testValidCompiles() throws Exception {
assertNotNull(JavascriptCompiler.compile("100"));
assertNotNull(JavascriptCompiler.compile("valid0+100"));
@ -99,6 +100,15 @@ public class TestJavascriptCompiler extends LuceneTestCase {
}
}
public void testInvalidLexer() throws Exception {
try {
JavascriptCompiler.compile("\n .");
fail();
} catch (ParseException pe) {
assertTrue(pe.getMessage().contains("unexpected character '.' on line (2) position (1)"));
}
}
public void testInvalidCompiles() throws Exception {
try {
JavascriptCompiler.compile("100 100");

View File

@ -90,7 +90,6 @@ com.sun.jersey.version = 1.9
/net.sourceforge.argparse4j/argparse4j = 0.4.3
/net.sourceforge.jmatio/jmatio = 1.0
/net.sourceforge.nekohtml/nekohtml = 1.9.17
/org.antlr/antlr-runtime = 3.5
/org.antlr/antlr4-runtime = 4.5
/org.apache.ant/ant = 1.8.2

View File

@ -1 +0,0 @@
0baa82bff19059401e90e1b90020beb9c96305d7

View File

@ -1,7 +0,0 @@
Copyright (c) 2012 Terence Parr and Sam Harwell
All rights reserved.
Redistribution and use 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 the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
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.

View File

@ -1 +0,0 @@

View File

@ -0,0 +1 @@
29e48af049f17dd89153b83a7ad5d01b3b4bcdda

View File

@ -0,0 +1,26 @@
[The "BSD license"]
Copyright (c) 2015 Terence Parr, Sam Harwell
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions
are met:
1. Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
2. 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.
3. The name of the author may not be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``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 AUTHOR 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.

View File

@ -37,7 +37,7 @@
<dependency org="commons-lang" name="commons-lang" rev="${/commons-lang/commons-lang}" conf="compile"/>
<dependency org="com.google.guava" name="guava" rev="${/com.google.guava/guava}" conf="compile"/>
<dependency org="com.spatial4j" name="spatial4j" rev="${/com.spatial4j/spatial4j}" conf="compile"/>
<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}"/>
<dependency org="org.apache.commons" name="commons-math3" rev="${/org.apache.commons/commons-math3}" conf="test"/>
<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"/>
@ -132,7 +132,6 @@
<!-- SQL Parser -->
<dependency org="com.facebook.presto" name="presto-parser" rev="${/com.facebook.presto/presto-parser}"/>
<dependency org="org.antlr" name="antlr4-runtime" rev="${/org.antlr/antlr4-runtime}"/>
<dependency org="io.airlift" name="slice" rev="${/io.airlift/slice}"/>
<exclude org="*" ext="*" matcher="regexp" type="${ivy.exclude.types}"/>

View File

@ -1 +0,0 @@
0baa82bff19059401e90e1b90020beb9c96305d7

View File

@ -1,7 +0,0 @@
Copyright (c) 2012 Terence Parr and Sam Harwell
All rights reserved.
Redistribution and use 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 the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
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.

View File

@ -1 +0,0 @@