You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by he...@apache.org on 2012/02/11 12:26:43 UTC

svn commit: r1243029 [1/2] - in /commons/proper/jexl/trunk: ./ src/main/java/org/apache/commons/jexl3/ src/main/java/org/apache/commons/jexl3/internal/ src/main/java/org/apache/commons/jexl3/parser/ src/test/java/org/apache/commons/jexl3/ src/test/java...

Author: henrib
Date: Sat Feb 11 11:26:42 2012
New Revision: 1243029

URL: http://svn.apache.org/viewvc?rev=1243029&view=rev
Log:
JEXL-127; added support for 'function', includes variable hoisting

Added:
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Frame.java   (with props)
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Scope.java   (with props)
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java
      - copied, changed from r1211892, commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/LambdaTest.java   (with props)
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/PropertyAccessTest.java
      - copied, changed from r1212868, commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayAccessTest.java
Modified:
    commons/proper/jexl/trunk/pom.xml
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
    commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayAccessTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AssignTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/JexlTest.java
    commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/parser/ParserTest.java

Modified: commons/proper/jexl/trunk/pom.xml
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/pom.xml?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/pom.xml (original)
+++ commons/proper/jexl/trunk/pom.xml Sat Feb 11 11:26:42 2012
@@ -19,7 +19,7 @@
     <parent>
         <groupId>org.apache.commons</groupId>
         <artifactId>commons-parent</artifactId>
-        <version>22</version>
+        <version>23</version>
     </parent>
     <modelVersion>4.0.0</modelVersion>
     <groupId>org.apache.commons</groupId>

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/JexlException.java Sat Feb 11 11:26:42 2012
@@ -46,10 +46,7 @@ public class JexlException extends Runti
      * @param msg the error message
      */
     public JexlException(JexlNode node, String msg) {
-        super(msg);
-        mark = node;
-        info = node != null ? node.jexlInfo() : null;
-
+        this(node, msg, null);
     }
 
     /**
@@ -59,32 +56,19 @@ public class JexlException extends Runti
      * @param cause the exception causing the error
      */
     public JexlException(JexlNode node, String msg, Throwable cause) {
-        super(msg, unwrap(cause));
-        mark = node;
-        info = node != null ? node.jexlInfo() : null;
-    }
-
-    /**
-     * Creates a new JexlException.
-     * @param dbg the debugging information associated
-     * @param msg the error message
-     */
-    public JexlException(JexlInfo dbg, String msg) {
-        super(msg);
-        mark = null;
-        info = dbg;
+        this(node != null ? node.jexlInfo() : null, msg, cause);
     }
 
     /**
      * Creates a new JexlException.
-     * @param dbg the debugging information associated
+     * @param jinfo the debugging information associated
      * @param msg the error message
      * @param cause the exception causing the error
      */
