You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by rm...@apache.org on 2013/09/13 05:11:18 UTC

svn commit: r1522767 [2/5] - in /lucene/dev/branches/lucene5207: dev-tools/scripts/ lucene/ lucene/core/src/java/org/apache/lucene/util/ lucene/core/src/test/org/apache/lucene/util/ lucene/expressions/ lucene/expressions/src/ lucene/expressions/src/jav...

Added: lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java?rev=1522767&view=auto
==============================================================================
--- lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java (added)
+++ lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptCompiler.java Fri Sep 13 03:11:17 2013
@@ -0,0 +1,630 @@
+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.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicLong;
+
+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.apache.lucene.expressions.Expression;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Label;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+import static org.objectweb.asm.Opcodes.AALOAD;
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+import static org.objectweb.asm.Opcodes.ACC_PUBLIC;
+import static org.objectweb.asm.Opcodes.ACC_SUPER;
+import static org.objectweb.asm.Opcodes.ALOAD;
+import static org.objectweb.asm.Opcodes.BIPUSH;
+import static org.objectweb.asm.Opcodes.D2I;
+import static org.objectweb.asm.Opcodes.D2L;
+import static org.objectweb.asm.Opcodes.DADD;
+import static org.objectweb.asm.Opcodes.DCMPG;
+import static org.objectweb.asm.Opcodes.DCMPL;
+import static org.objectweb.asm.Opcodes.DCONST_0;
+import static org.objectweb.asm.Opcodes.DCONST_1;
+import static org.objectweb.asm.Opcodes.DDIV;
+import static org.objectweb.asm.Opcodes.DNEG;
+import static org.objectweb.asm.Opcodes.DREM;
+import static org.objectweb.asm.Opcodes.DRETURN;
+import static org.objectweb.asm.Opcodes.DSUB;
+import static org.objectweb.asm.Opcodes.GOTO;
+import static org.objectweb.asm.Opcodes.I2D;
+import static org.objectweb.asm.Opcodes.I2L;
+import static org.objectweb.asm.Opcodes.ICONST_0;
+import static org.objectweb.asm.Opcodes.ICONST_1;
+import static org.objectweb.asm.Opcodes.ICONST_2;
+import static org.objectweb.asm.Opcodes.ICONST_3;
+import static org.objectweb.asm.Opcodes.ICONST_4;
+import static org.objectweb.asm.Opcodes.ICONST_5;
+import static org.objectweb.asm.Opcodes.IFEQ;
+import static org.objectweb.asm.Opcodes.IFGE;
+import static org.objectweb.asm.Opcodes.IFGT;
+import static org.objectweb.asm.Opcodes.IFLE;
+import static org.objectweb.asm.Opcodes.IFLT;
+import static org.objectweb.asm.Opcodes.IFNE;
+import static org.objectweb.asm.Opcodes.ILOAD;
+import static org.objectweb.asm.Opcodes.INVOKESPECIAL;
+import static org.objectweb.asm.Opcodes.INVOKESTATIC;
+import static org.objectweb.asm.Opcodes.INVOKEVIRTUAL;
+import static org.objectweb.asm.Opcodes.L2D;
+import static org.objectweb.asm.Opcodes.L2I;
+import static org.objectweb.asm.Opcodes.LAND;
+import static org.objectweb.asm.Opcodes.LCONST_0;
+import static org.objectweb.asm.Opcodes.LCONST_1;
+import static org.objectweb.asm.Opcodes.LOR;
+import static org.objectweb.asm.Opcodes.LSHL;
+import static org.objectweb.asm.Opcodes.LSHR;
+import static org.objectweb.asm.Opcodes.LUSHR;
+import static org.objectweb.asm.Opcodes.LXOR;
+import static org.objectweb.asm.Opcodes.RETURN;
+import static org.objectweb.asm.Opcodes.SIPUSH;
+import static org.objectweb.asm.Opcodes.V1_7;
+
+/**
+ * An expression compiler for javascript expressions.
+ *
+ * @lucene.experimental
+ */
+public class JavascriptCompiler {
+  private static enum ComputedType {
+    INT, LONG, DOUBLE
+  }
+
+  class Loader extends ClassLoader {
+
+    Loader(ClassLoader parent) {
+      super(parent);
+    }
+
+    public Class<? extends Expression> define(String className, byte[] bytecode) {
+      return super.defineClass(className, bytecode, 0, bytecode.length).asSubclass(Expression.class);
+    }
+  }
+  
+  private static final String EXPRESSION_CLASS_PREFIX = JavascriptCompiler.class.getPackage().getName() + ".Computed_";
+  private static final String EXPRESSION_INTERNAL_PREFIX = JavascriptCompiler.class.getPackage().getName().replace(".", "/") + "/Computed_";
+  private static final String COMPILED_EXPRESSION_INTERNAL = Type.getInternalName(Expression.class);
+  private static final String FUNCTION_VALUES_INTERNAL = Type.getInternalName(FunctionValues.class);
+  
+  private Loader loader;
+  private AtomicLong counter = new AtomicLong();
+  
+  private String className;
+  private ClassWriter classWriter;
+  private MethodVisitor methodVisitor;
+  private Map<String, Integer> externalsMap;
+  private List<String> externalsList;
+  
+  /**
+   * Constructs a compiler for expressions.
+   */
+  public JavascriptCompiler() {
+    this(null);
+  }
+
+  /**
+   * Constructs a compiler for expressions that will be loaded using the given class loader as the parent.
+   * @param parent Class loader to load the dynamically compiled expression
+   */
+  public JavascriptCompiler(ClassLoader parent) {
+    if (parent == null) {
+      parent = getClass().getClassLoader();
+    }
+    loader = new Loader(parent);
+  }
+  
+  /**
+   * Compiles the given expression.
+   *
+   * @param expression The expression to compile
+   * @return A new compiled expression
+   * @throws ParseException on failure to compile
+   */
+  public static Expression compile(String expression) throws ParseException {
+    return new JavascriptCompiler().compileExpression(expression);
+  }
+
+  /**
+   * Compiles the given expression.
+   *
+   * @param expression The expression to compile
+   * @return A new compiled expression
+   * @throws ParseException on failure to compile
+   */
+  public Expression compileExpression(String expression) throws ParseException {
+    try {
+      this.className = "Expr" + Long.toString(counter.incrementAndGet());
+      externalsMap = new HashMap<String, Integer>();
+      externalsList = new ArrayList<String>();
+      
+      Tree antlrTree = getAntlrComputedExpressionTree(expression);
+      
+      beginCompile();
+      recursiveCompile(antlrTree, ComputedType.DOUBLE);
+      endCompile();
+      
+      Class<? extends Expression> evaluatorClass = loader.define(EXPRESSION_CLASS_PREFIX + className, classWriter.toByteArray());
+      Constructor<? extends Expression> constructor = evaluatorClass.getConstructor(String.class, String[].class);
+      return constructor.newInstance(expression, externalsList.toArray(new String[externalsList.size()]));
+    } catch (InstantiationException exception) {
+      throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
+    } catch (IllegalAccessException exception) {
+      throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
+    } catch (NoSuchMethodException exception) {
+      throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
+    } catch (InvocationTargetException exception) {
+      throw new IllegalStateException("An internal error occurred attempting to compile the expression (" + className + ").", exception);
+    }
+  }
+
+  private void beginCompile() {
+    classWriter = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+    classWriter.visit(V1_7, ACC_PUBLIC + ACC_SUPER + ACC_FINAL, EXPRESSION_INTERNAL_PREFIX + className,
+        null, COMPILED_EXPRESSION_INTERNAL, null);
+    MethodVisitor constructor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/String;[Ljava/lang/String;)V", null, null);
+    constructor.visitCode();
+    constructor.visitVarInsn(ALOAD, 0);
+    constructor.visitVarInsn(ALOAD, 1);
+    constructor.visitVarInsn(ALOAD, 2);
+    constructor.visitMethodInsn(INVOKESPECIAL, COMPILED_EXPRESSION_INTERNAL, "<init>", "(Ljava/lang/String;[Ljava/lang/String;)V");
+    constructor.visitInsn(RETURN);
+    constructor.visitMaxs(0, 0);
+    constructor.visitEnd();
+    
+    methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "evaluate", "(I[L" + FUNCTION_VALUES_INTERNAL + ";)D", null, null);
+    methodVisitor.visitCode();
+  }
+  
+  private void recursiveCompile(Tree current, ComputedType 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;
+        
+        JavascriptFunction method = JavascriptFunction.getMethod(call, arguments);
+        
+        for (int argument = 1; argument <= arguments; ++argument) {
+          recursiveCompile(current.getChild(argument), ComputedType.DOUBLE);
+        }
+        
+        methodVisitor.visitMethodInsn(INVOKESTATIC, method.klass, method.method, method.signature);
+        
+        typeCompile(expected, ComputedType.DOUBLE);
+        break;
+      case JavascriptParser.ID:
+        int index;
+        
+        if (externalsMap.containsKey(text)) {
+          index = externalsMap.get(text);
+        } else {
+          index = externalsList.size();
+          externalsList.add(text);
+          externalsMap.put(text, index);
+        }
+        
+        methodVisitor.visitVarInsn(ALOAD, 2);
+        
+        switch (index) {
+          case 0:
+            methodVisitor.visitInsn(ICONST_0);
+            break;
+          case 1:
+            methodVisitor.visitInsn(ICONST_1);
+            break;
+          case 2:
+            methodVisitor.visitInsn(ICONST_2);
+            break;
+          case 3:
+            methodVisitor.visitInsn(ICONST_3);
+            break;
+          case 4:
+            methodVisitor.visitInsn(ICONST_4);
+            break;
+          case 5:
+            methodVisitor.visitInsn(ICONST_5);
+            break;
+          default:
+            if (index < 128) {
+              methodVisitor.visitIntInsn(BIPUSH, index);
+            } else if (index < 16384) {
+              methodVisitor.visitIntInsn(SIPUSH, index);
+            } else {
+              methodVisitor.visitLdcInsn(index);
+            }
+            
+            break;
+        }
+        
+        methodVisitor.visitInsn(AALOAD);
+        methodVisitor.visitVarInsn(ILOAD, 1);
+        methodVisitor.visitMethodInsn(INVOKEVIRTUAL, FUNCTION_VALUES_INTERNAL, "doubleVal", "(I)D");
+        
+        typeCompile(expected, ComputedType.DOUBLE);
+        break;
+      case JavascriptParser.HEX:
+        long hex = Long.parseLong(text.substring(2), 16);
+        
+        if (expected == ComputedType.INT) {
+          methodVisitor.visitLdcInsn((int)hex);
+        } else if (expected == ComputedType.LONG) {
+          methodVisitor.visitLdcInsn(hex);
+        } else {
+          methodVisitor.visitLdcInsn((double)hex);
+        }
+        
+        break;
+      case JavascriptParser.OCTAL:
+        long octal = Long.parseLong(text.substring(1), 8);
+        
+        if (expected == ComputedType.INT) {
+          methodVisitor.visitLdcInsn((int)octal);
+        } else if (expected == ComputedType.LONG) {
+          methodVisitor.visitLdcInsn(octal);
+        } else {
+          methodVisitor.visitLdcInsn((double)octal);
+        }
+        
+        break;
+      case JavascriptParser.DECIMAL:
+        double decimal = Double.parseDouble(text);
+        methodVisitor.visitLdcInsn(decimal);
+        
+        typeCompile(expected, ComputedType.DOUBLE);
+        break;
+      case JavascriptParser.AT_NEGATE:
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(DNEG);
+        
+        typeCompile(expected, ComputedType.DOUBLE);
+        break;
+      case JavascriptParser.AT_ADD:
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(DADD);
+        
+        typeCompile(expected, ComputedType.DOUBLE);
+        break;
+      case JavascriptParser.AT_SUBTRACT:
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(DSUB);
+        
+        typeCompile(expected, ComputedType.DOUBLE);
+        break;
+      case JavascriptParser.AT_MULTIPLY:
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(Opcodes.DMUL);
+        
+        typeCompile(expected, ComputedType.DOUBLE);
+        break;
+      case JavascriptParser.AT_DIVIDE:
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(DDIV);
+        
+        typeCompile(expected, ComputedType.DOUBLE);
+        break;
+      case JavascriptParser.AT_MODULO:
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(DREM);
+        
+        typeCompile(expected, ComputedType.DOUBLE);
+        break;
+      case JavascriptParser.AT_BIT_SHL:
+        recursiveCompile(current.getChild(0), ComputedType.LONG);
+        recursiveCompile(current.getChild(1), ComputedType.INT);
+        methodVisitor.visitInsn(LSHL);
+        
+        typeCompile(expected, ComputedType.LONG);
+        break;
+      case JavascriptParser.AT_BIT_SHR:
+        recursiveCompile(current.getChild(0), ComputedType.LONG);
+        recursiveCompile(current.getChild(1), ComputedType.INT);
+        methodVisitor.visitInsn(LSHR);
+        
+        typeCompile(expected, ComputedType.LONG);
+        break;
+      case JavascriptParser.AT_BIT_SHU:
+        recursiveCompile(current.getChild(0), ComputedType.LONG);
+        recursiveCompile(current.getChild(1), ComputedType.INT);
+        methodVisitor.visitInsn(LUSHR);
+        
+        typeCompile(expected, ComputedType.LONG);
+        break;
+      case JavascriptParser.AT_BIT_AND:
+        recursiveCompile(current.getChild(0), ComputedType.LONG);
+        recursiveCompile(current.getChild(1), ComputedType.LONG);
+        methodVisitor.visitInsn(LAND);
+        
+        typeCompile(expected, ComputedType.LONG);
+        break;
+      case JavascriptParser.AT_BIT_OR:
+        recursiveCompile(current.getChild(0), ComputedType.LONG);
+        recursiveCompile(current.getChild(1), ComputedType.LONG);
+        methodVisitor.visitInsn(LOR);
+        
+        typeCompile(expected, ComputedType.LONG);            
+        break;
+      case JavascriptParser.AT_BIT_XOR:
+        recursiveCompile(current.getChild(0), ComputedType.LONG);
+        recursiveCompile(current.getChild(1), ComputedType.LONG);
+        methodVisitor.visitInsn(LXOR);
+        
+        typeCompile(expected, ComputedType.LONG);            
+        break;
+      case JavascriptParser.AT_BIT_NOT:
+        recursiveCompile(current.getChild(0), ComputedType.LONG);
+        methodVisitor.visitLdcInsn(new Long(-1));
+        methodVisitor.visitInsn(LXOR);
+        
+        typeCompile(expected, ComputedType.LONG);
+        break;
+      case JavascriptParser.AT_COMP_EQ:
+        Label labelEqTrue = new Label();
+        Label labelEqReturn = new Label();
+        
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(DCMPL);
+        
+        methodVisitor.visitJumpInsn(IFEQ, labelEqTrue);
+        truthCompile(expected, false);
+        methodVisitor.visitJumpInsn(GOTO, labelEqReturn);
+        methodVisitor.visitLabel(labelEqTrue);
+        truthCompile(expected, true);
+        methodVisitor.visitLabel(labelEqReturn);
+        
+        break;
+      case JavascriptParser.AT_COMP_NEQ:
+        Label labelNeqTrue = new Label();
+        Label labelNeqReturn = new Label();
+        
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(DCMPL);
+        
+        methodVisitor.visitJumpInsn(IFNE, labelNeqTrue);
+        truthCompile(expected, false);
+        methodVisitor.visitJumpInsn(GOTO, labelNeqReturn);
+        methodVisitor.visitLabel(labelNeqTrue);
+        truthCompile(expected, true);
+        methodVisitor.visitLabel(labelNeqReturn);
+        
+        break;
+      case JavascriptParser.AT_COMP_LT:
+        Label labelLtTrue = new Label();
+        Label labelLtReturn = new Label();
+        
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(DCMPG);
+        
+        methodVisitor.visitJumpInsn(IFLT, labelLtTrue);
+        truthCompile(expected, false);
+        methodVisitor.visitJumpInsn(GOTO, labelLtReturn);
+        methodVisitor.visitLabel(labelLtTrue);
+        truthCompile(expected, true);
+        methodVisitor.visitLabel(labelLtReturn);
+        
+        break;
+      case JavascriptParser.AT_COMP_GT:
+        Label labelGtTrue = new Label();
+        Label labelGtReturn = new Label();
+        
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(DCMPL);
+        
+        methodVisitor.visitJumpInsn(IFGT, labelGtTrue);
+        truthCompile(expected, false);
+        methodVisitor.visitJumpInsn(GOTO, labelGtReturn);
+        methodVisitor.visitLabel(labelGtTrue);
+        truthCompile(expected, true);
+        methodVisitor.visitLabel(labelGtReturn);
+        
+        break;
+      case JavascriptParser.AT_COMP_LTE:
+        Label labelLteTrue = new Label();
+        Label labelLteReturn = new Label();
+        
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(DCMPG);
+        
+        methodVisitor.visitJumpInsn(IFLE, labelLteTrue);
+        truthCompile(expected, false);
+        methodVisitor.visitJumpInsn(GOTO, labelLteReturn);
+        methodVisitor.visitLabel(labelLteTrue);
+        truthCompile(expected, true);
+        methodVisitor.visitLabel(labelLteReturn);
+        
+        break;
+      case JavascriptParser.AT_COMP_GTE:
+        Label labelGteTrue = new Label();
+        Label labelGteReturn = new Label();
+        
+        recursiveCompile(current.getChild(0), ComputedType.DOUBLE);
+        recursiveCompile(current.getChild(1), ComputedType.DOUBLE);
+        methodVisitor.visitInsn(DCMPL);
+        
+        methodVisitor.visitJumpInsn(IFGE, labelGteTrue);
+        truthCompile(expected, false);
+        methodVisitor.visitJumpInsn(GOTO, labelGteReturn);
+        methodVisitor.visitLabel(labelGteTrue);
+        truthCompile(expected, true);
+        methodVisitor.visitLabel(labelGteReturn);
+        
+        break;
+      case JavascriptParser.AT_BOOL_NOT:
+        Label labelNotTrue = new Label();
+        Label labelNotReturn = new Label();
+        
+        recursiveCompile(current.getChild(0), ComputedType.INT);
+        methodVisitor.visitJumpInsn(IFEQ, labelNotTrue);
+        truthCompile(expected, false);
+        methodVisitor.visitJumpInsn(GOTO, labelNotReturn);
+        methodVisitor.visitLabel(labelNotTrue);
+        truthCompile(expected, true);
+        methodVisitor.visitLabel(labelNotReturn);
+        
+        break;
+      case JavascriptParser.AT_BOOL_AND:
+        Label andFalse = new Label();
+        Label andEnd = new Label();
+        
+        recursiveCompile(current.getChild(0), ComputedType.INT);
+        methodVisitor.visitJumpInsn(IFEQ, andFalse);
+        recursiveCompile(current.getChild(1), ComputedType.INT);
+        methodVisitor.visitJumpInsn(IFEQ, andFalse);
+        truthCompile(expected, true);
+        methodVisitor.visitJumpInsn(GOTO, andEnd);
+        methodVisitor.visitLabel(andFalse);
+        truthCompile(expected, false);
+        methodVisitor.visitLabel(andEnd);
+        
+        break;
+      case JavascriptParser.AT_BOOL_OR:
+        Label orTrue = new Label();
+        Label orEnd = new Label();
+        
+        recursiveCompile(current.getChild(0), ComputedType.INT);
+        methodVisitor.visitJumpInsn(IFNE, orTrue);
+        recursiveCompile(current.getChild(1), ComputedType.INT);
+        methodVisitor.visitJumpInsn(IFNE, orTrue);
+        truthCompile(expected, false);
+        methodVisitor.visitJumpInsn(GOTO, orEnd);
+        methodVisitor.visitLabel(orTrue);
+        truthCompile(expected, true);
+        methodVisitor.visitLabel(orEnd);
+        
+        break;
+      case JavascriptParser.AT_COND_QUE:
+        Label condFalse = new Label();
+        Label condEnd = new Label();
+        
+        recursiveCompile(current.getChild(0), ComputedType.INT);
+        methodVisitor.visitJumpInsn(IFEQ, condFalse);
+        recursiveCompile(current.getChild(1), expected);
+        methodVisitor.visitJumpInsn(GOTO, condEnd);
+        methodVisitor.visitLabel(condFalse);
+        recursiveCompile(current.getChild(2), expected);
+        methodVisitor.visitLabel(condEnd);
+        
+        break;
+      default:
+        throw new IllegalStateException("Unknown operation specified: (" + current.getText() + ").");
+    }
+  }
+  
+  private void typeCompile(ComputedType expected, ComputedType actual) {
+    if (expected == actual) {
+      return;
+    }
+    
+    switch (expected) {
+      case INT:
+        if (actual == ComputedType.LONG) {
+          methodVisitor.visitInsn(L2I);
+        } else {
+          methodVisitor.visitInsn(D2I);
+        }
+        
+        break;
+      case LONG:
+        if (actual == ComputedType.INT) {
+          methodVisitor.visitInsn(I2L);
+        } else {
+          methodVisitor.visitInsn(D2L);
+        }
+        
+        break;
+      default:
+        if (actual == ComputedType.INT) {
+          methodVisitor.visitInsn(I2D);
+        } else {
+          methodVisitor.visitInsn(L2D);
+        }
+        
+        break;
+    }
+  }
+  
+  private void truthCompile(ComputedType expected, boolean truth) {
+    switch (expected) {
+      case INT:
+        methodVisitor.visitInsn(truth ? ICONST_1 : ICONST_0);
+        
+        break;
+      case LONG:
+        methodVisitor.visitInsn(truth ? LCONST_1 : LCONST_0);
+        
+        break;
+      default:
+        methodVisitor.visitInsn(truth ? DCONST_1 : DCONST_0);
+        
+        break;
+    }
+  }
+  
+  private void endCompile() {
+    methodVisitor.visitInsn(DRETURN);
+    methodVisitor.visitMaxs(0, 0);
+    methodVisitor.visitEnd();
+    
+    classWriter.visitEnd();
+  }
+
+  private static Tree getAntlrComputedExpressionTree(String expression) throws ParseException {
+    CharStream input = new ANTLRStringStream(expression);
+    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;
+    }
+  }
+}

