This is an automated email from the ASF dual-hosted git repository.

henrib pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-jexl.git


The following commit(s) were added to refs/heads/master by this push:
     new 753d6e6  JEXL-307: tidy API (made JexlOptions a concrete non derivable 
class), try to ensure lexical interpretation on lexical feature, allow runtime 
options to be controlled through pragma (jexl.options) Task #JEXL-307 - 
Variable redeclaration option
753d6e6 is described below

commit 753d6e634d94022871b481896db78e323d7ccfed
Author: henrib <hen...@apache.org>
AuthorDate: Tue Nov 12 23:00:39 2019 +0100

    JEXL-307: tidy API (made JexlOptions a concrete non derivable class), try 
to ensure lexical interpretation on lexical feature, allow runtime options to 
be controlled through pragma (jexl.options)
    Task #JEXL-307 - Variable redeclaration option
---
 .../java/org/apache/commons/jexl3/JexlBuilder.java |  21 +-
 .../java/org/apache/commons/jexl3/JexlContext.java |  16 ++
 .../java/org/apache/commons/jexl3/JexlEngine.java  |  10 +-
 .../java/org/apache/commons/jexl3/JexlInfo.java    |  11 +
 .../java/org/apache/commons/jexl3/JexlOptions.java | 223 +++++++++++++++--
 .../org/apache/commons/jexl3/internal/Engine.java  |  68 ++++-
 .../apache/commons/jexl3/internal/Interpreter.java |  33 ++-
 .../commons/jexl3/internal/InterpreterBase.java    |   7 +-
 .../org/apache/commons/jexl3/internal/Options.java | 274 ---------------------
 .../org/apache/commons/jexl3/internal/Script.java  |  45 +++-
 .../jexl3/internal/TemplateInterpreter.java        |   2 +-
 .../org/apache/commons/jexl3/AntishCallTest.java   |  15 +-
 .../org/apache/commons/jexl3/Issues300Test.java    |  25 +-
 .../java/org/apache/commons/jexl3/JXLTTest.java    |   3 +-
 .../org/apache/commons/jexl3/JexlEvalContext.java  |   3 +-
 .../org/apache/commons/jexl3/JexlTestCase.java     |   2 +-
 .../java/org/apache/commons/jexl3/LexicalTest.java |  43 ++++
 17 files changed, 431 insertions(+), 370 deletions(-)

diff --git a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java 
b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
index 35c8d5f..744dfe1 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlBuilder.java
@@ -18,7 +18,6 @@
 package org.apache.commons.jexl3;
 
 import org.apache.commons.jexl3.internal.Engine;
-import org.apache.commons.jexl3.internal.Options;
 import org.apache.commons.jexl3.introspection.JexlSandbox;
 import org.apache.commons.jexl3.introspection.JexlUberspect;
 import org.apache.commons.logging.Log;
@@ -81,7 +80,7 @@ public class JexlBuilder {
     private Boolean cancellable = null;
     
     /** The options. */
-    private final Options options = new Options();
+    private final JexlOptions options = new JexlOptions();
 
     /** Whether getVariables considers all potential equivalent syntactic 
forms. */
     private Boolean collectAll = null;
@@ -109,23 +108,7 @@ public class JexlBuilder {
 
     /** The features. */
     private JexlFeatures features = null;
-        
-    /**
-     * Sets the default (static, shared) option flags.
-     * <p>
-     * Whenever possible, we recommend using JexlBuilder methods to 
unambiguously instantiate a JEXL
-     * engine; this method should only be used for testing / validation.
-     * <p>A '+flag' or 'flag' will set the option named 'flag' as true, 
'-flag' set as false.
-     * The possible flag names are:
-     * cancellable, strict, silent, safe, lexical, antish, lexicalShade
-     * <p>Calling JexlBuilder.setDefaultOptions("+safe") once before JEXL 
engine creation 
-     * may ease validating JEXL3.2 in your environment.
-     * @param flags the flags to set 
-     */
-    public static void setDefaultOptions(String...flags) {
-        Options.setDefaultFlags(flags);
-    }
-    
+
     /**
      * Sets the JexlUberspect instance the engine will use.
      *
diff --git a/src/main/java/org/apache/commons/jexl3/JexlContext.java 
b/src/main/java/org/apache/commons/jexl3/JexlContext.java
index fdc85a3..ce730f7 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlContext.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlContext.java
@@ -159,4 +159,20 @@ public interface JexlContext {
          */
         JexlOptions getEngineOptions();
     }
+     
+    /**
+     * A marker interface of the JexlContext that processes pragmas.
+     * It is called by the engine before interpreter creation; as a marker of
+     * JexlContext, it is expected to have access and interact with the context
+     * instance.
+     * @since 3.2
+     */
+    interface PragmaProcessor {
+        /**
+         * Process one pragma.
+         * @param key the key
+         * @param value the value
+         */
+        void processPragma(String key, Object value);
+    }
 }