-    public JexlException(JexlInfo dbg, String msg, Throwable cause) {
+    public JexlException(JexlInfo jinfo, String msg, Throwable cause) {
         super(msg, unwrap(cause));
         mark = null;
-        info = dbg;
+        info = jinfo;
     }
 
     /**
@@ -140,22 +124,22 @@ public class JexlException extends Runti
     public static class Tokenization extends JexlException {
         /**
          * Creates a new Tokenization exception instance.
-         * @param node the location info
+         * @param info the location info
          * @param expr the expression
          * @param cause the javacc cause
          */
-        public Tokenization(JexlInfo node, CharSequence expr, TokenMgrError cause) {
-            super(merge(node, cause), expr.toString(), cause);
+        public Tokenization(JexlInfo info, CharSequence expr, TokenMgrError cause) {
+            super(merge(info, cause), expr.toString(), cause);
         }
 
         /**
          * Merge the node info and the cause info to obtain best possible location.
-         * @param node the node
+         * @param info the node
          * @param cause the cause
          * @return the info to use
          */
-        private static JexlInfo merge(JexlInfo node, TokenMgrError cause) {
-            JexlInfo dbgn = node != null ? node : null;
+        private static JexlInfo merge(JexlInfo info, TokenMgrError cause) {
+            JexlInfo dbgn = info != null ? info : null;
             if (cause == null) {
                 return dbgn;
             } else if (dbgn == null) {
@@ -185,22 +169,22 @@ public class JexlException extends Runti
     public static class Parsing extends JexlException {
         /**
          * Creates a new Variable exception instance.
-         * @param node the offending ASTnode
+         * @param info the location information
          * @param expr the offending source
          * @param cause the javacc cause
          */
-        public Parsing(JexlInfo node, CharSequence expr, ParseException cause) {
-            super(merge(node, cause), expr.toString(), cause);
+        public Parsing(JexlInfo info, CharSequence expr, ParseException cause) {
+            super(merge(info, cause), expr.toString(), cause);
         }
 
         /**
          * Merge the node info and the cause info to obtain best possible location.
-         * @param node the node
+         * @param info the location information
          * @param cause the cause
          * @return the info to use
          */
-        private static JexlInfo merge(JexlInfo node, ParseException cause) {
-            JexlInfo dbgn = node != null ? node : null;
+        private static JexlInfo merge(JexlInfo info, ParseException cause) {
+            JexlInfo dbgn = info != null ? info : null;
             if (cause == null) {
                 return dbgn;
             } else if (dbgn == null) {
@@ -232,9 +216,10 @@ public class JexlException extends Runti
          * Creates a new Variable exception instance.
          * @param node the offending ASTnode
          * @param var the unknown variable
+         * @param cause the exception causing the error
          */
         public Variable(JexlNode node, String var) {
-            super(node, var);
+            super(node, var, null);
         }
 
         /**
@@ -261,7 +246,17 @@ public class JexlException extends Runti
          * @param var the unknown variable
          */
         public Property(JexlNode node, String var) {
-            super(node, var);
+            this(node, var, null);
+        }
+
+        /**
+         * Creates a new Property exception instance.
+         * @param node the offending ASTnode
+         * @param var the unknown variable
+         * @param cause the exception causing the error
+         */
+        public Property(JexlNode node, String var, Throwable cause) {
+            super(node, var, cause);
         }
 
         /**
@@ -286,9 +281,20 @@ public class JexlException extends Runti
          * Creates a new Method exception instance.
          * @param node the offending ASTnode
          * @param name the unknown method
+         * @param cause the exception causing the error
+         */
+        public Method(JexlNode node, String name, Throwable cause) {
+            super(node, name, cause);
+        }
+
+        /**
+         * Creates a new Method exception instance.
+         * @param info the location information
+         * @param name the unknown method
+         * @param cause the exception causing the error
          */
-        public Method(JexlNode node, String name) {
-            super(node, name);
+        public Method(JexlInfo info, String name, Throwable cause) {
+            super(info, name, cause);
         }
 
         /**
@@ -319,7 +325,7 @@ public class JexlException extends Runti
          * @param value the returned value
          */
         public Return(JexlNode node, String msg, Object value) {
-            super(node, msg);
+            super(node, msg, null);
             this.result = value;
         }
 

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Debugger.java Sat Feb 11 11:26:42 2012
@@ -17,6 +17,7 @@
 package org.apache.commons.jexl3.internal;
 
 import java.util.regex.Pattern;
+import org.apache.commons.jexl3.JexlExpression;
 import org.apache.commons.jexl3.JexlScript;
 import org.apache.commons.jexl3.parser.ASTAdditiveNode;
 import org.apache.commons.jexl3.parser.ASTAdditiveOperator;
@@ -41,6 +42,7 @@ import org.apache.commons.jexl3.parser.A
 import org.apache.commons.jexl3.parser.ASTGTNode;
 import org.apache.commons.jexl3.parser.ASTIdentifier;
 import org.apache.commons.jexl3.parser.ASTIfStatement;
+import org.apache.commons.jexl3.parser.ASTJexlLambda;
 import org.apache.commons.jexl3.parser.ASTJexlScript;
 import org.apache.commons.jexl3.parser.ASTLENode;
 import org.apache.commons.jexl3.parser.ASTLTNode;
@@ -101,6 +103,19 @@ public final class Debugger extends Pars
     }
     
     /**
+     * Position the debugger on the root of an expression.
+     * @param jscript the expression
+     * @return true if the expression was a {@link Script} instance, false otherwise
+     */
+    public boolean debug(JexlExpression jscript) {
+        if (jscript instanceof Script) {
+            return debug(((Script) jscript).script);
+        } else {
+            return false;
+        }
+    }
+
+    /**
      * Position the debugger on the root of a script.
      * @param jscript the script
      * @return true if the script was a {@link Script} instance, false otherwise
@@ -140,8 +155,7 @@ public final class Debugger extends Pars
     public String data() {
         return builder.toString();
     }
-    
-    
+
     /**
      * Rebuilds an expression from a Jexl node.
      * @param node the node to rebuilt from
@@ -431,14 +445,15 @@ public final class Debugger extends Pars
     protected Object visit(ASTGTNode node, Object data) {
         return infixChildren(node, " > ", false, data);
     }
-
     /** Checks identifiers that contain space, quote, double-quotes or backspace. */
     private static final Pattern QUOTED_IDENTIFIER = Pattern.compile("['\"\\s\\\\]");
+    /** Checks number used as identifiers. */
+    private static final Pattern NUMBER_IDENTIFIER = Pattern.compile("^\\d*$");
     
     @Override
     protected Object visit(ASTIdentifier node, Object data) {
         String image = node.image;
-        if (QUOTED_IDENTIFIER.matcher(image).find()) {
+        if (QUOTED_IDENTIFIER.matcher(image).find() || NUMBER_IDENTIFIER.matcher(image).find()) {
             // quote it
             image = "'" + node.image.replace("'", "\\'") + "'";
         }
@@ -471,6 +486,18 @@ public final class Debugger extends Pars
 
     @Override
     protected Object visit(ASTJexlScript node, Object data) {
+        if (node instanceof ASTJexlLambda) {
+            builder.append("function(");
+            String[] params = node.getParameters();
+            if (params != null && params.length > 0) {
+                builder.append(params[0]);
+                for (int p = 1; p < params.length; ++p) {
+                    builder.append(", ");
+                    builder.append(params[p]);
+                }
+            }
+            builder.append(")");
+        }
         int num = node.jjtGetNumChildren();
         for (int i = 0; i < num; ++i) {
             JexlNode child = node.jjtGetChild(i);
@@ -699,5 +726,4 @@ public final class Debugger extends Pars
         }
         return data;
     }
-
 }
\ No newline at end of file

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Engine.java Sat Feb 11 11:26:42 2012
@@ -16,7 +16,6 @@
  */
 package org.apache.commons.jexl3.internal;
 
-
 import org.apache.commons.jexl3.JexlArithmetic;
 import org.apache.commons.jexl3.JexlBuilder;
 import org.apache.commons.jexl3.JexlContext;
@@ -54,7 +53,6 @@ import java.io.StringReader;
 
 import java.util.ArrayList;
 import java.util.Collections;
-import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Map;
@@ -207,7 +205,7 @@ public class Engine extends JexlEngine {
     public JexlArithmetic getArithmetic() {
         return arithmetic;
     }
-    
+
     @Override
     public TemplateEngine createJxltEngine() {
         return new TemplateEngine(this);
@@ -263,7 +261,7 @@ public class Engine extends JexlEngine {
     public Script createScript(String scriptText) {
         return createScript(scriptText, null, null);
     }
-    
+
     @Override
     public Script createScript(String scriptText, String... names) {
         return createScript(scriptText, null, names);
@@ -275,7 +273,7 @@ public class Engine extends JexlEngine {
             throw new NullPointerException("scriptText is null");
         }
         // Parse the expression
-        ASTJexlScript tree = parse(scriptText, info, new Scope(names));
+        ASTJexlScript tree = parse(scriptText, info, new Scope(null, names));
         return createScript(tree, scriptText);
     }
 
@@ -334,7 +332,7 @@ public class Engine extends JexlEngine {
         expr = "#0" + (expr.charAt(0) == '[' ? "" : ".") + expr + ";";
         try {
             parser.ALLOW_REGISTERS = true;
-            Scope scope = new Scope("#0");
+            Scope scope = new Scope(null, "#0");
             ASTJexlScript script = parse(expr, null, scope);
             JexlNode node = script.jjtGetChild(0);
             Frame frame = script.createFrame(bean);
@@ -365,7 +363,7 @@ public class Engine extends JexlEngine {
         expr = "#0" + (expr.charAt(0) == '[' ? "" : ".") + expr + "=" + "#1" + ";";
         try {
             parser.ALLOW_REGISTERS = true;
-            Scope scope = new Scope("#0", "#1");
+            Scope scope = new Scope(null, "#0", "#1");
             ASTJexlScript script = parse(expr, null, scope);
             JexlNode node = script.jjtGetChild(0);
             Frame frame = script.createFrame(bean, value);
@@ -395,10 +393,10 @@ public class Engine extends JexlEngine {
             if (method != null) {
                 result = method.invoke(obj, args);
             } else {
-                xjexl = new JexlException(info, "failed finding method " + meth);
+                xjexl = new JexlException.Method(info, meth, null);
             }
         } catch (Exception xany) {
-            xjexl = new JexlException(info, "failed executing method " + meth, xany);
+            xjexl = new JexlException.Method(info, meth, xany);
         } finally {
             if (xjexl != null) {
                 if (silent) {
@@ -440,10 +438,10 @@ public class Engine extends JexlEngine {
             if (ctor != null) {
                 result = ctor.invoke(clazz, args);
             } else {
-                xjexl = new JexlException(info, "failed finding constructor for " + clazz.toString());
+                xjexl = new JexlException.Method(info, clazz.toString(), null);
             }
         } catch (Exception xany) {
-            xjexl = new JexlException(info, "failed executing constructor for " + clazz.toString(), xany);
+            xjexl = new JexlException.Method(info, clazz.toString(), xany);
         } finally {
             if (xjexl != null) {
                 if (silent) {
@@ -462,7 +460,7 @@ public class Engine extends JexlEngine {
      * @param frame the interpreter frame
      * @return an Interpreter
      */
-    protected Interpreter createInterpreter(JexlContext context, Engine.Frame frame) {
+    protected Interpreter createInterpreter(JexlContext context, Frame frame) {
         return new Interpreter(this, context == null ? EMPTY_CONTEXT : context, frame);
     }
 
@@ -662,201 +660,6 @@ public class Engine extends JexlEngine {
     }
 
     /**
-     * A script scope, stores the declaration of parameters and local variables.
-     * @since 3.0
-     */
-    public static final class Scope {
-        /**
-         * The number of parameters.
-         */
-        private final int parms;
-        /**
-         * The map of named registers aka script parameters.
-         * Each parameter is associated to a register and is materialized as an offset in the registers array used
-         * during evaluation.
-         */
-        private Map<String, Integer> namedRegisters = null;
-
-        /**
-         * Creates a new scope with a list of parameters.
-         * @param parameters the list of parameters
-         */
-        public Scope(String... parameters) {
-            if (parameters != null) {
-                parms = parameters.length;
-                namedRegisters = new LinkedHashMap<String, Integer>();
-                for (int p = 0; p < parms; ++p) {
-                    namedRegisters.put(parameters[p], p);
-                }
-            } else {
-                parms = 0;
-            }
-        }
-
-        @Override
-        public int hashCode() {
-            return namedRegisters == null ? 0 : parms ^ namedRegisters.hashCode();
-        }
-
-        @Override
-        public boolean equals(Object o) {
-            return o instanceof Scope && equals((Scope) o);
-        }
-
-        /**
-         * Whether this frame is equal to another.
-         * @param frame the frame to compare to
-         * @return true if equal, false otherwise
-         */
-        public boolean equals(Scope frame) {
-            if (this == frame) {
-                return true;
-            } else if (frame == null || parms != frame.parms) {
-                return false;
-            } else if (namedRegisters == null) {
-                return frame.namedRegisters == null;
-            } else {
-                return namedRegisters.equals(frame.namedRegisters);
-            }
-        }
-
-        /**
-         * Checks whether an identifier is a local variable or argument, ie stored in a register. 
-         * @param name the register name
-         * @return the register index
-         */
-        public Integer getRegister(String name) {
-            return namedRegisters != null ? namedRegisters.get(name) : null;
-        }
-
-        /**
-         * Declares a local variable.
-         * <p>
-         * This method creates an new entry in the named register map.
-         * </p>
-         * @param name the variable name
-         * @return the register index storing this variable
-         */
-        public Integer declareVariable(String name) {
-            if (namedRegisters == null) {
-                namedRegisters = new LinkedHashMap<String, Integer>();
-            }
-            Integer register = namedRegisters.get(name);
-            if (register == null) {
-                register = Integer.valueOf(namedRegisters.size());
-                namedRegisters.put(name, register);
-            }
-            return register;
-        }
-
-        /**
-         * Creates a frame by copying values up to the number of parameters.
-         * @param values the argument values
-         * @return the arguments array
-         */
-        public Frame createFrame(Object... values) {
-            if (namedRegisters != null) {
-                Object[] arguments = new Object[namedRegisters.size()];
-                if (values != null) {
-                    System.arraycopy(values, 0, arguments, 0, Math.min(parms, values.length));
-                }
-                return new Frame(arguments, namedRegisters.keySet().toArray(new String[0]));
-            } else {
-                return null;
-            }
-        }
-
-        /**
-         * Gets the (maximum) number of arguments this script expects.
-         * @return the number of parameters
-         */
-        public int getArgCount() {
-            return parms;
-        }
-
-        /**
-         * Gets this script registers, i.e. parameters and local variables.
-         * @return the register names
-         */
-        public String[] getRegisters() {
-            return namedRegisters != null ? namedRegisters.keySet().toArray(new String[0]) : new String[0];
-        }
-
-        /**
-         * Gets this script parameters, i.e. registers assigned before creating local variables.
-         * @return the parameter names
-         */
-        public String[] getParameters() {
-            if (namedRegisters != null && parms > 0) {
-                String[] pa = new String[parms];
-                int p = 0;
-                for (Map.Entry<String, Integer> entry : namedRegisters.entrySet()) {
-                    if (entry.getValue().intValue() < parms) {
-                        pa[p++] = entry.getKey();
-                    }
-                }
-                return pa;
-            } else {
-                return null;
-            }
-        }
-
-        /**
-         * Gets this script local variable, i.e. registers assigned to local variables.
-         * @return the parameter names
-         */
-        public String[] getLocalVariables() {
-            if (namedRegisters != null && parms > 0) {
-                String[] pa = new String[parms];
-                int p = 0;
-                for (Map.Entry<String, Integer> entry : namedRegisters.entrySet()) {
-                    if (entry.getValue().intValue() >= parms) {
-                        pa[p++] = entry.getKey();
-                    }
-                }
-                return pa;
-            } else {
-                return null;
-            }
-        }
-    }
-
-    /**
-     * A call frame, created from a scope, stores the arguments and local variables as "registers".
-     * @since 3.0
-     */
-    public static final class Frame {
-        /** Registers or arguments. */
-        private final Object[] registers;
-        /** Parameter and argument names if any. */
-        private final String[] parameters;
-
-        /**
-         * Creates a new frame.
-         * @param r the registers
-         * @param p the parameters
-         */
-        Frame(Object[] r, String[] p) {
-            registers = r;
-            parameters = p;
-        }
-
-        /**
-         * @return the registers
-         */
-        public Object[] getRegisters() {
-            return registers;
-        }
-
-        /**
-         * @return the parameters
-         */
-        public String[] getParameters() {
-            return parameters;
-        }
-    }
-
-    /**
      * Parses an expression.
      * @param expression the expression to parse
      * @param info debug information structure
@@ -886,13 +689,7 @@ public class Engine extends JexlEngine {
                 } else {
                     jexlInfo = info;
                 }
-                parser.setFrame(frame);
-                script = parser.parse(reader, jexlInfo);
-                // reaccess in case local variables have been declared
-                frame = parser.getFrame();
-                if (frame != null) {
-                    script.setScope(frame);
-                }
+                script = parser.parse(reader, frame, jexlInfo);
                 if (cache != null) {
                     cache.put(expr, script);
                 }
@@ -952,5 +749,4 @@ public class Engine extends JexlEngine {
         }
         return info;
     }
-
 }

Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Frame.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Frame.java?rev=1243029&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Frame.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Frame.java Sat Feb 11 11:26:42 2012
@@ -0,0 +1,65 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jexl3.internal;
+
+/**
+ * A call frame, created from a scope, stores the arguments and local variables as "registers".
+ * @since 3.0
+ */
+public final class Frame {
+    /** Registers or arguments. */
+    final Object[] registers;
+    /** Parameter and argument names if any. */
+    final String[] parameters;
+
+    /**
+     * Creates a new frame.
+     * @param r the registers
+     * @param p the parameters
+     */
+    public Frame(Object[] r, String[] p) {
+        registers = r;
+        parameters = p;
+    }
+
+    /**
+     * @return the registers
+     */
+    public Object[] getRegisters() {
+        return registers;
+    }
+
+    /**
+     * @return the parameters
+     */
+    public String[] getParameters() {
+        return parameters;
+    }
+
+    /**
+     * Assign arguments to the frame.
+     * @param values the argument values
+     * @return this frame
+     */
+    public Frame assign(Object... values) {
+        if (registers != null && values != null && values.length > 0) {
+            System.arraycopy(values, 0, registers, 0, Math.min(registers.length, values.length));
+        }
+        return this;
+    }
+    
+}

Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Frame.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java Sat Feb 11 11:26:42 2012
@@ -89,6 +89,7 @@ import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
+import org.apache.commons.jexl3.parser.ASTJexlLambda;
 
 /**
  * An interpreter of JEXL syntax.
@@ -96,6 +97,8 @@ import java.util.Set;
  * @since 2.0
  */
 public class Interpreter extends ParserVisitor {
+    /** The JEXL engine. */
+    protected final Engine jexl;
     /** The logger. */
     protected final Log logger;
     /** The uberspect. */
@@ -117,9 +120,7 @@ public class Interpreter extends ParserV
     /** Cache executors. */
     protected final boolean cache;
     /** Registers or arguments. */
-    protected final Object[] registers;
-    /** Parameter names if any. */
-    protected final String[] parameters;
+    protected final Frame frame;
     /** Cancellation support. */
     protected volatile boolean cancelled = false;
     /** Empty parameters for method matching. */
@@ -127,20 +128,21 @@ public class Interpreter extends ParserV
 
     /**
      * Creates an interpreter.
-     * @param jexl the engine creating this interpreter
+     * @param engine the engine creating this interpreter
      * @param aContext the context to evaluate expression
      * @param frame the engine evaluation frame
      */
-    protected Interpreter(Engine jexl, JexlContext aContext, Engine.Frame frame) {
+    protected Interpreter(Engine engine, JexlContext aContext, Frame eFrame) {
+        this.jexl = engine;
         this.logger = jexl.logger;
         this.uberspect = jexl.uberspect;
-        this.context = aContext != null? aContext : Engine.EMPTY_CONTEXT;
+        this.context = aContext != null ? aContext : Engine.EMPTY_CONTEXT;
         if (this.context instanceof JexlEngine.Options) {
             JexlEngine.Options opts = (JexlEngine.Options) context;
             Boolean ostrict = opts.isStrict();
             Boolean osilent = opts.isSilent();
-            this.strictEngine = ostrict == null? jexl.isStrict() : ostrict.booleanValue();
-            this.silent = osilent == null? jexl.isSilent() : osilent.booleanValue();
+            this.strictEngine = ostrict == null ? jexl.isStrict() : ostrict.booleanValue();
+            this.silent = osilent == null ? jexl.isSilent() : osilent.booleanValue();
             this.arithmetic = jexl.arithmetic.options(opts);
         } else {
             this.strictEngine = jexl.isStrict();
@@ -150,26 +152,24 @@ public class Interpreter extends ParserV
         this.functions = jexl.functions;
         this.strictArithmetic = this.arithmetic.isStrict();
         this.cache = jexl.cache != null;
-        if (frame != null) {
-            this.parameters = frame.getParameters();
-            this.registers = frame.getRegisters();
-        } else {
-            this.parameters = null;
-            this.registers = null;
-        }
+        this.frame = eFrame;
         this.functors = null;
     }
 
-    /**
-     * Checks whether this interpreter considers unknown variables, methods and constructors as errors.
-     * @return true if isStrict, false otherwise
-     */
-
-    /**
-     * Checks whether this interpreter throws JexlException when encountering errors.
-     * @return true if silent, false otherwise
-     */
-
+    protected Interpreter(Interpreter copy, Frame eFrame) {
+        this.jexl = copy.jexl;
+        this.logger = copy.logger;
+        this.uberspect = copy.uberspect;
+        this.context = copy.context;
+        this.arithmetic = copy.arithmetic;
+        this.silent = copy.silent;
+        this.functions = copy.functions;
+        this.strictEngine = copy.strictEngine;
+        this.strictArithmetic = copy.strictArithmetic;
+        this.cache = copy.cache;
+        this.frame = eFrame;
+        this.functors = null;
+    }
 
     /**
      * Interpret the given script/expression.
@@ -207,7 +207,7 @@ public class Interpreter extends ParserV
      */
     protected JexlNode findNullOperand(RuntimeException xrt, JexlNode node, Object left, Object right) {
         if (xrt instanceof ArithmeticException
-            && (Object) JexlException.NULL_OPERAND == xrt.getMessage()) {
+                && (Object) JexlException.NULL_OPERAND == xrt.getMessage()) {
             if (left == null) {
                 return node.jjtGetChild(0);
             }
@@ -283,7 +283,7 @@ public class Interpreter extends ParserV
         if (namespace == null) {
             namespace = functions.get(prefix);
             if (prefix != null && namespace == null) {
-                throw new JexlException(node, "no such function namespace " + prefix);
+                throw new JexlException(node, "no such function namespace " + prefix, null);
             }
         }
         // allow namespace to be instantiated as functor with context if possible, not an error otherwise
@@ -422,7 +422,7 @@ public class Interpreter extends ParserV
 
         // determine initial object & property:
         JexlNode objectNode = null;
-        Object object = register >= 0 ? registers[register] : null;
+        Object object = register >= 0 ? frame.registers[register] : null;
         JexlNode propertyNode = null;
         Object property = null;
         boolean isVariable = true;
@@ -504,7 +504,7 @@ public class Interpreter extends ParserV
         }
         // deal with ant variable; set context
         if (isRegister) {
-            registers[register] = right;
+            frame.registers[register] = right;
             return right;
         } else if (antVar) {
             if (isVariable && object == null) {
@@ -668,7 +668,7 @@ public class Interpreter extends ParserV
                     if (register < 0) {
                         context.set(loopVariable.image, value);
                     } else {
-                        registers[register] = value;
+                        frame.registers[register] = value;
                     }
                     // execute statement
                     result = statement.jjtAccept(this, data);
@@ -766,7 +766,7 @@ public class Interpreter extends ParserV
         if (data == null) {
             int register = node.getRegister();
             if (register >= 0) {
-                return registers[register];
+                return frame.registers[register];
             }
             Object value = context.get(name);
             if (value == null
@@ -824,13 +824,37 @@ public class Interpreter extends ParserV
 
     @Override
     protected Object visit(ASTJexlScript node, Object data) {
-        int numChildren = node.jjtGetNumChildren();
-        Object result = null;
-        for (int i = 0; i < numChildren; i++) {
-            JexlNode child = node.jjtGetChild(i);
-            result = child.jjtAccept(this, data);
+        if (node instanceof ASTJexlLambda) {
+            return new Closure((ASTJexlLambda) node, frame);
+        } else {
+            final int numChildren = node.jjtGetNumChildren();
+            Object result = null;
+            for (int i = 0; i < numChildren; i++) {
+                JexlNode child = node.jjtGetChild(i);
+                result = child.jjtAccept(this, data);
+            }
+            return result;
         }
-        return result;
+    }
+    
+    private static final class Closure {
+        Frame frame;
+        ASTJexlLambda lambda;
+        Closure(ASTJexlLambda l, Frame f) {
+            lambda = l;
+            frame = lambda.createFrame(f);
+        }
+    }
+    
+    private Object call(Closure closure, Object[] argv) {
+        Frame cframe = closure.frame;
+        if (cframe != null) {
+            cframe.assign(argv);
+        }
+        ASTJexlLambda lambda = closure.lambda;
+        JexlNode block = lambda.jjtGetChild(lambda.jjtGetNumChildren() - 1);
+        Interpreter interpreter = new Interpreter(this, cframe);
+        return interpreter.interpret(block);
     }
 
     @Override
@@ -915,40 +939,49 @@ public class Interpreter extends ParserV
                 }
             }
             boolean cacheable = cache;
-            JexlMethod vm = uberspect.getMethod(bean, methodName, argv);
-            // DG: If we can't find an exact match, narrow the parameters and try again
-            if (vm == null) {
-                if (arithmetic.narrowArguments(argv)) {
-                    vm = uberspect.getMethod(bean, methodName, argv);
+            boolean narrow = true;
+            JexlMethod vm = null;
+            // pseudo loop
+            while (true) {
+                vm = uberspect.getMethod(bean, methodName, argv);
+                if (vm != null) {
+                    break;
                 }
-                if (vm == null) {
-                    Object functor = null;
-                    // could not find a method, try as a var
-                    if (bean == context) {
-                        int register = methodNode.getRegister();
-                        if (register >= 0) {
-                            functor = registers[register];
-                        } else {
-                            functor = context.get(methodName);
-                        }
+                Object functor = null;
+                // could not find a method, try as a var
+                if (bean == context) {
+                    int register = methodNode.getRegister();
+                    if (register >= 0) {
+                        functor = frame.registers[register];
                     } else {
-                        JexlPropertyGet gfunctor = uberspect.getPropertyGet(bean, methodName);
-                        if (gfunctor != null) {
-                            functor = gfunctor.tryInvoke(bean, methodName);
-                        }
+                        functor = context.get(methodName);
                     }
-                    // script of jexl method will do
-                    if (functor instanceof JexlScript) {
-                        return ((JexlScript) functor).execute(context, argv.length > 0 ? argv : null);
-                    } else if (functor instanceof JexlMethod) {
-                        vm = (JexlMethod) functor;
-                        cacheable = false;
-                    } else {
-                        xjexl = new JexlException.Method(node, methodName);
+                } else {
+                    JexlPropertyGet gfunctor = uberspect.getPropertyGet(bean, methodName);
+                    if (gfunctor != null) {
+                        functor = gfunctor.tryInvoke(bean, methodName);
                     }
                 }
+                // lambda, script or jexl method will do
+                if (functor instanceof Closure) {
+                    return call((Closure) functor, argv);
+                }
+                if (functor instanceof JexlScript) {
+                    return ((JexlScript) functor).execute(context, argv);
+                }
+                if (functor instanceof JexlMethod) {
+                    return ((JexlMethod) functor).invoke(bean, argv);
+                }
+                // if we did not find an exact match and we haven't tried yet,
+                // attempt to narrow the parameters and if this succeeds, try again
+                if (narrow && arithmetic.narrowArguments(argv)) {
+                    narrow = false;
+                } else {
+                    break;
+                }
             }
-            if (xjexl == null) {
+            // we have either evaluated and returned or might have found a method
+            if (vm != null) {
                 // vm cannot be null if xjexl is null
                 Object eval = vm.invoke(bean, argv);
                 // cache executor in volatile JexlNode.value
@@ -956,11 +989,11 @@ public class Interpreter extends ParserV
                     node.jjtSetValue(vm);
                 }
                 return eval;
+            } else {
+                xjexl = new JexlException.Method(node, methodName, null);
             }
-        } catch (InvocationTargetException e) {
-            xjexl = new JexlException(node, "method invocation error", e.getCause());
-        } catch (Exception e) {
-            xjexl = new JexlException(node, "method error", e);
+        } catch (Exception xany) {
+            xjexl = new JexlException(node, methodName, xany);
         }
         return invocationFailed(xjexl);
     }
@@ -1029,7 +1062,8 @@ public class Interpreter extends ParserV
                     ctor = uberspect.getConstructor(cobject, argv);
                 }
                 if (ctor == null) {
-                    xjexl = new JexlException.Method(node, cobject.toString());
+                    String dbgStr = cobject != null ? cobject.toString() : null;
+                    xjexl = new JexlException.Method(node, dbgStr, null);
                 }
             }
             if (xjexl == null) {
@@ -1040,10 +1074,9 @@ public class Interpreter extends ParserV
                 }
                 return instance;
             }
-        } catch (InvocationTargetException e) {
-            xjexl = new JexlException(node, "constructor invocation error", e.getCause());
-        } catch (Exception e) {
-            xjexl = new JexlException(node, "constructor error", e);
+        } catch (Exception xany) {
+            String dbgStr = cobject != null ? cobject.toString() : null;
+            xjexl = new JexlException(node, dbgStr, xany);
         }
         return invocationFailed(xjexl);
     }
@@ -1221,10 +1254,10 @@ public class Interpreter extends ParserV
                     // variable unknow in context and not (from) a register
                     && !(context.has(variableName.toString())
                     || (numChildren == 1
-                    && node.jjtGetChild(0) instanceof ASTIdentifier
-                    && ((ASTIdentifier) node.jjtGetChild(0)).getRegister() >= 0))) {
+                        && node.jjtGetChild(0) instanceof ASTIdentifier
+                        && ((ASTIdentifier) node.jjtGetChild(0)).getRegister() >= 0))) {
                 JexlException xjexl = propertyName != null
-                                      ? new JexlException.Property(node, propertyName)
+                                      ? new JexlException.Property(node, variableName.toString())
                                       : new JexlException.Variable(node, variableName.toString());
                 return unknownVariable(xjexl);
             }
@@ -1423,16 +1456,13 @@ public class Interpreter extends ParserV
                 }
                 return value;
             } catch (Exception xany) {
-                if (node == null) {
-                    throw new RuntimeException(xany);
-                } else {
-                    JexlException xjexl = new JexlException.Property(node, attribute.toString());
-                    if (strictArithmetic) {
-                        throw xjexl;
-                    }
-                    if (!silent) {
-                        logger.warn(xjexl.getMessage());
-                    }
+                String attrStr = attribute != null ? attribute.toString() : null;
+                JexlException xjexl = new JexlException.Property(node, attrStr, xany);
+                if (strictArithmetic) {
+                    throw xjexl;
+                }
+                if (!silent) {
+                    logger.warn(xjexl.getMessage());
                 }
             }
         }

Added: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Scope.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Scope.java?rev=1243029&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Scope.java (added)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Scope.java Sat Feb 11 11:26:42 2012
@@ -0,0 +1,254 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jexl3.internal;
+
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+/**
+ * A script scope, stores the declaration of parameters and local variables.
+ * @since 3.0
+ */
+public final class Scope {
+    /**
+     * The parent scope.
+     */
+    private Scope parent = null;
+    /**
+     * The number of parameters.
+     */
+    private int parms;
+    /**
+     * The number of local variables.
+     */
+    private int vars;
+    /**
+     * The number of hoisted variables.
+     */
+    private int hoisted;
+    /**
+     * The map of named registers aka script parameters.
+     * Each parameter is associated to a register and is materialized as an offset in the registers array used
+     * during evaluation.
+     */
+    private Map<String, Integer> namedRegisters = null;
+    /**
+     * The map of registers to parent registers when hoisted by closure.
+     */
+    private Map<Integer, Integer> hoistedRegisters = null;
+
+    /**
+     * Creates a new scope with a list of parameters.
+     * @param parameters the list of parameters
+     */
+    public Scope(Scope scope, String... parameters) {
+        if (parameters != null) {
+            parms = parameters.length;
+            namedRegisters = new LinkedHashMap<String, Integer>();
+            for (int p = 0; p < parms; ++p) {
+                namedRegisters.put(parameters[p], p);
+            }
+        } else {
+            parms = 0;
+        }
+        vars = 0;
+        hoisted = 0;
+        parent = scope;
+    }
+
+    @Override
+    public int hashCode() {
+        return namedRegisters == null ? 0 : parms ^ namedRegisters.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        return o instanceof Scope && equals((Scope) o);
+    }
+
+    /**
+     * Whether this frame is equal to another.
+     * @param frame the frame to compare to
+     * @return true if equal, false otherwise
+     */
+    public boolean equals(Scope frame) {
+        if (this == frame) {
+            return true;
+        } else if (frame == null || parms != frame.parms) {
+            return false;
+        } else if (namedRegisters == null) {
+            return frame.namedRegisters == null;
+        } else {
+            return namedRegisters.equals(frame.namedRegisters);
+        }
+    }
+
+    /**
+     * Checks whether an identifier is a local variable or argument, ie stored in a register.
+     * If this fails, attempt to solve by hoisting parent registers.
+     * @param name the register name
+     * @return the register index
+     */
+    public Integer getRegister(String name) {
+        return getRegister(name, true);
+    }
+
+    /**
+     * Checks whether an identifier is a local variable or argument, ie stored in a register.
+     * @param name the register name
+     * @param hoist whether solving by hoisting parent registers is allowed
+     * @return the register index
+     */
+    private Integer getRegister(String name, boolean hoist) {
+        Integer register = namedRegisters != null ? namedRegisters.get(name) : null;
+        if (register == null && hoist && parent != null) {
+            Integer pr = parent.getRegister(name, false);
+            if (pr != null) {
+                if (hoistedRegisters == null) {
+                    hoistedRegisters = new LinkedHashMap<Integer, Integer>();
+                }
+                register = Integer.valueOf(namedRegisters.size());
+                if (namedRegisters == null) {
+                    namedRegisters = new LinkedHashMap<String, Integer>();
+                }
+                namedRegisters.put(name, register);
+                hoistedRegisters.put(register, pr);
+                hoisted += 1;
+            }
+        }
+        return register;
+    }
+
+    /**
+     * Declares a parameter.
+     * <p>
+     * This method creates an new entry in the named register map.
+     * </p>
+     * @param name the parameter name
+     */
+    public void declareParameter(String name) {
+        if (namedRegisters == null) {
+            namedRegisters = new LinkedHashMap<String, Integer>();
+        } else if (vars > 0) {
+            throw new IllegalStateException("cant declare parameters after variables");
+        }
+        Integer register = namedRegisters.get(name);
+        if (register == null) {
+            register = Integer.valueOf(namedRegisters.size());
+            namedRegisters.put(name, register);
+            parms += 1;
+        }
+    }
+
+    /**
+     * Declares a local variable.
+     * <p>
+     * This method creates an new entry in the named register map.
+     * </p>
+     * @param name the variable name
+     * @return the register index storing this variable
+     */
+    public Integer declareVariable(String name) {
+        if (namedRegisters == null) {
+            namedRegisters = new LinkedHashMap<String, Integer>();
+        }
+        Integer register = namedRegisters.get(name);
+        if (register == null) {
+            register = Integer.valueOf(namedRegisters.size());
+            namedRegisters.put(name, register);
+            vars += 1;
+        }
+        return register;
+    }
+
+    /**
+     * Creates a frame by copying values up to the number of parameters.
+     * @param values the argument values
+     * @return the arguments array
+     */
+    public Frame createFrame(Frame caller) {
+        if (namedRegisters != null) {
+            Object[] arguments = new Object[namedRegisters.size()];
+            if (caller != null && hoistedRegisters != null && parent != null) {
+                for (Map.Entry<Integer, Integer> hoist : hoistedRegisters.entrySet()) {
+                    Integer target = hoist.getKey();
+                    Integer source = hoist.getValue();
+                    Object arg = caller.registers[source.intValue()];
+                    arguments[target.intValue()] = arg;
+                }
+            }
+            return new Frame(arguments, namedRegisters.keySet().toArray(new String[namedRegisters.size()]));
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Gets the (maximum) number of arguments this script expects.
+     * @return the number of parameters
+     */
+    public int getArgCount() {
+        return parms;
+    }
+
+    /**
+     * Gets this script registers, i.e. parameters and local variables.
+     * @return the register names
+     */
+    public String[] getRegisters() {
+        return namedRegisters != null ? namedRegisters.keySet().toArray(new String[0]) : new String[0];
+    }
+
+    /**
+     * Gets this script parameters, i.e. registers assigned before creating local variables.
+     * @return the parameter names
+     */
+    public String[] getParameters() {
+        if (namedRegisters != null && parms > 0) {
+            String[] pa = new String[parms];
+            int p = 0;
+            for (Map.Entry<String, Integer> entry : namedRegisters.entrySet()) {
+                if (entry.getValue().intValue() < parms) {
+                    pa[p++] = entry.getKey();
+                }
+            }
+            return pa;
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * Gets this script local variable, i.e. registers assigned to local variables.
+     * @return the parameter names
+     */
+    public String[] getLocalVariables() {
+        if (namedRegisters != null && vars > 0) {
+            String[] pa = new String[parms];
+            int p = 0;
+            for (Map.Entry<String, Integer> entry : namedRegisters.entrySet()) {
+                if (entry.getValue().intValue() >= parms) {
+                    pa[p++] = entry.getKey();
+                }
+            }
+            return pa;
+        } else {
+            return null;
+        }
+    }
+    
+}

Propchange: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Scope.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/Script.java Sat Feb 11 11:26:42 2012
@@ -57,7 +57,7 @@ public class Script implements JexlScrip
         if (script.jjtGetNumChildren() < 1) {
             return null;
         }
-        Engine.Frame frame = script.createFrame((Object[]) null);
+        Frame frame = script.createFrame((Object[]) null);
         Interpreter interpreter = jexl.createInterpreter(context, frame);
         return interpreter.interpret(script.jjtGetChild(0));
     }
@@ -94,7 +94,7 @@ public class Script implements JexlScrip
      */
     @Override
     public Object execute(JexlContext context) {
-        Engine.Frame frame = script.createFrame((Object[]) null);
+        Frame frame = script.createFrame((Object[]) null);
         Interpreter interpreter = jexl.createInterpreter(context, frame);
         return interpreter.interpret(script);
     }
@@ -104,7 +104,7 @@ public class Script implements JexlScrip
      */
     @Override
     public Object execute(JexlContext context, Object... args) {
-        Engine.Frame frame = script.createFrame(args);
+        Frame frame = script.createFrame(args != null && args.length > 0? args : null);
         Interpreter interpreter = jexl.createInterpreter(context, frame);
         return interpreter.interpret(script);
     }

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/internal/TemplateEngine.java Sat Feb 11 11:26:42 2012
@@ -257,7 +257,7 @@ public final class TemplateEngine extend
         @Override
         public final TemplateExpression prepare(JexlContext context) {
             try {
-                Engine.Frame frame = context instanceof TemplateContext
+                Frame frame = context instanceof TemplateContext
                                      ? ((TemplateContext) context).getFrame()
                                      : null;
                 Interpreter interpreter = jexl.createInterpreter(context, frame);
@@ -275,7 +275,7 @@ public final class TemplateEngine extend
         @Override
         public final Object evaluate(JexlContext context) {
             try {
-                Engine.Frame frame = context instanceof TemplateContext
+                Frame frame = context instanceof TemplateContext
                                      ? ((TemplateContext) context).getFrame()
                                      : null;
                 Interpreter interpreter = jexl.createInterpreter(context, frame);
@@ -681,7 +681,7 @@ public final class TemplateEngine extend
      * @return the unified expression instance
      * @throws JexlException if an error occur during parsing
      */
-    private TemplateExpression parseExpression(String expr, Engine.Scope scope) {
+    private TemplateExpression parseExpression(String expr, Scope scope) {
         final int size = expr.length();
         ExpressionBuilder builder = new ExpressionBuilder(0);
         StringBuilder strb = new StringBuilder(size);
@@ -893,7 +893,7 @@ public final class TemplateEngine extend
             if (reader == null) {
                 throw new NullPointerException("null input");
             }
-            Engine.Scope scope = new Engine.Scope(parms);
+            Scope scope = new Scope(null, parms);
             prefix = directive;
             List<Block> blocks = readTemplate(prefix, reader);
             List<TemplateExpression> uexprs = new ArrayList<TemplateExpression>();
@@ -973,7 +973,7 @@ public final class TemplateEngine extend
 
         @Override
         public TemplateScript prepare(JexlContext context) {
-            Engine.Frame frame = script.createFrame((Object[]) null);
+            Frame frame = script.createFrame((Object[]) null);
             TemplateContext tcontext = new TemplateContext(context, frame, exprs, null);
             TemplateExpression[] immediates = new TemplateExpression[exprs.length];
             for (int e = 0; e < exprs.length; ++e) {
@@ -989,7 +989,7 @@ public final class TemplateEngine extend
 
         @Override
         public void evaluate(JexlContext context, Writer writer, Object... args) {
-            Engine.Frame frame = script.createFrame(args);
+            Frame frame = script.createFrame(args);
             TemplateContext tcontext = new TemplateContext(context, frame, exprs, writer);
             Interpreter interpreter = jexl.createInterpreter(tcontext, frame);
             interpreter.interpret(script);
@@ -1009,7 +1009,7 @@ public final class TemplateEngine extend
         /** The writer used to output. */
         private final Writer writer;
         /** The call frame. */
-        private final Engine.Frame frame;
+        private final Frame frame;
 
         /**
          * Creates a TemplateScript context instance.
@@ -1018,7 +1018,7 @@ public final class TemplateEngine extend
          * @param expressions the list of TemplateExpression from the TemplateScript to evaluate
          * @param out the output writer
          */
-        protected TemplateContext(JexlContext jcontext, Engine.Frame jframe,
+        protected TemplateContext(JexlContext jcontext, Frame jframe,
                                   UnifiedExpression[] expressions, Writer out) {
             wrap = jcontext;
             frame = jframe;
@@ -1030,7 +1030,7 @@ public final class TemplateEngine extend
          * Gets this context calling frame.
          * @return the engine frame
          */
-        public Engine.Frame getFrame() {
+        public Frame getFrame() {
             return frame;
         }
 

Copied: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java (from r1211892, commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java)
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java?p2=commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java&p1=commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java&r1=1211892&r2=1243029&rev=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlLambda.java Sat Feb 11 11:26:42 2012
@@ -16,81 +16,32 @@
  */
 package org.apache.commons.jexl3.parser;
 
-import org.apache.commons.jexl3.internal.Engine;
+import org.apache.commons.jexl3.internal.Frame;
 
 /**
  * Enhanced script to allow parameters declaration.
  */
-public class ASTJexlScript extends JexlNode {
-    /** The script scope. */
-    private Engine.Scope scope = null;
-
-    public ASTJexlScript(int id) {
+public class ASTJexlLambda extends ASTJexlScript {
+    public ASTJexlLambda(int id) {
         super(id);
     }
 
-    public ASTJexlScript(Parser p, int id) {
+    public ASTJexlLambda(Parser p, int id) {
         super(p, id);
     }
-
-    @Override
-    public Object jjtAccept(ParserVisitor visitor, Object data) {
-        return visitor.visit(this, data);
-    }
-
-    /**
-     * Sets the parameters and registers
-     * @param theScope the scope
-     */
-    public void setScope(Engine.Scope theScope) {
-        this.scope = theScope;
-    }
-    
-    /**
-     * Gets this script scope.
-     */
-    public Engine.Scope getScope() {
-        return scope;
-    }
-    
+        
     /**
      * Creates an array of arguments by copying values up to the number of parameters.
      * @param values the argument values
      * @return the arguments array
      */
-    public Engine.Frame createFrame(Object... values) {
-        return scope != null? scope.createFrame(values) : null;
-    }
-    
-    /**
-     * Gets the (maximum) number of arguments this script expects.
-     * @return the number of parameters
-     */
-    public int getArgCount() {
-        return scope != null? scope.getArgCount() : 0;
-    }
-    
-    /**
-     * Gets this script registers, i.e. parameters and local variables.
-     * @return the register names
-     */
-    public String[] getRegisters() {
-        return scope != null? scope.getRegisters() : null;
-    }
-
-    /**
-     * Gets this script parameters, i.e. registers assigned before creating local variables.
-     * @return the parameter names
-     */
-    public String[] getParameters() {
-        return scope != null? scope.getParameters() : null;
-    }
-
-    /**
-     * Gets this script local variable, i.e. registers assigned to local variables.
-     * @return the parameter names
-     */
-    public String[] getLocalVariables() {
-        return scope != null? scope.getLocalVariables() : null;
+    public Frame createFrame(Frame frame, Object... values) {
+        if (scope != null) {
+            Frame cframe = scope.createFrame(frame);
+            if (cframe != null) {
+                return cframe.assign(values);
+            }
+        }
+        return null;
     }
 }

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/ASTJexlScript.java Sat Feb 11 11:26:42 2012
@@ -17,13 +17,15 @@
 package org.apache.commons.jexl3.parser;
 
 import org.apache.commons.jexl3.internal.Engine;
+import org.apache.commons.jexl3.internal.Frame;
+import org.apache.commons.jexl3.internal.Scope;
 
 /**
  * Enhanced script to allow parameters declaration.
  */
 public class ASTJexlScript extends JexlNode {
     /** The script scope. */
-    private Engine.Scope scope = null;
+    protected Scope scope = null;
 
     public ASTJexlScript(int id) {
         super(id);
@@ -42,14 +44,14 @@ public class ASTJexlScript extends JexlN
      * Sets the parameters and registers
      * @param theScope the scope
      */
-    public void setScope(Engine.Scope theScope) {
+    public void setScope(Scope theScope) {
         this.scope = theScope;
     }
     
     /**
      * Gets this script scope.
      */
-    public Engine.Scope getScope() {
+    public Scope getScope() {
         return scope;
     }
     
@@ -58,8 +60,13 @@ public class ASTJexlScript extends JexlN
      * @param values the argument values
      * @return the arguments array
      */
-    public Engine.Frame createFrame(Object... values) {
-        return scope != null? scope.createFrame(values) : null;
+    public Frame createFrame(Object... values) {
+        if (scope != null) {
+            Frame frame = scope.createFrame(null);
+            return frame != null? frame.assign(values) : null;
+        } else {
+            return null;
+        }
     }
     
     /**

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/JexlParser.java Sat Feb 11 11:26:42 2012
@@ -16,21 +16,25 @@
  */
 package org.apache.commons.jexl3.parser;
 
-import org.apache.commons.jexl3.internal.Engine;
+import java.io.Reader;
+import java.util.Stack;
 import org.apache.commons.jexl3.JexlException;
 import org.apache.commons.jexl3.JexlInfo;
+import org.apache.commons.jexl3.internal.Engine;
+import org.apache.commons.jexl3.internal.Scope;
 
 /**
  * The base class for parsing, manages the parameter/local variable frame.
  * @author henri
  */
-public class JexlParser extends StringParser {
+public abstract class JexlParser extends StringParser {
     /**
      * The map of named registers aka script parameters.
      * Each parameter is associated to a register and is materialized as an offset in the registers array used
      * during evaluation.
      */
-    protected Engine.Scope frame;
+    protected Scope frame = null;
+    protected Stack<Scope> frames = new Stack<Scope>();
 
     /**
      * Sets the frame to use bythis parser.
@@ -39,7 +43,7 @@ public class JexlParser extends StringPa
      * </p>
      * @param theFrame the register map
      */
-    public void setFrame(Engine.Scope theFrame) {
+    public void setFrame(Scope theFrame) {
         frame = theFrame;
     }
     
@@ -51,10 +55,31 @@ public class JexlParser extends StringPa
      * </p>
      * @return the named register map
      */
-    public Engine.Scope getFrame() {
+    public Scope getFrame() {
         return frame;
     }
     
+    /**
+     * Create a new local variable frame and push it as current scope.
+     */
+    public void pushFrame() {
+        if (frame != null) {
+            frames.push(frame);
+        }
+        frame = new Scope(frame, (String[])null);
+    }
+    
+    /**
+     * Pops back to previous local variable frame.
+     */
+    public void popFrame() {
+        if (!frames.isEmpty()) {
+            frame = frames.pop();
+        } else {
+            frame = null;
+        }
+    }
+    
 
     /**
      * Checks whether an identifier is a local variable or argument, ie stored in a register. 
@@ -82,12 +107,27 @@ public class JexlParser extends StringPa
      */
     public void declareVariable(ASTVar identifier, String image) {
         if (frame == null) {
-            frame = new Engine.Scope((String[])null);
+            frame = new Scope(null, (String[])null);
         }
         Integer register = frame.declareVariable(image);
         identifier.setRegister(register.intValue());
         identifier.image = image;
     }
+    
+    /**
+     * Declares a local parameter.
+     * <p>
+     * This method creates an new entry in the named register map.
+     * </p>
+     * @param identifier the identifier used to declare
+     * @param image the variable name
+     */
+    public void declareParameter(String image) {
+        if (frame == null) {
+            frame = new Scope(null, (String[])null);
+        }
+        frame.declareParameter(image);
+    }
 
     /**
      * Default implementation does nothing but is overriden by generated code.
@@ -125,5 +165,13 @@ public class JexlParser extends StringPa
             }
             throw new JexlException.Parsing(dbgInfo, "Ambiguous statement, missing ';' between expressions", null);
         }
+        if (n instanceof ASTJexlScript) {
+            ASTJexlScript script = (ASTJexlScript) n;
+            // reaccess in case local variables have been declared
+            if (script.getScope() != frame) {
+                script.setScope(frame);
+            }
+            popFrame();
+        }
     }
 }

Modified: commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt (original)
+++ commons/proper/jexl/trunk/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt Sat Feb 11 11:26:42 2012
@@ -32,12 +32,14 @@ package org.apache.commons.jexl3.parser;
 
 import java.io.Reader;
 import org.apache.commons.jexl3.JexlInfo;
+import org.apache.commons.jexl3.internal.Engine;
+import org.apache.commons.jexl3.internal.Scope;
 
 public class Parser extends JexlParser
 {
     public boolean ALLOW_REGISTERS = false;
 
-    public ASTJexlScript parse(Reader reader, JexlInfo info)
+    public ASTJexlScript parse(Reader reader, Scope scope, JexlInfo info)
         throws ParseException {
         /*
          * If registers are allowed, the default parser state has to be REGISTERS.
@@ -51,9 +53,10 @@ public class Parser extends JexlParser
          *  safe - it's a pain to remember
          */
 
-        ASTJexlScript tree = JexlScript();
-        tree.value = info;
-        return tree;
+        frame = scope;
+        ASTJexlScript script = JexlScript(scope);
+        script.value = info;
+        return script;
     }
 
 }
@@ -92,6 +95,7 @@ PARSER_END(Parser)
     | < TRUE : "true" >
     | < FALSE : "false" >
     | < RETURN : "return" >
+    | < FUNCTION : "function" >
 }
 
 <FOR_EACH_IN> TOKEN : /* foreach in */
@@ -110,6 +114,7 @@ PARSER_END(Parser)
     | < COLON : ":" >
     | < COMMA : "," >
     | < DOT : "." >
+    | < LAMBDA : "->" >
 }
 
 <*> TOKEN : { /* CONDITIONALS */
@@ -185,7 +190,9 @@ PARSER_END(Parser)
  *      Statements
  ***************************************/
 
-ASTJexlScript JexlScript() : {}
+ASTJexlScript JexlScript(Scope frame) : {
+    jjtThis.setScope(frame);
+}
 {
    ( Statement() )* <EOF>
    { return jjtThis;}
@@ -519,12 +526,12 @@ void SizeFunction() : {}
 void Function() #FunctionNode: {}
 {
 
-   Identifier() <COLON> Identifier() <LPAREN>[ Expression() ( <COMMA> Expression() )* ] <RPAREN>
+   Identifier(true) <COLON> Identifier() <LPAREN>[ Expression() ( <COMMA> Expression() )* ] <RPAREN>
 }
 
 void Method() #MethodNode: {}
 {
-   Identifier() <LPAREN>[ Expression() ( <COMMA> Expression() )* ] <RPAREN>
+   Identifier(true) <LPAREN>[ Expression() ( <COMMA> Expression() )* ] <RPAREN>
 }
 
 void AnyMethod() #void : {}
@@ -544,6 +551,22 @@ void Constructor() #ConstructorNode() : 
   <NEW> <LPAREN>[ Expression() ( <COMMA> Expression() )* ] <RPAREN>
 }
 
+void DeclareParm() #void :
+{
+    Token t;
+}
+{
+    t=<IDENTIFIER> { declareParameter(t.image); }
+}
+
+void Lambda() #JexlLambda() : {
+   pushFrame();
+}
+{
+  <FUNCTION> <LPAREN>[ DeclareParm() ( <COMMA> DeclareParm() )* ] <RPAREN> Block()
+}
+
+
 
 /***************************************
  *     References
@@ -564,6 +587,7 @@ void PrimaryExpression() #void : {}
   LOOKAHEAD( <LBRACKET> (Expression() | <RBRACKET>) ) ArrayLiteral()
 |
   Literal()
+| Lambda()
 }
 
 void ArrayAccess() : {}

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArithmeticTest.java Sat Feb 11 11:26:42 2012
@@ -123,7 +123,7 @@ public class ArithmeticTest extends Jexl
         asserter.setVariable("aBigDecimal", new BigDecimal("8.8"));
 
         asserter.assertExpression("-3", new Integer("-3"));
-        asserter.assertExpression("-3.0", new Float("-3.0"));
+        asserter.assertExpression("-3.0", new Double("-3.0"));
         asserter.assertExpression("-aByte", new Byte((byte) -1));
         asserter.assertExpression("-aShort", new Short((short) -2));
         asserter.assertExpression("-anInteger", new Integer(-3));

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayAccessTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayAccessTest.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayAccessTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayAccessTest.java Sat Feb 11 11:26:42 2012
@@ -21,6 +21,7 @@ import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
 
+import org.apache.commons.jexl3.internal.Debugger;
 import org.apache.commons.jexl3.junit.Asserter;
 
 

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/ArrayLiteralTest.java Sat Feb 11 11:26:42 2012
@@ -51,7 +51,7 @@ public class ArrayLiteralTest extends Je
         JexlContext jc = new MapContext();
 
         Object o = e.evaluate( jc );
-        Object[] check = { new Float(5), new Integer(10) };
+        Object[] check = { new Double(5), new Integer(10) };
         assertTrue( Arrays.equals(check, (Object[])o) );
         assertTrue (o.getClass().isArray() && o.getClass().getComponentType().equals(Number.class));
     }

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AssignTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AssignTest.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AssignTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/AssignTest.java Sat Feb 11 11:26:42 2012
@@ -157,6 +157,4 @@ public class AssignTest extends JexlTest
         o = JEXL.getProperty(quux, "['froboz']['value']");
         assertEquals("Result is not 1000", new Integer(1000), o);
     }
-
-
 }
\ No newline at end of file

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/IssuesTest.java Sat Feb 11 11:26:42 2012
@@ -17,7 +17,6 @@
 package org.apache.commons.jexl3;
 
 import org.apache.commons.jexl3.internal.Engine;
-import org.apache.commons.jexl3.internal.Script;
 import org.apache.commons.jexl3.internal.TemplateEngine;
 import org.apache.commons.jexl3.internal.Debugger;
 import java.math.BigDecimal;
@@ -35,7 +34,7 @@ public class IssuesTest extends JexlTest
     public IssuesTest() {
         super("IssuesTest", null);
     }
-    
+
     @Override
     public void setUp() throws Exception {
         // ensure jul logging is only error to avoid warning in silent mode
@@ -56,7 +55,7 @@ public class IssuesTest extends JexlTest
         assertEquals(42.0d, vars.get("d"));
         assertEquals(56.3f, vars.get("e"));
         assertEquals(56.3f, vars.get("f"));
-        assertEquals(63.5f, vars.get("g"));
+        assertEquals(63.5d, vars.get("g"));
         assertEquals(0x10, vars.get("h"));
         assertEquals(010, vars.get("i"));
         assertEquals(0x10L, vars.get("j"));
@@ -496,6 +495,10 @@ public class IssuesTest extends JexlTest
         public String getPropA() {
             return propA;
         }
+        
+        public String uppercase(String str) {
+            return str.toUpperCase();
+        }
     }
 
     public void test105() throws Exception {
@@ -617,10 +620,6 @@ public class IssuesTest extends JexlTest
         RichContext(JexlEngine jexl, A105 a105) {
             super(jexl, a105);
         }
-
-        public String uppercase(String str) {
-            return str.toUpperCase();
-        }
     }
 
     public void testRichContext() throws Exception {
@@ -719,131 +718,25 @@ public class IssuesTest extends JexlTest
         assertTrue((Boolean) result);
     }
 
-    public void testStringIdentifier() throws Exception {
-        JexlEngine jexl = new Engine();
-        Map<String, String> foo = new HashMap<String, String>();
 
-        JexlContext jc = new MapContext();
-        jc.set("foo", foo);
-        foo.put("q u u x", "456");
-        JexlExpression e = jexl.createExpression("foo.\"q u u x\"");
-        Object result = e.evaluate(jc);
-        assertEquals("456", result);
-        e = jexl.createExpression("foo.'q u u x'");
-        result = e.evaluate(jc);
-        assertEquals("456", result);
-        JexlScript s = jexl.createScript("foo.\"q u u x\"");
-        result = s.execute(jc);
-        assertEquals("456", result);
-        s = jexl.createScript("foo.'q u u x'");
-        result = s.execute(jc);
-        assertEquals("456", result);
-        
-        Debugger dbg = new Debugger();
-//        dbg.debug(((Script)s).script);
-//        String dbgdata = dbg.data();
-//        assertEquals("foo.'q u u x';", dbgdata);
-    }
-        
-    public static class Container {
-        String value0;
-        int value1;
-        public Container(String name, int number) {
-            value0 = name;
-            value1 = number;
-        }
-        
-        public Object getProperty(String name) {
-            if ("name".equals(name)) {
-                return value0;
-            } else if ("number".equals(name)) {
-                return value1;
-            } else {
-                return null;
-            }
-        }
-        public Object getProperty(int ref) {
-            if (0 == ref) {
-                return value0;
-            } else if (1 == ref) {
-                return value1;
-            } else {
-                return null;
-            }
+
+
+    public static class Foo125 {
+        public String method() {
+            return "OK";
         }
-        
-        public void setProperty(String name, String value) {
-            if ("name".equals(name)) {
-                this.value0 = value;
-            }
-        }  
-        
-        public void setProperty(String name, int value) {
-            if ("number".equals(name)) {
-                this.value1 = value;
-            }
-        }        
-        public void setProperty(int ref, String value) {
-            if (0 == ref) {
-                this.value0 = value;
-            }
-        }  
-        
-        public void setProperty(int ref, int value) {
-            if (1 == ref) {
-                this.value1 = value;
-            }
+    }
+
+    public static class Foo125Context extends ObjectContext<Foo125> {
+        public Foo125Context(JexlEngine engine, Foo125 wrapped) {
+            super(engine, wrapped);
         }
     }
 
-    public void test119() throws Exception {
+    public void test125() throws Exception {
         JexlEngine jexl = new Engine();
-        Container quux = new Container("quux", 42);
-        JexlScript get;
-        Object result;
-        
-        JexlScript getName = jexl.createScript("foo.property.name", "foo");
-        result = getName.execute(null, quux);
-        assertEquals("quux", result);
-        
-        JexlScript get0 = jexl.createScript("foo.property.0", "foo");
-        result = get0.execute(null, quux);
-        assertEquals("quux", result);
-        
-        JexlScript getNumber = jexl.createScript("foo.property.number", "foo");
-        result = getNumber.execute(null, quux);
-        assertEquals(42, result);
-        
-        JexlScript get1 = jexl.createScript("foo.property.1", "foo");
-        result = get1.execute(null, quux);
-        assertEquals(42, result);
-        
-        JexlScript setName = jexl.createScript("foo.property.name = $0", "foo", "$0");
-        setName.execute(null, quux, "QUUX");
-        result = getName.execute(null, quux);
-        assertEquals("QUUX", result);
-        result = get0.execute(null, quux);
-        assertEquals("QUUX", result);
-        
-        JexlScript set0 = jexl.createScript("foo.property.0 = $0", "foo", "$0");
-        set0.execute(null, quux, "BAR");
-        result = getName.execute(null, quux);
-        assertEquals("BAR", result);
-        result = get0.execute(null, quux);
-        assertEquals("BAR", result);
-        
-        JexlScript setNumber = jexl.createScript("foo.property.number = $0", "foo", "$0");
-        setNumber.execute(null, quux, -42);
-        result = getNumber.execute(null, quux);
-        assertEquals(-42, result);
-        result = get1.execute(null, quux);
-        assertEquals(-42, result);
-        
-        JexlScript set1 = jexl.createScript("foo.property.1 = $0", "foo", "$0");
-        set1.execute(null, quux, 24);
-        result = getNumber.execute(null, quux);
-        assertEquals(24, result);
-        result = get1.execute(null, quux);
-        assertEquals(24, result);
+        JexlExpression e = jexl.createExpression("method()");
+        JexlContext jc = new Foo125Context(jexl, new Foo125());
+        assertEquals("OK", e.evaluate(jc));
     }
 }

Modified: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/JexlTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/JexlTest.java?rev=1243029&r1=1243028&r2=1243029&view=diff
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/JexlTest.java (original)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/JexlTest.java Sat Feb 11 11:26:42 2012
@@ -649,7 +649,7 @@ public class JexlTest extends JexlTestCa
         Foo foo = new Foo();
         jc.set("foo", foo);
         Parser parser = new Parser(new StringReader(";"));
-        parser.parse(new StringReader("aString = 'World';"), null);
+        parser.parse(new StringReader("aString = 'World';"), null, null);
 
         assertExpression(jc, "hello = 'world'", "world");
         assertEquals("hello variable not changed", "world", jc.get("hello"));

Added: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/LambdaTest.java
URL: http://svn.apache.org/viewvc/commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/LambdaTest.java?rev=1243029&view=auto
==============================================================================
--- commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/LambdaTest.java (added)
+++ commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/LambdaTest.java Sat Feb 11 11:26:42 2012
@@ -0,0 +1,70 @@
+/*
+ * 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.
+ */
+package org.apache.commons.jexl3;
+
+import org.apache.commons.jexl3.internal.Engine;
+
+/**
+ * Tests function/lambda/closure features.
+ */
+public class LambdaTest extends JexlTestCase {
+
+    public LambdaTest(String testName) {
+        super(testName);
+    }
+    
+    public void testScriptArguments() throws Exception {
+        JexlEngine jexl = new Engine();
+        JexlScript s = jexl.createScript("{ x + x }", "x");
+        String strs = "s(21)";
+        JexlScript s42 = jexl.createScript("s(21)", "s");
+        Object result = s42.execute(null, s);
+        assertEquals(42, result);
+    }
+
+    public void testLambda() throws Exception {
+        JexlEngine jexl = new Engine();
+        String strs = "var s = function(x) { x + x }; s(21)";
+        JexlScript s42 = jexl.createScript(strs);
+        Object result = s42.execute(null);
+        assertEquals(42, result);
+        strs = "var s = function(x, y) { x + y }; s(15, 27)";
+        s42 = jexl.createScript(strs);
+        result = s42.execute(null);
+        assertEquals(42, result);
+    }
+    
+    public void testLambdaClosure() throws Exception {
+        JexlEngine jexl = new Engine();
+        String strs = "var t = 20; var s = function(x, y) { x + y + t}; s(15, 7)";
+        JexlScript s42 = jexl.createScript(strs);
+        Object result = s42.execute(null);
+        assertEquals(42, result);
+        strs = "var t = 19; var s = function(x, y) { var t = 20; x + y + t}; s(15, 7)";
+        s42 = jexl.createScript(strs);
+        result = s42.execute(null);
+        assertEquals(42, result);
+        strs = "var t = 20; var s = function(x, y) {x + y + t}; t = 54; s(15, 7)";
+        s42 = jexl.createScript(strs);
+        result = s42.execute(null);
+        assertEquals(42, result);
+        strs = "var t = 19; var s = function(x, y) { var t = 20; x + y + t}; t = 54; s(15, 7)";
+        s42 = jexl.createScript(strs);
+        result = s42.execute(null);
+        assertEquals(42, result);
+    }
+}

Propchange: commons/proper/jexl/trunk/src/test/java/org/apache/commons/jexl3/LambdaTest.java
------------------------------------------------------------------------------
    svn:eol-style = native