Added: lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptFunction.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptFunction.java?rev=1522767&view=auto
==============================================================================
--- lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptFunction.java (added)
+++ lucene/dev/branches/lucene5207/lucene/expressions/src/java/org/apache/lucene/expressions/js/JavascriptFunction.java Fri Sep 13 03:11:17 2013
@@ -0,0 +1,116 @@
+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.util.HashMap;
+import java.util.Map;
+
+import org.apache.lucene.util.MathUtil;
+import org.objectweb.asm.Type;
+
+/**
+ * A wrapper to delegate function calls from a javascript expression.
+ */
+class JavascriptFunction {
+  private static Map<String, JavascriptFunction> methods = null;
+  
+  static {
+    String mathClass  = Type.getInternalName(Math.class);
+    String mathUtilClass = Type.getInternalName(MathUtil.class);
+    
+    JavascriptFunction abs    = new JavascriptFunction("abs",    1, mathClass,  "abs",      "(D)D" );
+    JavascriptFunction acos   = new JavascriptFunction("acos",   1, mathClass,  "acos",     "(D)D" );
+    JavascriptFunction acosh  = new JavascriptFunction("acosh",  1, mathUtilClass, "acosh",    "(D)D" );
+    JavascriptFunction asin   = new JavascriptFunction("asin",   1, mathClass,  "asin",     "(D)D" );
+    JavascriptFunction asinh  = new JavascriptFunction("asinh",  1, mathUtilClass, "asinh",    "(D)D" );
+    JavascriptFunction atan   = new JavascriptFunction("atan",   1, mathClass,  "atan",     "(D)D" );
+    JavascriptFunction atan2  = new JavascriptFunction("atan2",  1, mathClass,  "atan2",    "(DD)D");
+    JavascriptFunction atanh  = new JavascriptFunction("atanh",  1, mathUtilClass, "atanh",    "(D)D" );
+    JavascriptFunction ceil   = new JavascriptFunction("ceil",   1, mathClass,  "ceil",     "(D)D" );
+    JavascriptFunction cos    = new JavascriptFunction("cos",    1, mathClass,  "cos",      "(D)D" );
+    JavascriptFunction cosh   = new JavascriptFunction("cosh",   1, mathClass,  "cosh",     "(D)D" );
+    JavascriptFunction exp    = new JavascriptFunction("exp",    1, mathClass,  "exp",      "(D)D" );
+    JavascriptFunction floor  = new JavascriptFunction("floor",  1, mathClass,  "floor",    "(D)D" );
+    JavascriptFunction ln     = new JavascriptFunction("ln",     1, mathClass,  "log",      "(D)D" );
+    JavascriptFunction log10  = new JavascriptFunction("log10",  1, mathClass,  "log10",    "(D)D" );
+    JavascriptFunction logn   = new JavascriptFunction("logn",   2, mathUtilClass, "log",      "(DD)D");
+    JavascriptFunction pow    = new JavascriptFunction("pow",    2, mathClass,  "pow",      "(DD)D");
+    JavascriptFunction sin    = new JavascriptFunction("sin",    1, mathClass,  "sin",      "(D)D" );
+    JavascriptFunction sinh   = new JavascriptFunction("sinh",   1, mathClass,  "sinh",     "(D)D" );
+    JavascriptFunction sqrt   = new JavascriptFunction("sqrt",   1, mathClass,  "sqrt",     "(D)D" );
+    JavascriptFunction tan    = new JavascriptFunction("tan",    1, mathClass,  "tan",      "(D)D" );
+    JavascriptFunction tanh   = new JavascriptFunction("tanh",   1, mathClass,  "tanh",     "(D)D" );
+    
+    JavascriptFunction min    = new JavascriptFunction("min",    2, mathClass,  "min",      "(DD)D");
+    JavascriptFunction max    = new JavascriptFunction("max",    2, mathClass,  "max",      "(DD)D");
+    
+    methods  = new HashMap<String, JavascriptFunction>();
+    methods.put( "abs",    abs   );
+    methods.put( "acos",   acos  );
+    methods.put( "acosh",  acosh );
+    methods.put( "asin",   asin  );
+    methods.put( "asinh",  asinh );
+    methods.put( "atan",   atan  );
+    methods.put( "atan2",  atan2 );
+    methods.put( "atanh",  atanh );
+    methods.put( "ceil",   ceil  );
+    methods.put( "cos",    cos   );
+    methods.put( "cosh",   cosh  );
+    methods.put( "exp",    exp   );
+    methods.put( "floor",  floor );
+    methods.put( "ln",     ln    );
+    methods.put( "log10",  log10 );
+    methods.put( "logn",   logn  );
+    methods.put( "max",    max   );
+    methods.put( "min",    min   );
+    methods.put( "pow",    pow   );
+    methods.put( "sin",    sin   );
+    methods.put( "sinh",   sinh  );
+    methods.put( "sqrt",   sqrt  );
+    methods.put( "tan",    tan   );
+    methods.put( "tanh",   tanh  );
+  }
+
+  public static JavascriptFunction getMethod(String call, int arguments) {
+    JavascriptFunction method = methods.get(call);
+
+    if (method == null) {
+      throw new IllegalArgumentException("Unrecognized method call (" + call + ").");
+    }
+
+    if (arguments != method.arguments && method.arguments != -1) {
+      throw new IllegalArgumentException("Expected (" + method.arguments + ") arguments for method call (" +
+          call + "), but found (" + arguments + ").");
+    }
+
+    return method;
+  }
+  
+  public final String call;
+  public final int arguments;
+  public final String klass;
+  public final String method;
+  public final String signature;
+  
+  private JavascriptFunction(String call, int arguments, String klass, String method, String signature) {
+    this.call = call;
+    this.arguments = arguments;
+    this.klass = klass;
+    this.method = method;
+    this.signature = signature;
+  }
+}