diff --git a/src/main/java/org/apache/commons/jexl3/JexlEngine.java 
b/src/main/java/org/apache/commons/jexl3/JexlEngine.java
index bc41a82..7b9d198 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlEngine.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlEngine.java
@@ -352,7 +352,7 @@ public abstract class JexlEngine {
      * @return A {@link JexlScript} which can be executed using a {@link 
JexlContext}
      * @throws JexlException if there is a problem parsing the script
      */
-    public abstract JexlScript createScript(JexlFeatures features, JexlInfo 
info, String source, String[] names);
+    public abstract JexlScript createScript(JexlFeatures features, JexlInfo 
info, String source, String... names);
 
     /**
      * Creates a JexlScript from a String containing valid JEXL syntax.
@@ -365,7 +365,7 @@ public abstract class JexlEngine {
      * @return A {@link JexlScript} which can be executed using a {@link 
JexlContext}
      * @throws JexlException if there is a problem parsing the script
      */
-    public final JexlScript createScript(JexlInfo info, String source, 
String[] names) {
+    public final JexlScript createScript(JexlInfo info, String source, 
String... names) {
         return createScript(null, info, source, names);
     }
 
@@ -378,7 +378,7 @@ public abstract class JexlEngine {
      * @throws JexlException if there is a problem parsing the script.
      */
     public final JexlScript createScript(String scriptText) {
-        return createScript(null, null, scriptText, null);
+        return createScript(null, null, scriptText, (String[]) null);
     }
 
     /**
@@ -404,7 +404,7 @@ public abstract class JexlEngine {
      * @throws JexlException if there is a problem reading or parsing the 
script.
      */
     public final JexlScript createScript(File scriptFile) {
-        return createScript(null, null, readSource(scriptFile), null);
+        return createScript(null, null, readSource(scriptFile), (String[]) 
null);
     }
 
     /**
@@ -445,7 +445,7 @@ public abstract class JexlEngine {
      * @throws JexlException if there is a problem reading or parsing the 
script.
      */
     public final JexlScript createScript(URL scriptUrl) {
-        return createScript(null, readSource(scriptUrl), null);
+        return createScript(null, readSource(scriptUrl), (String[]) null);
     }
 
     /**
diff --git a/src/main/java/org/apache/commons/jexl3/JexlInfo.java 
b/src/main/java/org/apache/commons/jexl3/JexlInfo.java
index 19a4a72..296138b 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlInfo.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlInfo.java
@@ -17,6 +17,8 @@
 
 package org.apache.commons.jexl3;
 
+import org.apache.commons.jexl3.internal.Script;
+
 /**
  * Helper class to carry information such as a url/file name, line and column 
for
  * debugging information reporting.
@@ -182,5 +184,14 @@ public class JexlInfo {
     public final int getColumn() {
         return column;
     }
+    
+    /**
+     * Gets the info from a script.
+     * @param script the script
+     * @return the info
+     */
+    public static JexlInfo from(JexlScript script) {
+        return script instanceof Script? ((Script) script).getInfo() :  null;
+    }
 }
 
diff --git a/src/main/java/org/apache/commons/jexl3/JexlOptions.java 
b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
index dc35074..c650e9b 100644
--- a/src/main/java/org/apache/commons/jexl3/JexlOptions.java
+++ b/src/main/java/org/apache/commons/jexl3/JexlOptions.java
@@ -18,6 +18,7 @@
 package org.apache.commons.jexl3;
 
 import java.math.MathContext;
+import org.apache.commons.jexl3.internal.Engine;
 
 /**
  * Flags and properties that can alter the evaluation behavior.
@@ -35,31 +36,150 @@ import java.math.MathContext;
  * <p>This interface replaces the now deprecated JexlEngine.Options.
  * @since 3.2
  */
-public interface JexlOptions {
+public final class JexlOptions {
+    /** The local shade bit. */
+    private static final int SHADE = 6;
+    /** The antish var bit. */
+    private static final int ANTISH = 5;
+    /** The lexical scope bit. */
+    private static final int LEXICAL = 4;
+    /** The safe bit. */
+    private static final int SAFE = 3;
+    /** The silent bit. */
+    private static final int SILENT = 2;
+    /** The strict bit. */
+    private static final int STRICT = 1;
+    /** The cancellable bit. */
+    private static final int CANCELLABLE = 0;
+    /** The flags names ordered. */
+    private static final String[] NAMES = {
+        "cancellable", "strict", "silent", "safe", "lexical", "antish", 
"lexicalShade"
+    };
+    /** Default mask .*/
+    private static int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1 << 
ANTISH | 1 << SAFE;
+    /** The arithmetic math context. */
+    private MathContext mathContext = null;
+    /** The arithmetic math scale. */
+    private int mathScale = Integer.MIN_VALUE;
+    /** The arithmetic strict math flag. */
+    private boolean strictArithmetic = true;
+    /** The default flags, all but safe. */
+    private int flags = DEFAULT;
+
+    /**
+     * Sets the value of a flag in a mask.
+     * @param ordinal the flag ordinal
+     * @param mask the flags mask
+     * @param value true or false
+     * @return the new flags mask value
+     */
+    private static int set(int ordinal, int mask, boolean value) {
+        return value? mask | (1 << ordinal) : mask & ~(1 << ordinal);
+    }
+
+    /**
+     * Checks the value of a flag in the mask.
+     * @param ordinal the flag ordinal
+     * @param mask the flags mask
+     * @return the mask value with this flag or-ed in
+     */
+    private static boolean isSet(int ordinal, int mask) {
+        return (mask & 1 << ordinal) != 0;
+    }
+        
+    /**
+     * Default ctor.
+     */
+    public JexlOptions() {}
+            
+    /**
+     * Sets the default (static, shared) option flags.
+     * <p>
+     * Whenever possible, we recommend using JexlBuilder methods to 
unambiguously instantiate a JEXL
+     * engine; this method should only be used for testing / validation.
+     * <p>A '+flag' or 'flag' will set the option named 'flag' as true, 
'-flag' set as false.
+     * The possible flag names are:
+     * cancellable, strict, silent, safe, lexical, antish, lexicalShade
+     * <p>Calling JexlBuilder.setDefaultOptions("+safe") once before JEXL 
engine creation 
+     * may ease validating JEXL3.2 in your environment.
+     * @param flags the flags to set 
+     */
+    public static void setDefaultFlags(String...flags) {
+        DEFAULT = parseFlags(DEFAULT, flags);
+    }
+        
+    /**
+     * Parses flags by name.
+     * <p>A '+flag' or 'flag' will set flag as true, '-flag' set as false.
+     * The possible flag names are:
+     * cancellable, strict, silent, safe, lexical, antish, lexicalShade
+     * @param mask the initial mask state
+     * @param flags the flags to set 
+     * @return the flag mask updated
+     */
+    public static int parseFlags(int mask, String...flags) {
+        for(String name : flags) {
+            boolean b = true;
+            if (name.charAt(0) == '+') {
+                name = name.substring(1);
+            } else if (name.charAt(0) == '-') {
+                name = name.substring(1);
+                b = false;
+            }
+            for(int flag = 0; flag < NAMES.length; ++flag) {
+                if (NAMES[flag].equals(name)) {
+                    if (b) {
+                        mask |= (1 << flag);
+                    } else {
+                        mask &= ~(1 << flag);
+                    }
+                    break;
+                }
+            }
+        }
+        return mask;
+    }
+    
+    /**
+     * Sets this option flags using the +/- syntax.
+     * @param opts the option flags
+     */
+    public void setFlags(String[] opts) {
+        flags = parseFlags(flags, opts);
+    }
+    
     /**
      * The MathContext instance used for +,-,/,*,% operations on big decimals.
      * @return the math context
      */
-    MathContext getMathContext();
+    public MathContext getMathContext() {
+        return mathContext;
+    }
 
     /**
      * The BigDecimal scale used for comparison and coercion operations.
      * @return the scale
      */
-    int getMathScale();
+    public int getMathScale() {
+        return mathScale;
+    }
 
     /**
      * Checks whether evaluation will attempt resolving antish variable names.
      * @return true if antish variables are solved, false otherwise
      */
-    boolean isAntish();
+    public boolean isAntish() {
+        return isSet(ANTISH, flags);
+    }
     
     /**
      * Checks whether evaluation will throw JexlException.Cancel (true) or
      * return null (false) if interrupted.
      * @return true when cancellable, false otherwise
      */
-    boolean isCancellable();
+    public boolean isCancellable() {
+        return isSet(CANCELLABLE, flags);
+    }
 
     /**
      * Checks whether runtime variable scope is lexical.
@@ -67,7 +187,9 @@ public interface JexlOptions {
      * Redefining a variable in the same lexical unit will generate errors.
      * @return true if scope is lexical, false otherwise
      */
-    boolean isLexical();
+    public boolean isLexical() {
+        return isSet(LEXICAL, flags);
+    }
     
     /**
      * Checks whether local variables shade global ones.
@@ -79,122 +201,169 @@ public interface JexlOptions {
      * raise an error.
      * @return true if lexical shading is applied, false otherwise
      */
-    boolean isLexicalShade();
+    public boolean isLexicalShade() {
+        return isSet(SHADE, flags);
+    }
     
     /**
      * Checks whether the engine considers null in navigation expression as
      * errors during evaluation..
      * @return true if safe, false otherwise
      */
-    boolean isSafe();
+    public boolean isSafe() {
+        return isSet(SAFE, flags);
+    }
 
     /**
      * Checks whether the engine will throw a {@link JexlException} when an
      * error is encountered during evaluation.
      * @return true if silent, false otherwise
      */
-    boolean isSilent();
+    public boolean isSilent() {
+        return isSet(SILENT, flags);
+    }
 
     /**
      * Checks whether the engine considers unknown variables, methods and
      * constructors as errors during evaluation.
      * @return true if strict, false otherwise
      */
-    boolean isStrict();
+    public boolean isStrict() {
+        return isSet(STRICT, flags);
+    }
 
     /**
      * Checks whether the arithmetic triggers errors during evaluation when 
null
      * is used as an operand.
      * @return true if strict, false otherwise
      */
-    boolean isStrictArithmetic();
+    public boolean isStrictArithmetic() {
+        return strictArithmetic;
+    }
     
     /**
      * Sets whether the engine will attempt solving antish variable names from 
      * context.
      * @param flag true if antish variables are solved, false otherwise
      */
-    void setAntish(boolean flag);
+    public void setAntish(boolean flag) {
+        flags = set(ANTISH, flags, flag);
+    }
 
     /**
      * Sets whether the engine will throw JexlException.Cancel (true) or return
      * null (false) when interrupted during evaluation.
      * @param flag true when cancellable, false otherwise
      */
-    void setCancellable(boolean flag);
+    public void setCancellable(boolean flag) {
+        flags = set(CANCELLABLE, flags, flag);
+    }
     
     /**
      * Sets whether the engine uses a strict block lexical scope during
      * evaluation.
      * @param flag true if lexical scope is used, false otherwise
      */
-    void setLexical(boolean flag);
+    public void setLexical(boolean flag) {
+        flags = set(LEXICAL, flags, flag);
+    }   
     
     /**
      * Sets whether the engine strictly shades global variables.
      * Local symbols shade globals after definition and creating global
      * variables is prohibited evaluation.
+     * If setting to lexical shade, lexical is also set.
      * @param flag true if creation is allowed, false otherwise
      */
-    void setLexicalShade(boolean flag);
+    public void setLexicalShade(boolean flag) {
+        flags = set(SHADE, flags, flag);
+        if (flag) {
+            flags = set(LEXICAL, flags, true);
+        }
+    }
 
     /**
      * Sets the arithmetic math context.
      * @param mcontext the context
      */
-    void setMathContext(MathContext mcontext);
+    public void setMathContext(MathContext mcontext) {
+        this.mathContext = mcontext;
+    }
 
     /**
      * Sets the arithmetic math scale.
      * @param mscale the scale
      */
-    void setMathScale(int mscale);
+    public void setMathScale(int mscale) {
+        this.mathScale = mscale;
+    }
 
     /**
      * Sets whether the engine considers null in navigation expression as 
errors
      * during evaluation.
      * @param flag true if safe, false otherwise
      */
-    void setSafe(boolean flag);
+    public void setSafe(boolean flag) {
+        flags = set(SAFE, flags, flag);
+    } 
 
     /**
      * Sets whether the engine will throw a {@link JexlException} when an error
      * is encountered during evaluation.
      * @param flag true if silent, false otherwise
      */
-    void setSilent(boolean flag);
+    public void setSilent(boolean flag) {
+        flags = set(SILENT, flags, flag);
+    }
 
     /**
      * Sets whether the engine considers unknown variables, methods and
      * constructors as errors during evaluation.
      * @param flag true if strict, false otherwise
      */
-    void setStrict(boolean flag);
+    public void setStrict(boolean flag) {
+        flags = set(STRICT, flags, flag);
+    }
 
     /**
      * Sets the strict arithmetic flag.
      * @param stricta true or false
      */
-    void setStrictArithmetic(boolean stricta);
+    public void setStrictArithmetic(boolean stricta) {
+        this.strictArithmetic = stricta;
+    }
 
     /**
      * Set options from engine.
      * @param jexl the engine
      * @return this instance
-     */
-    JexlOptions set(JexlEngine jexl);
+     */        
+    public JexlOptions set(JexlEngine jexl) {
+        if (jexl instanceof Engine) {
+            ((Engine) jexl).optionsSet(this);
+        }
+        return this;
+    }
 
     /**
      * Set options from options.
-     * @param options the options
+     * @param src the options
      * @return this instance
      */
-    JexlOptions set(JexlOptions options);
-        
+    public JexlOptions set(JexlOptions src) {
+        mathContext = src.mathContext;
+        mathScale = src.mathScale;
+        strictArithmetic = src.strictArithmetic;
+        flags = src.flags;
+        return this;
+    }
+
     /**
      * Creates a copy of this instance.
      * @return a copy
      */
-    JexlOptions copy();
+    public JexlOptions copy() {
+        return new JexlOptions().set(this);
+    }
     
 }
\ No newline at end of file
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Engine.java 
b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
index d752cca..4895578 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Engine.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Engine.java
@@ -74,6 +74,10 @@ public class Engine extends JexlEngine {
         private UberspectHolder() {}
     }
     /**
+     * The name of the options pragma.
+     */
+    protected static final String PRAGMA_OPTIONS = "jexl.options";
+    /**
      * The Log to which all JexlEngine messages will be logged.
      */
     protected final Log logger;
@@ -347,18 +351,62 @@ public class Engine extends JexlEngine {
             cache.clear();
         }
     }
-
+       
+    /**
+     * Sets options from this engine options.
+     * @param opts the options to set
+     * @return the options
+     */
+    public JexlOptions optionsSet(JexlOptions opts) {
+        if (opts != null) {
+            opts.set(options);
+        }
+        return opts;
+    }
+    
+    /**
+     * Creates a script evaluation options.
+     * <p>This also calls the pragma processor if any
+     * @param script the script
+     * @param context the context
+     * @return the options
+     */
+    protected JexlOptions createOptions(Script script, JexlContext context) {
+        JexlOptions opts = options(context);
+        Map<String, Object> pragmas = script.getPragmas();
+        if (pragmas != null) {
+            JexlContext.PragmaProcessor processor =
+                    context instanceof JexlContext.PragmaProcessor
+                    ? (JexlContext.PragmaProcessor) context
+                    : null;
+            for(Map.Entry<String, Object> pragma : pragmas.entrySet()) {
+                String key = pragma.getKey();
+                Object value = pragma.getValue();
+                if (PRAGMA_OPTIONS.equals(key) && opts != null) {
+                    if (value instanceof String) {
+                        String[] vs = ((String) value).split(" ");
+                        opts.setFlags(vs);
+                    }
+                }
+                if (processor != null) {
+                    processor.processPragma(key, value);
+                }
+            }
+        }
+        return opts;
+    }
+    
     /**
      * Creates an interpreter.
      * @param context a JexlContext; if null, the empty context is used 
instead.
      * @param frame   the interpreter frame
+     * @param opts    the evaluation options
      * @return an Interpreter
      */
-    protected Interpreter createInterpreter(JexlContext context, Frame frame) {
-        return new Interpreter(this, context, frame);
+    protected Interpreter createInterpreter(JexlContext context, Frame frame, 
JexlOptions opts) {
+        return new Interpreter(this, opts, context, frame);
     }
 
-
     @Override
     public Script createExpression(JexlInfo info, String expression) {
         return createScript(expressionFeatures, info, expression, null);
@@ -371,8 +419,12 @@ public class Engine extends JexlEngine {
         }
         String source = trimSource(scriptText);
         Scope scope = names == null ? null : new Scope(null, names);
-        ASTJexlScript tree = parse(info, features == null? scriptFeatures : 
features, source, scope);
-        return new Script(this, source, tree);
+        JexlFeatures ftrs = features == null? scriptFeatures : features;
+        ASTJexlScript tree = parse(info, ftrs, source, scope);
+        // when parsing lexical, try hard to run lexical
+        return ftrs.isLexical()
+            ? new Script.Lexical(this, source, tree)
+            : new Script(this, source, tree);
     }
 
     /**
@@ -405,7 +457,7 @@ public class Engine extends JexlEngine {
             final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, 
scope);
             final JexlNode node = script.jjtGetChild(0);
             final Frame frame = script.createFrame(bean);
-            final Interpreter interpreter = createInterpreter(context, frame);
+            final Interpreter interpreter = createInterpreter(context, frame, 
null);
             return interpreter.visitLexicalNode(node, null);
         } catch (JexlException xjexl) {
             if (silent) {
@@ -434,7 +486,7 @@ public class Engine extends JexlEngine {
             final ASTJexlScript script = parse(null, PROPERTY_FEATURES, src, 
scope);
             final JexlNode node = script.jjtGetChild(0);
             final Frame frame = script.createFrame(bean, value);
-            final Interpreter interpreter = createInterpreter(context, frame);
+            final Interpreter interpreter = createInterpreter(context, frame, 
null);
             interpreter.visitLexicalNode(node, null);
         } catch (JexlException xjexl) {
             if (silent) {
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java 
b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
index ed7efa7..9c27f61 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -17,13 +17,18 @@
 //CSOFF: FileLength
 package org.apache.commons.jexl3.internal;
 
+import java.util.Iterator;
+import java.util.concurrent.Callable;
+
 import org.apache.commons.jexl3.JexlArithmetic;
 import org.apache.commons.jexl3.JexlContext;
 import org.apache.commons.jexl3.JexlEngine;
 import org.apache.commons.jexl3.JexlException;
 import org.apache.commons.jexl3.JexlOperator;
+import org.apache.commons.jexl3.JexlOptions;
 import org.apache.commons.jexl3.JexlScript;
 import org.apache.commons.jexl3.JxltEngine;
+
 import org.apache.commons.jexl3.introspection.JexlMethod;
 import org.apache.commons.jexl3.introspection.JexlPropertyGet;
 
@@ -105,9 +110,6 @@ import org.apache.commons.jexl3.parser.ASTWhileStatement;
 import org.apache.commons.jexl3.parser.JexlNode;
 import org.apache.commons.jexl3.parser.Node;
 
-import java.util.Iterator;
-import java.util.concurrent.Callable;
-
 /**
  * An interpreter of JEXL syntax.
  *
@@ -130,11 +132,12 @@ public class Interpreter extends InterpreterBase {
     /**
      * Creates an interpreter.
      * @param engine   the engine creating this interpreter
-     * @param aContext the context to evaluate expression
-     * @param eFrame   the interpreter evaluation frame
+     * @param aContext the evaluation context, global variables, methods and 
functions
+     * @param opts     the evaluation options, flags modifying evaluation 
behavior
+     * @param eFrame   the evaluation frame, arguments and local variables
      */
-    protected Interpreter(Engine engine, JexlContext aContext, Frame eFrame) {
-        super(engine, aContext);
+    protected Interpreter(Engine engine, JexlOptions opts, JexlContext 
aContext, Frame eFrame) {
+        super(engine, opts, aContext);
         this.frame = eFrame;
     }
 
@@ -1122,7 +1125,7 @@ public class Interpreter extends InterpreterBase {
         JexlNode objectNode = null;
         JexlNode ptyNode = null;
         StringBuilder ant = null;
-        boolean antish = !(parent instanceof ASTReference) && 
options.isAntish();
+        boolean antish = !(parent instanceof ASTReference);
         int v = 1;
         main:
         for (int c = 0; c < numChildren; c++) {
@@ -1172,15 +1175,21 @@ public class Interpreter extends InterpreterBase {
                     if (first instanceof ASTIdentifier) {
                         ASTIdentifier afirst = (ASTIdentifier) first;
                         ant = new StringBuilder(afirst.getName());
-                        // skip the first node case since it was trialed in 
jjtAccept above and returned null
-                        if (c == 0) {
-                            continue;
-                        }
+                        // skip the else...*
                     } else {
                         // not an identifier, not antish
                         ptyNode = objectNode;
                         break main;
                     }
+                    // *... and continue
+                    if (!options.isAntish()) {
+                        antish = false;
+                        continue;
+                    }
+                    // skip the first node case since it was trialed in 
jjtAccept above and returned null
+                    if (c == 0) {
+                        continue;
+                    }
                 }
                 // catch up to current node
                 for (; v <= c; ++v) {
diff --git 
a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java 
b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
index 74c8f8f..504c1c1 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/InterpreterBase.java
@@ -78,16 +78,17 @@ public abstract class InterpreterBase extends ParserVisitor 
{
     /**
      * Creates an interpreter base.
      * @param engine   the engine creating this interpreter
-     * @param aContext the context to evaluate expression
+     * @param opts     the evaluation options
+     * @param aContext the evaluation context
      */
-    protected InterpreterBase(Engine engine, JexlContext aContext) {
+    protected InterpreterBase(Engine engine, JexlOptions opts, JexlContext 
aContext) {
         this.jexl = engine;
         this.logger = jexl.logger;
         this.uberspect = jexl.uberspect;
         this.context = aContext != null ? aContext : Engine.EMPTY_CONTEXT;
         this.cache = engine.cache != null;
         JexlArithmetic jexla = jexl.arithmetic;
-        this.options = jexl.options(context);
+        this.options = opts == null? engine.options(aContext) : opts;
         this.arithmetic = jexla.options(options);
         if (arithmetic != jexla && 
!arithmetic.getClass().equals(jexla.getClass())) {
             logger.warn("expected arithmetic to be " + 
jexla.getClass().getSimpleName()
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Options.java 
b/src/main/java/org/apache/commons/jexl3/internal/Options.java
deleted file mode 100644
index 04dd182..0000000
--- a/src/main/java/org/apache/commons/jexl3/internal/Options.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * 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 org.apache.commons.jexl3.JexlOptions;
-import org.apache.commons.jexl3.JexlEngine;
-
-import java.math.MathContext;
-
-/**
- * A basic implementation of JexlOptions.
- * <p>Thread safety is only guaranteed if no modifications (call set* method)
- * occurs in different threads; note however that using the 'callable()' 
method to
- * allow a script to run as such will use a copy.
- */
-public class Options implements JexlOptions {
-    /** The local shade bit. */
-    protected static final int SHADE = 6;
-    /** The antish var bit. */
-    protected static final int ANTISH = 5;
-    /** The lexical scope bit. */
-    protected static final int LEXICAL = 4;
-    /** The safe bit. */
-    protected static final int SAFE = 3;
-    /** The silent bit. */
-    protected static final int SILENT = 2;
-    /** The strict bit. */
-    protected static final int STRICT = 1;
-    /** The cancellable bit. */
-    protected static final int CANCELLABLE = 0;
-    /** The flags names ordered. */
-    private static final String[] NAMES = {
-        "cancellable", "strict", "silent", "safe", "lexical", "antish", 
"lexicalShade"
-    };
-    /** Default mask .*/
-    protected static int DEFAULT = 1 /*<< CANCELLABLE*/ | 1 << STRICT | 1 << 
ANTISH | 1 << SAFE;
-    /** The arithmetic math context. */
-    private MathContext mathContext = null;
-    /** The arithmetic math scale. */
-    private int mathScale = Integer.MIN_VALUE;
-    /** The arithmetic strict math flag. */
-    private boolean strictArithmetic = true;
-    /** The default flags, all but safe. */
-    private int flags = DEFAULT;
-
-    /**
-     * Sets the value of a flag in a mask.
-     * @param ordinal the flag ordinal
-     * @param mask the flags mask
-     * @param value true or false
-     * @return the new flags mask value
-     */
-    protected static int set(int ordinal, int mask, boolean value) {
-        return value? mask | (1 << ordinal) : mask & ~(1 << ordinal);
-    }
-
-    /**
-     * Checks the value of a flag in the mask.
-     * @param ordinal the flag ordinal
-     * @param mask the flags mask
-     * @return the mask value with this flag or-ed in
-     */
-    protected static boolean isSet(int ordinal, int mask) {
-        return (mask & 1 << ordinal) != 0;
-    }
-        
-    /**
-     * Default ctor.
-     */
-    public Options() {}
-    
-    /**
-     * Sets the default flags.
-     * <p>Used by tests to force default options.
-     * @param flags the flags to set 
-     */
-    public static void setDefaultFlags(String...flags) {
-        DEFAULT = parseFlags(DEFAULT, flags);
-    }
-        
-    /**
-     * Parses flags by name.
-     * <p>A '+flag' or 'flag' will set flag as true, '-flag' set as false.
-     * The possible flag names are:
-     * cancellable, strict, silent, safe, lexical, antish, lexicalShade
-     * @param mask the initial mask state
-     * @param flags the flags to set 
-     * @return the flag mask updated
-     */
-    public static int parseFlags(int mask, String...flags) {
-        for(String name : flags) {
-            boolean b = true;
-            if (name.charAt(0) == '+') {
-                name = name.substring(1);
-            } else if (name.charAt(0) == '-') {
-                name = name.substring(1);
-                b = false;
-            }
-            for(int flag = 0; flag < NAMES.length; ++flag) {
-                if (NAMES[flag].equals(name)) {
-                    if (b) {
-                        mask |= (1 << flag);
-                    } else {
-                        mask &= ~(1 << flag);
-                    }
-                    break;
-                }
-            }
-        }
-        return mask;
-    }
-
-    @Override
-    public Options set(JexlEngine jexl) {
-        if (jexl instanceof Engine) {
-            set(((Engine) jexl).options);
-        } else {
-            mathContext = jexl.getArithmetic().getMathContext();
-            mathScale = jexl.getArithmetic().getMathScale();
-            strictArithmetic = jexl.getArithmetic().isStrict();
-            set(STRICT, flags, jexl.isStrict());
-            set(SILENT, flags, jexl.isSilent());
-            set(SAFE, flags, jexl.isSafe());
-            set(CANCELLABLE, flags, jexl.isCancellable());
-        }
-        return this;
-    }
-    
-    @Override
-    public Options set(JexlOptions opts) {
-        if (opts instanceof Options) {
-            Options src = (Options) opts;
-            mathContext = src.mathContext;
-            mathScale = src.mathScale;
-            strictArithmetic = src.strictArithmetic;
-            flags = src.flags;
-        } else {
-            mathContext = opts.getMathContext();
-            mathScale = opts.getMathScale();
-            strictArithmetic = opts.isStrict();
-            int mask = DEFAULT;
-            mask = set(STRICT, mask, opts.isStrict());
-            mask = set(SILENT, mask, opts.isSilent());
-            mask = set(SAFE, mask, opts.isSafe());
-            mask = set(CANCELLABLE, mask, opts.isCancellable());
-            mask = set(LEXICAL, mask, opts.isLexical());
-            mask = set(SHADE, mask, opts.isLexicalShade());
-            mask = set(ANTISH, mask, opts.isAntish());
-            flags = mask;
-        }
-        return this;
-    }
-    
-    @Override
-    public Options copy() {
-        return new Options().set(this);
-    }
-    
-    @Override
-    public void setAntish(boolean flag) {
-        flags = set(ANTISH, flags, flag);
-    }
-
-    @Override
-    public void setMathContext(MathContext mcontext) {
-        this.mathContext = mcontext;
-    }
-
-    @Override
-    public void setMathScale(int mscale) {
-        this.mathScale = mscale;
-    }
-
-    @Override
-    public void setStrictArithmetic(boolean stricta) {
-        this.strictArithmetic = stricta;
-    }
-
-    @Override
-    public void setStrict(boolean flag) {
-        flags = set(STRICT, flags, flag);
-    }
-            
-    @Override
-    public void setSafe(boolean flag) {
-        flags = set(SAFE, flags, flag);
-    }    
-
-    @Override
-    public void setSilent(boolean flag) {
-        flags = set(SILENT, flags, flag);
-    }
-            
-    @Override
-    public void setCancellable(boolean flag) {
-        flags = set(CANCELLABLE, flags, flag);
-    }
-            
-    @Override
-    public void setLexical(boolean flag) {
-        flags = set(LEXICAL, flags, flag);
-    }    
-    
-    @Override
-    public void setLexicalShade(boolean flag) {
-        flags = set(SHADE, flags, flag);
-    }
-    
-    @Override
-    public boolean isAntish() {
-        return isSet(ANTISH, flags);
-    }
-    
-    @Override
-    public boolean isSilent() {
-        return isSet(SILENT, flags);
-    }
-
-    @Override
-    public boolean isStrict() {
-        return isSet(STRICT, flags);
-    }
-    
-    @Override
-    public boolean isSafe() {
-        return isSet(SAFE, flags);
-    }
-
-    @Override
-    public boolean isCancellable() {
-        return isSet(CANCELLABLE, flags);
-    }
-     
-    @Override
-    public boolean isLexical() {
-        return isSet(LEXICAL, flags);
-    }
-       
-    @Override
-    public boolean isLexicalShade() {
-        return isSet(SHADE, flags);
-    }
-
-    @Override
-    public boolean isStrictArithmetic() {
-        return strictArithmetic;
-    }
-
-    @Override
-    public MathContext getMathContext() {
-        return mathContext;
-    }
-    
-    @Override
-    public int getMathScale() {
-        return mathScale;
-    }
-    
-}
diff --git a/src/main/java/org/apache/commons/jexl3/internal/Script.java 
b/src/main/java/org/apache/commons/jexl3/internal/Script.java
index 62ecaca..593ad08 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Script.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Script.java
@@ -17,6 +17,8 @@
 package org.apache.commons.jexl3.internal;
 
 import org.apache.commons.jexl3.JexlContext;
+import org.apache.commons.jexl3.JexlInfo;
+import org.apache.commons.jexl3.JexlOptions;
 import org.apache.commons.jexl3.JexlEngine;
 import org.apache.commons.jexl3.JexlScript;
 import org.apache.commons.jexl3.JexlExpression;
@@ -96,7 +98,39 @@ public class Script implements JexlScript, JexlExpression {
     protected Frame createFrame(Object[] args) {
         return script.createFrame(args);
     }
+    
+    /**
+     * Creates this script options for evaluation.
+     * <p>This also calls the pragma processor 
+     * @param context the context
+     * @return the options
+     */
+    protected JexlOptions createOptions(JexlContext context) {
+        return jexl.createOptions(this, context);
+    }
+    
+    /**
+     * A lexical script, ensures options are lexical.
+     */
+    public static class Lexical extends Script {
+        /**
+         * Sole ctor.
+         * @param engine the engine
+         * @param expr the source.
+         * @param ref the ast
+         */
+        protected Lexical(Engine engine, String expr, ASTJexlScript ref) {
+            super(engine, expr, ref);
+        }
 
+        @Override
+        public JexlOptions createOptions(JexlContext ctxt) {
+            JexlOptions opts = super.createOptions(ctxt);
+            opts.setLexical(true);
+            return opts;
+        }
+    }
+    
     /**
      * Creates this script interpreter.
      * @param context the context
@@ -104,7 +138,7 @@ public class Script implements JexlScript, JexlExpression {
      * @return  the interpreter
      */
     protected Interpreter createInterpreter(JexlContext context, Frame frame) {
-        return jexl.createInterpreter(context, frame);
+        return jexl.createInterpreter(context, frame, createOptions(context));
     }
 
     /**
@@ -215,6 +249,13 @@ public class Script implements JexlScript, JexlExpression {
     public String[] getLocalVariables() {
         return script.getLocalVariables();
     }
+    
+    /**
+     * @return the info
+     */
+    public JexlInfo getInfo() {
+        return script.jexlInfo();
+    }
 
     /**
      * Gets this script variables.
@@ -259,7 +300,7 @@ public class Script implements JexlScript, JexlExpression {
      */
     @Override
     public Callable callable(JexlContext context, Object... args) {
-        return new Callable(jexl.createInterpreter(context, 
script.createFrame(args)));
+        return new Callable(createInterpreter(context, 
script.createFrame(args)));
     }
 
     /**
diff --git 
a/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java 
b/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
index 17b0ff9..7babe6f 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/TemplateInterpreter.java
@@ -48,7 +48,7 @@ public class TemplateInterpreter extends Interpreter {
      */
     TemplateInterpreter(Engine jexl,
             JexlContext jcontext, Frame jframe, TemplateExpression[] 
expressions, Writer out) {
-        super(jexl, jcontext, jframe);
+        super(jexl, null, jcontext, jframe);
         exprs = expressions;
         writer = out;
         block = new LexicalFrame(frame, null);
diff --git a/src/test/java/org/apache/commons/jexl3/AntishCallTest.java 
b/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
index 5f23b11..8397ce0 100644
--- a/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
+++ b/src/test/java/org/apache/commons/jexl3/AntishCallTest.java
@@ -173,7 +173,8 @@ public class AntishCallTest extends JexlTestCase {
     // JEXL-300
     @Test
     public void testSafeAnt() throws Exception {
-        JexlContext ctxt = new MapContext();
+        JexlEvalContext ctxt = new JexlEvalContext();
+        JexlOptions options = ctxt.getEngineOptions();
         ctxt.set("x.y.z", 42);
         JexlScript script;
         Object result;
@@ -182,6 +183,18 @@ public class AntishCallTest extends JexlTestCase {
         result = script.execute(ctxt);
         Assert.assertEquals(42, result);
         Assert.assertEquals(42, ctxt.get("x.y.z"));
+        
+        options.setAntish(false);
+        try {
+            result = script.execute(ctxt);
+            Assert.fail("antish var shall not be resolved");
+        } catch(JexlException.Variable xvar) {
+            Assert.assertTrue("x".equals(xvar.getVariable()));
+        } catch(JexlException xother) {
+            Assert.assertTrue(xother != null);
+        } finally {
+            options.setAntish(true);
+        }
                 
         result = null;
         script = JEXL.createScript("x?.y?.z");
diff --git a/src/test/java/org/apache/commons/jexl3/Issues300Test.java 
b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
index d2b45ec..913133b 100644
--- a/src/test/java/org/apache/commons/jexl3/Issues300Test.java
+++ b/src/test/java/org/apache/commons/jexl3/Issues300Test.java
@@ -324,25 +324,24 @@ public class Issues300Test {
         Assert.assertEquals(52, result);
     }
 
-    public static class ClazzB {
-        public int methodB()  {
-            return 42;
-        }
-    }
-    public static class ClazzA {
-        public ClazzB methodA() {
-            return new ClazzB();
-        }
-    }
 
     @Test
-    public void test317Tentative() throws Exception {
+    public void test317() throws Exception {
         JexlEngine jexl = new JexlBuilder().strict(true).create();
         JexlContext ctxt = new MapContext();
         JexlScript script;
         Object result;
-        script = jexl.createScript("x.methodA().methodB()", "x");
-        result = script.execute(ctxt, new ClazzA());
+        JexlInfo info = new JexlInfo("test317", 1, 1);
+        script = jexl.createScript(info, "var f = "
+                + "()-> {x + x }; f",
+                "x");
+        result = script.execute(ctxt, 21);
+        Assert.assertTrue(result instanceof JexlScript);
+        script = (JexlScript) result;
+        info = JexlInfo.from(script);
+        Assert.assertNotNull(info);
+        Assert.assertEquals("test317", info.getName());
+        result = script.execute(ctxt, 21);
         Assert.assertEquals(42, result);
     }
 }
diff --git a/src/test/java/org/apache/commons/jexl3/JXLTTest.java 
b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
index c59193d..3986c2e 100644
--- a/src/test/java/org/apache/commons/jexl3/JXLTTest.java
+++ b/src/test/java/org/apache/commons/jexl3/JXLTTest.java
@@ -19,7 +19,6 @@ package org.apache.commons.jexl3;
 import org.apache.commons.jexl3.internal.TemplateDebugger;
 import org.apache.commons.jexl3.internal.TemplateScript;
 import org.apache.commons.jexl3.internal.Debugger;
-import org.apache.commons.jexl3.internal.Options;
 
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
@@ -806,7 +805,7 @@ public class JXLTTest extends JexlTestCase {
         }
         
         JexlOptions newOptions() {
-            options = new Options();
+            options = new JexlOptions();
             return options;
         }
     }
diff --git a/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java 
b/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java
index 55fdcad..f0c233c 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java
+++ b/src/test/java/org/apache/commons/jexl3/JexlEvalContext.java
@@ -19,7 +19,6 @@ package org.apache.commons.jexl3;
 import java.util.Collections;
 import java.util.Map;
 import org.apache.commons.jexl3.annotations.NoJexl;
-import org.apache.commons.jexl3.internal.Options;
 
 /**
  * A JEXL evaluation environment wrapping variables, namespace and options.
@@ -35,7 +34,7 @@ public class JexlEvalContext implements
     /** The namespace. */
     private final JexlContext.NamespaceResolver ns; 
     /** The options. */
-    private final JexlOptions options = new Options();
+    private final JexlOptions options = new JexlOptions();
 
     
     
diff --git a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java 
b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
index f8d00d3..294ea4a 100644
--- a/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
+++ b/src/test/java/org/apache/commons/jexl3/JexlTestCase.java
@@ -35,7 +35,7 @@ public class JexlTestCase {
     // important can be identified by the builder  calling lexical(...).
     static {
         //JexlBuilder.setDefaultOptions("+safe", "-lexical"); 
-        JexlBuilder.setDefaultOptions("-safe", "+lexical");
+        JexlOptions.setDefaultFlags("-safe", "+lexical");
     }
     /** No parameters signature for test run. */
     private static final Class<?>[] NO_PARMS = {};
diff --git a/src/test/java/org/apache/commons/jexl3/LexicalTest.java 
b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
index cf8943e..891393a 100644
--- a/src/test/java/org/apache/commons/jexl3/LexicalTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LexicalTest.java
@@ -19,6 +19,7 @@ package org.apache.commons.jexl3;
 import java.io.StringReader;
 import java.io.StringWriter;
 import java.util.Set;
+import org.apache.commons.jexl3.internal.LexicalScope;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -344,4 +345,46 @@ public class LexicalTest {
             Assert.assertNotNull(xany);
         }
     }
+    
+    @Test
+    public void testPragmaOptions() throws Exception {
+        // same as 6d but using a pragma
+        String str = "#pragma jexl.options '+strict +lexical +lexicalShade 
-safe'\n"
+                + "i = 0; for (var i : [42]) i; i";
+        JexlEngine jexl = new JexlBuilder().strict(false).create();
+        JexlScript e = jexl.createScript(str);
+        JexlContext ctxt = new MapContext();
+        try {
+            Object o = e.execute(ctxt);
+            Assert.fail("i should be shaded");
+        } catch (JexlException xany) {
+            Assert.assertNotNull(xany);
+        }
+    }
+    
+    @Test
+    public void testPragmaNoop() throws Exception {
+        // unknow pragma
+        String str = "#pragma jexl.options 'no effect'\ni = -42; for (var i : 
[42]) i; i";
+        JexlEngine jexl = new 
JexlBuilder().lexical(false).strict(true).create();
+        JexlScript e = jexl.createScript(str);
+        JexlContext ctxt = new MapContext();
+        Object result = e.execute(ctxt);
+        Assert.assertEquals(42, result);
+    }
+    
+    
+    @Test
+    public void testScopeFrame() throws Exception {
+        LexicalScope scope = new LexicalScope(null);
+        for(int i = 0; i < 128; i += 2) {
+            Assert.assertTrue(scope.declareSymbol(i));
+            Assert.assertFalse(scope.declareSymbol(i));
+        }
+        for(int i = 0; i < 128; i += 2) {
+            Assert.assertTrue(scope.hasSymbol(i));
+            Assert.assertFalse(scope.hasSymbol(i + 1));
+        }
+    }
+    
 }

Reply via email to