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

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

commit 01310b5ef80288bcfe5e8269c7ccda388a5b0042
Author: Henri Biestro <hbies...@cloudera.com>
AuthorDate: Fri Feb 16 22:10:10 2024 +0100

    JEXL-418: initial drop;
---
 .../apache/commons/jexl3/internal/Debugger.java    | 129 ++++------
 .../apache/commons/jexl3/internal/Interpreter.java | 262 ++++++++++++++-------
 .../commons/jexl3/internal/ScriptVisitor.java      |  93 +-------
 .../commons/jexl3/parser/ASTTryStatement.java      |  49 ++++
 .../commons/jexl3/parser/FeatureController.java    |   8 +
 .../org/apache/commons/jexl3/parser/Parser.jjt     |  36 ++-
 .../apache/commons/jexl3/parser/ParserVisitor.java |   2 +
 .../apache/commons/jexl3/TryCatchFinallyTest.java  |  58 +++++
 8 files changed, 369 insertions(+), 268 deletions(-)

diff --git a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java 
b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
index 6353a57f..4cb224c7 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Debugger.java
@@ -25,95 +25,7 @@ import org.apache.commons.jexl3.JexlExpression;
 import org.apache.commons.jexl3.JexlFeatures;
 import org.apache.commons.jexl3.JexlInfo;
 import org.apache.commons.jexl3.JexlScript;
-import org.apache.commons.jexl3.parser.ASTAddNode;
-import org.apache.commons.jexl3.parser.ASTAndNode;
-import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
-import org.apache.commons.jexl3.parser.ASTAnnotation;
-import org.apache.commons.jexl3.parser.ASTArguments;
-import org.apache.commons.jexl3.parser.ASTArrayAccess;
-import org.apache.commons.jexl3.parser.ASTArrayLiteral;
-import org.apache.commons.jexl3.parser.ASTAssignment;
-import org.apache.commons.jexl3.parser.ASTBitwiseAndNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseComplNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseOrNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseXorNode;
-import org.apache.commons.jexl3.parser.ASTBlock;
-import org.apache.commons.jexl3.parser.ASTBreak;
-import org.apache.commons.jexl3.parser.ASTConstructorNode;
-import org.apache.commons.jexl3.parser.ASTContinue;
-import org.apache.commons.jexl3.parser.ASTDecrementGetNode;
-import org.apache.commons.jexl3.parser.ASTDefineVars;
-import org.apache.commons.jexl3.parser.ASTDivNode;
-import org.apache.commons.jexl3.parser.ASTDoWhileStatement;
-import org.apache.commons.jexl3.parser.ASTEQNode;
-import org.apache.commons.jexl3.parser.ASTERNode;
-import org.apache.commons.jexl3.parser.ASTEWNode;
-import org.apache.commons.jexl3.parser.ASTEmptyFunction;
-import org.apache.commons.jexl3.parser.ASTExtendedLiteral;
-import org.apache.commons.jexl3.parser.ASTFalseNode;
-import org.apache.commons.jexl3.parser.ASTForeachStatement;
-import org.apache.commons.jexl3.parser.ASTFunctionNode;
-import org.apache.commons.jexl3.parser.ASTGENode;
-import org.apache.commons.jexl3.parser.ASTGTNode;
-import org.apache.commons.jexl3.parser.ASTGetDecrementNode;
-import org.apache.commons.jexl3.parser.ASTGetIncrementNode;
-import org.apache.commons.jexl3.parser.ASTIdentifier;
-import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
-import org.apache.commons.jexl3.parser.ASTIfStatement;
-import org.apache.commons.jexl3.parser.ASTIncrementGetNode;
-import org.apache.commons.jexl3.parser.ASTJexlLambda;
-import org.apache.commons.jexl3.parser.ASTJexlScript;
-import org.apache.commons.jexl3.parser.ASTJxltLiteral;
-import org.apache.commons.jexl3.parser.ASTLENode;
-import org.apache.commons.jexl3.parser.ASTLTNode;
-import org.apache.commons.jexl3.parser.ASTMapEntry;
-import org.apache.commons.jexl3.parser.ASTMapLiteral;
-import org.apache.commons.jexl3.parser.ASTMethodNode;
-import org.apache.commons.jexl3.parser.ASTModNode;
-import org.apache.commons.jexl3.parser.ASTMulNode;
-import org.apache.commons.jexl3.parser.ASTNENode;
-import org.apache.commons.jexl3.parser.ASTNEWNode;
-import org.apache.commons.jexl3.parser.ASTNRNode;
-import org.apache.commons.jexl3.parser.ASTNSWNode;
-import org.apache.commons.jexl3.parser.ASTNotNode;
-import org.apache.commons.jexl3.parser.ASTNullLiteral;
-import org.apache.commons.jexl3.parser.ASTNullpNode;
-import org.apache.commons.jexl3.parser.ASTNumberLiteral;
-import org.apache.commons.jexl3.parser.ASTOrNode;
-import org.apache.commons.jexl3.parser.ASTQualifiedIdentifier;
-import org.apache.commons.jexl3.parser.ASTRangeNode;
-import org.apache.commons.jexl3.parser.ASTReference;
-import org.apache.commons.jexl3.parser.ASTReferenceExpression;
-import org.apache.commons.jexl3.parser.ASTRegexLiteral;
-import org.apache.commons.jexl3.parser.ASTReturnStatement;
-import org.apache.commons.jexl3.parser.ASTSWNode;
-import org.apache.commons.jexl3.parser.ASTSetAddNode;
-import org.apache.commons.jexl3.parser.ASTSetAndNode;
-import org.apache.commons.jexl3.parser.ASTSetDivNode;
-import org.apache.commons.jexl3.parser.ASTSetLiteral;
-import org.apache.commons.jexl3.parser.ASTSetModNode;
-import org.apache.commons.jexl3.parser.ASTSetMultNode;
-import org.apache.commons.jexl3.parser.ASTSetOrNode;
-import org.apache.commons.jexl3.parser.ASTSetShiftLeftNode;
-import org.apache.commons.jexl3.parser.ASTSetShiftRightNode;
-import org.apache.commons.jexl3.parser.ASTSetShiftRightUnsignedNode;
-import org.apache.commons.jexl3.parser.ASTSetSubNode;
-import org.apache.commons.jexl3.parser.ASTSetXorNode;
-import org.apache.commons.jexl3.parser.ASTShiftLeftNode;
-import org.apache.commons.jexl3.parser.ASTShiftRightNode;
-import org.apache.commons.jexl3.parser.ASTShiftRightUnsignedNode;
-import org.apache.commons.jexl3.parser.ASTSizeFunction;
-import org.apache.commons.jexl3.parser.ASTStringLiteral;
-import org.apache.commons.jexl3.parser.ASTSubNode;
-import org.apache.commons.jexl3.parser.ASTTernaryNode;
-import org.apache.commons.jexl3.parser.ASTTrueNode;
-import org.apache.commons.jexl3.parser.ASTUnaryMinusNode;
-import org.apache.commons.jexl3.parser.ASTUnaryPlusNode;
-import org.apache.commons.jexl3.parser.ASTVar;
-import org.apache.commons.jexl3.parser.ASTWhileStatement;
-import org.apache.commons.jexl3.parser.JexlNode;
-import org.apache.commons.jexl3.parser.ParserVisitor;
-import org.apache.commons.jexl3.parser.StringParser;
+import org.apache.commons.jexl3.parser.*;
 
 /**
  * Helps pinpoint the cause of problems in expressions that fail during 
evaluation.
@@ -381,6 +293,7 @@ public class Debugger extends ParserVisitor implements 
JexlInfo.Detail {
                 || child instanceof ASTBlock
                 || child instanceof ASTIfStatement
                 || child instanceof ASTForeachStatement
+                || child instanceof ASTTryStatement
                 || child instanceof ASTWhileStatement
                 || child instanceof ASTDoWhileStatement
                 || child instanceof ASTAnnotation;
@@ -785,6 +698,44 @@ public class Debugger extends ParserVisitor implements 
JexlInfo.Detail {
         return data;
     }
 
+    @Override
+    protected Object visit(final ASTTryStatement node, final Object data) {
+        final int form = node.getTryForm();
+        builder.append("try");
+        int nc = 0;
+        // try with-resource named
+        if ((form & 1) != 0) {
+            builder.append('(');
+           accept(node.jjtGetChild(nc++), data);
+           if ((form & 2) != 0) {
+               builder.append(" = ");
+               accept(node.jjtGetChild(nc++), data);
+           }
+           builder.append(')');
+        } else if ((form & 2) != 0) {
+            // try with-resource un-named
+            builder.append('(');
+            accept(node.jjtGetChild(nc++), data);
+            builder.append(')');
+        }
+        // try-body
+        if ((form & 4) != 0) {
+            accept(node.jjtGetChild(nc++), data);
+        }
+        // catch-body
+        if ((form & 8) != 0) {
+            builder.append(" catch(");
+            accept(node.jjtGetChild(nc++), data);
+            builder.append(')');
+        }
+        // finally-body
+        if ((form & 16) != 0) {
+            builder.append(" finally ");
+            accept(node.jjtGetChild(nc++), data);
+        }
+        return data;
+    }
+
     @Override
     protected Object visit(final ASTGENode node, final Object data) {
         return infixChildren(node, " >= ", false, data);
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 4c690273..c2553001 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/Interpreter.java
@@ -33,94 +33,7 @@ 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;
-import org.apache.commons.jexl3.parser.ASTAddNode;
-import org.apache.commons.jexl3.parser.ASTAndNode;
-import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
-import org.apache.commons.jexl3.parser.ASTAnnotation;
-import org.apache.commons.jexl3.parser.ASTArguments;
-import org.apache.commons.jexl3.parser.ASTArrayAccess;
-import org.apache.commons.jexl3.parser.ASTArrayLiteral;
-import org.apache.commons.jexl3.parser.ASTAssignment;
-import org.apache.commons.jexl3.parser.ASTBitwiseAndNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseComplNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseOrNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseXorNode;
-import org.apache.commons.jexl3.parser.ASTBlock;
-import org.apache.commons.jexl3.parser.ASTBreak;
-import org.apache.commons.jexl3.parser.ASTConstructorNode;
-import org.apache.commons.jexl3.parser.ASTContinue;
-import org.apache.commons.jexl3.parser.ASTDecrementGetNode;
-import org.apache.commons.jexl3.parser.ASTDefineVars;
-import org.apache.commons.jexl3.parser.ASTDivNode;
-import org.apache.commons.jexl3.parser.ASTDoWhileStatement;
-import org.apache.commons.jexl3.parser.ASTEQNode;
-import org.apache.commons.jexl3.parser.ASTERNode;
-import org.apache.commons.jexl3.parser.ASTEWNode;
-import org.apache.commons.jexl3.parser.ASTEmptyFunction;
-import org.apache.commons.jexl3.parser.ASTExtendedLiteral;
-import org.apache.commons.jexl3.parser.ASTFalseNode;
-import org.apache.commons.jexl3.parser.ASTForeachStatement;
-import org.apache.commons.jexl3.parser.ASTFunctionNode;
-import org.apache.commons.jexl3.parser.ASTGENode;
-import org.apache.commons.jexl3.parser.ASTGTNode;
-import org.apache.commons.jexl3.parser.ASTGetDecrementNode;
-import org.apache.commons.jexl3.parser.ASTGetIncrementNode;
-import org.apache.commons.jexl3.parser.ASTIdentifier;
-import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
-import org.apache.commons.jexl3.parser.ASTIdentifierAccessJxlt;
-import org.apache.commons.jexl3.parser.ASTIfStatement;
-import org.apache.commons.jexl3.parser.ASTIncrementGetNode;
-import org.apache.commons.jexl3.parser.ASTJexlLambda;
-import org.apache.commons.jexl3.parser.ASTJexlScript;
-import org.apache.commons.jexl3.parser.ASTJxltLiteral;
-import org.apache.commons.jexl3.parser.ASTLENode;
-import org.apache.commons.jexl3.parser.ASTLTNode;
-import org.apache.commons.jexl3.parser.ASTMapEntry;
-import org.apache.commons.jexl3.parser.ASTMapLiteral;
-import org.apache.commons.jexl3.parser.ASTMethodNode;
-import org.apache.commons.jexl3.parser.ASTModNode;
-import org.apache.commons.jexl3.parser.ASTMulNode;
-import org.apache.commons.jexl3.parser.ASTNENode;
-import org.apache.commons.jexl3.parser.ASTNEWNode;
-import org.apache.commons.jexl3.parser.ASTNRNode;
-import org.apache.commons.jexl3.parser.ASTNSWNode;
-import org.apache.commons.jexl3.parser.ASTNotNode;
-import org.apache.commons.jexl3.parser.ASTNullLiteral;
-import org.apache.commons.jexl3.parser.ASTNullpNode;
-import org.apache.commons.jexl3.parser.ASTNumberLiteral;
-import org.apache.commons.jexl3.parser.ASTOrNode;
-import org.apache.commons.jexl3.parser.ASTQualifiedIdentifier;
-import org.apache.commons.jexl3.parser.ASTRangeNode;
-import org.apache.commons.jexl3.parser.ASTReference;
-import org.apache.commons.jexl3.parser.ASTReferenceExpression;
-import org.apache.commons.jexl3.parser.ASTRegexLiteral;
-import org.apache.commons.jexl3.parser.ASTReturnStatement;
-import org.apache.commons.jexl3.parser.ASTSWNode;
-import org.apache.commons.jexl3.parser.ASTSetAddNode;
-import org.apache.commons.jexl3.parser.ASTSetAndNode;
-import org.apache.commons.jexl3.parser.ASTSetDivNode;
-import org.apache.commons.jexl3.parser.ASTSetLiteral;
-import org.apache.commons.jexl3.parser.ASTSetModNode;
-import org.apache.commons.jexl3.parser.ASTSetMultNode;
-import org.apache.commons.jexl3.parser.ASTSetOrNode;
-import org.apache.commons.jexl3.parser.ASTSetShiftLeftNode;
-import org.apache.commons.jexl3.parser.ASTSetShiftRightNode;
-import org.apache.commons.jexl3.parser.ASTSetShiftRightUnsignedNode;
-import org.apache.commons.jexl3.parser.ASTSetSubNode;
-import org.apache.commons.jexl3.parser.ASTSetXorNode;
-import org.apache.commons.jexl3.parser.ASTShiftLeftNode;
-import org.apache.commons.jexl3.parser.ASTShiftRightNode;
-import org.apache.commons.jexl3.parser.ASTShiftRightUnsignedNode;
-import org.apache.commons.jexl3.parser.ASTSizeFunction;
-import org.apache.commons.jexl3.parser.ASTStringLiteral;
-import org.apache.commons.jexl3.parser.ASTSubNode;
-import org.apache.commons.jexl3.parser.ASTTernaryNode;
-import org.apache.commons.jexl3.parser.ASTTrueNode;
-import org.apache.commons.jexl3.parser.ASTUnaryMinusNode;
-import org.apache.commons.jexl3.parser.ASTUnaryPlusNode;
-import org.apache.commons.jexl3.parser.ASTVar;
-import org.apache.commons.jexl3.parser.ASTWhileStatement;
-import org.apache.commons.jexl3.parser.JexlNode;
+import org.apache.commons.jexl3.parser.*;
 
 /**
  * An interpreter of JEXL syntax.
@@ -700,6 +613,179 @@ public class Interpreter extends InterpreterBase {
         throw new JexlException.Break(node);
     }
 
+    @Override
+    protected Object visit(final ASTTryStatement node, final Object data) {
+        int form = node.getTryForm();
+        int nc = 0;
+        JexlNode tryVar = (form & 1) != 0 ? node.jjtGetChild(nc++) : null;
+        JexlNode tryExpression = (form & 2) != 0 ? node.jjtGetChild(nc++) : 
null;
+        JexlNode tryBody = (form & 4) != 0 ? node.jjtGetChild(nc++) : null;
+        JexlNode catchVar = (form & 8) != 0 ? node.jjtGetChild(nc++) : null;
+        JexlNode catchBody = (form & 8) != 0 ? node.jjtGetChild(nc++) : null;
+        JexlNode finallyBody = (form & 16) != 0 ? node.jjtGetChild(nc) : null;
+        JexlException caught = null;
+        JexlException flowControl = null;
+        Object result = null;
+        try {
+            // evaluate the try
+            result = evalTry(tryVar, tryExpression, tryBody, data);
+        } catch(JexlException.Return | JexlException.Cancel xflow) {
+            // flow control exceptions do not trigger the catch clause
+            flowControl = xflow;
+        } catch(JexlException xany) {
+            caught = xany;
+            result = null;
+        }
+        // if we caught an exception and have a catch body, evaluate it
+        JexlException thrownByCatch = null;
+        if (caught != null && catchBody != null) {
+            try {
+                // evaluate the catch
+                evalCatch(catchVar, catchBody, caught,  data);
+            } catch(JexlException.Break | JexlException.Continue |
+                    JexlException.Return | JexlException.Cancel alterFlow) {
+                flowControl = alterFlow;
+            } catch (JexlException exception) {
+                // catching an exception thrown from catch body; can be a 
(re)throw
+                thrownByCatch = exception;
+            }
+        }
+        // if we have a 'finally' block, no matter what, evaluate it: its 
control flow will
+        // take precedence over what the 'catch' block might have thrown.
+        if (finallyBody != null) {
+            try {
+                finallyBody.jjtAccept(this, data);
+            } catch (JexlException.Break | JexlException.Continue  | 
JexlException.Return flowException) {
+                // potentially swallow previous, even return but not cancel
+                if (!(flowControl instanceof JexlException.Cancel)) {
+                    flowControl = flowException;
+                }
+            } catch (JexlException.Cancel cancelException) {
+                // cancel swallows everything
+                flowControl = cancelException;
+            } catch (JexlException exception) {
+                // catching an exception thrown in finally body
+                if (jexl.logger.isDebugEnabled()) {
+                    jexl.logger.debug("exception thrown in finally", 
thrownByCatch);
+                }
+                // swallow the caught one
+                thrownByCatch = exception;
+            }
+        }
+        if (flowControl != null) {
+            if (thrownByCatch != null && jexl.logger.isDebugEnabled()) {
+                jexl.logger.debug("finally swallowed exception thrown by 
catch", thrownByCatch);
+            }
+            throw flowControl;
+        }
+        if (thrownByCatch != null) {
+            throw thrownByCatch;
+        }
+        return result;
+    }
+
+    /**
+     * Evaluate the try/try-with resource a try/catch/finally.
+     *
+     * @param tryVar the try-with-resource variable (if any)
+     * @param tryExpression the try-with-resource expression
+     * @param tryBody the try body
+     * @param data the data
+     * @return the result of body evaluation
+     */
+    private Object evalTry(JexlNode tryVar, JexlNode tryExpression, JexlNode 
tryBody, final Object data) {
+        boolean lexical = false;
+        try {
+            final ASTIdentifier tryVariable;
+            final int symbol;
+            Object tryResult = null;
+            /* Capture try variable if any. */
+            if (tryVar instanceof ASTReference) {
+                tryVariable = (ASTIdentifier) tryVar.jjtGetChild(0);
+                symbol = tryVariable.getSymbol();
+                lexical = tryVariable.isLexical() || options.isLexical();
+                if (lexical) {
+                    // create lexical frame
+                    final LexicalFrame locals = new LexicalFrame(frame, block);
+                    // it may be a local previously declared
+                    final boolean trySymbol = symbol >= 0 && tryVariable 
instanceof ASTVar;
+                    if (trySymbol && !defineVariable((ASTVar) tryVariable, 
locals)) {
+                        return redefinedVariable(tryVar.jjtGetParent(), 
tryVariable.getName());
+                    }
+                    block = locals;
+                }
+            } else {
+                tryVariable = null;
+                symbol = -1;
+                lexical = false;
+            }
+            // evaluate try expression if any
+            if (tryExpression != null) {
+                tryResult = tryExpression.jjtAccept(this, data);
+                // assign try variable if declared
+                if (tryVariable != null) {
+                    if (symbol < 0) {
+                        setContextVariable(tryVar.jjtGetParent(), 
tryVariable.getName(), tryResult);
+                    } else {
+                        frame.set(symbol, tryResult);
+                    }
+                }
+            }
+            // evaluate the body
+            return tryBody.jjtAccept(this, data);
+        } finally {
+            // restore lexical frame
+            if (lexical) {
+                block = block.pop();
+            }
+        }
+    }
+
+    /**
+     * Evaluate the catch a try/catch/finally.
+     *
+     * @param catchVar the variable containing the exception
+     * @param catchBody the body
+     * @param caught the caught exception
+     * @param data the data
+     * @return the result of body evaluation
+     */
+    private Object evalCatch(JexlNode catchVar, JexlNode catchBody, 
JexlException caught, Object data) {
+        final int symbol;
+        final ASTIdentifier catchVariable;
+        boolean lexical = false;
+        try {
+            // declare catch variable and assign with caught exception
+            if (catchVar instanceof ASTReference) {
+                catchVariable = (ASTIdentifier) catchVar.jjtGetChild(0);
+                symbol = catchVariable.getSymbol();
+                lexical = catchVariable.isLexical() || options.isLexical();
+                if (lexical) {
+                    // create lexical frame
+                    final LexicalFrame locals = new LexicalFrame(frame, block);
+                    // it may be a local previously declared
+                    final boolean trySymbol = symbol >= 0 && catchVariable 
instanceof ASTVar;
+                    if (trySymbol && !defineVariable((ASTVar) catchVariable, 
locals)) {
+                        return redefinedVariable(catchVar.jjtGetParent(), 
catchVariable.getName());
+                    }
+                    block = locals;
+                }
+                if (symbol < 0) {
+                    setContextVariable(catchVar.jjtGetParent(), 
catchVariable.getName(), caught);
+                } else {
+                    frame.set(symbol, caught);
+                }
+            }
+            // evaluate body
+            return catchBody.jjtAccept(this, data);
+        } finally {
+            // restore lexical frame
+            if (lexical) {
+                block = block.pop();
+            }
+        }
+    }
+
     @Override
     protected Object visit(final ASTForeachStatement node, final Object data) {
         return node.getLoopForm() == 0 ? forIterator(node, data) : 
forLoop(node, data);
diff --git a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java 
b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
index f9f225a3..6bd0cf77 100644
--- a/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
+++ b/src/main/java/org/apache/commons/jexl3/internal/ScriptVisitor.java
@@ -18,93 +18,7 @@ package org.apache.commons.jexl3.internal;
 
 import org.apache.commons.jexl3.JexlExpression;
 import org.apache.commons.jexl3.JexlScript;
-import org.apache.commons.jexl3.parser.ASTAddNode;
-import org.apache.commons.jexl3.parser.ASTAndNode;
-import org.apache.commons.jexl3.parser.ASTAnnotatedStatement;
-import org.apache.commons.jexl3.parser.ASTAnnotation;
-import org.apache.commons.jexl3.parser.ASTArguments;
-import org.apache.commons.jexl3.parser.ASTArrayAccess;
-import org.apache.commons.jexl3.parser.ASTArrayLiteral;
-import org.apache.commons.jexl3.parser.ASTAssignment;
-import org.apache.commons.jexl3.parser.ASTBitwiseAndNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseComplNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseOrNode;
-import org.apache.commons.jexl3.parser.ASTBitwiseXorNode;
-import org.apache.commons.jexl3.parser.ASTBlock;
-import org.apache.commons.jexl3.parser.ASTBreak;
-import org.apache.commons.jexl3.parser.ASTConstructorNode;
-import org.apache.commons.jexl3.parser.ASTContinue;
-import org.apache.commons.jexl3.parser.ASTDecrementGetNode;
-import org.apache.commons.jexl3.parser.ASTDefineVars;
-import org.apache.commons.jexl3.parser.ASTDivNode;
-import org.apache.commons.jexl3.parser.ASTDoWhileStatement;
-import org.apache.commons.jexl3.parser.ASTEQNode;
-import org.apache.commons.jexl3.parser.ASTERNode;
-import org.apache.commons.jexl3.parser.ASTEWNode;
-import org.apache.commons.jexl3.parser.ASTEmptyFunction;
-import org.apache.commons.jexl3.parser.ASTExtendedLiteral;
-import org.apache.commons.jexl3.parser.ASTFalseNode;
-import org.apache.commons.jexl3.parser.ASTForeachStatement;
-import org.apache.commons.jexl3.parser.ASTFunctionNode;
-import org.apache.commons.jexl3.parser.ASTGENode;
-import org.apache.commons.jexl3.parser.ASTGTNode;
-import org.apache.commons.jexl3.parser.ASTGetDecrementNode;
-import org.apache.commons.jexl3.parser.ASTGetIncrementNode;
-import org.apache.commons.jexl3.parser.ASTIdentifier;
-import org.apache.commons.jexl3.parser.ASTIdentifierAccess;
-import org.apache.commons.jexl3.parser.ASTIfStatement;
-import org.apache.commons.jexl3.parser.ASTIncrementGetNode;
-import org.apache.commons.jexl3.parser.ASTJexlScript;
-import org.apache.commons.jexl3.parser.ASTJxltLiteral;
-import org.apache.commons.jexl3.parser.ASTLENode;
-import org.apache.commons.jexl3.parser.ASTLTNode;
-import org.apache.commons.jexl3.parser.ASTMapEntry;
-import org.apache.commons.jexl3.parser.ASTMapLiteral;
-import org.apache.commons.jexl3.parser.ASTMethodNode;
-import org.apache.commons.jexl3.parser.ASTModNode;
-import org.apache.commons.jexl3.parser.ASTMulNode;
-import org.apache.commons.jexl3.parser.ASTNENode;
-import org.apache.commons.jexl3.parser.ASTNEWNode;
-import org.apache.commons.jexl3.parser.ASTNRNode;
-import org.apache.commons.jexl3.parser.ASTNSWNode;
-import org.apache.commons.jexl3.parser.ASTNotNode;
-import org.apache.commons.jexl3.parser.ASTNullLiteral;
-import org.apache.commons.jexl3.parser.ASTNullpNode;
-import org.apache.commons.jexl3.parser.ASTNumberLiteral;
-import org.apache.commons.jexl3.parser.ASTOrNode;
-import org.apache.commons.jexl3.parser.ASTQualifiedIdentifier;
-import org.apache.commons.jexl3.parser.ASTRangeNode;
-import org.apache.commons.jexl3.parser.ASTReference;
-import org.apache.commons.jexl3.parser.ASTReferenceExpression;
-import org.apache.commons.jexl3.parser.ASTRegexLiteral;
-import org.apache.commons.jexl3.parser.ASTReturnStatement;
-import org.apache.commons.jexl3.parser.ASTSWNode;
-import org.apache.commons.jexl3.parser.ASTSetAddNode;
-import org.apache.commons.jexl3.parser.ASTSetAndNode;
-import org.apache.commons.jexl3.parser.ASTSetDivNode;
-import org.apache.commons.jexl3.parser.ASTSetLiteral;
-import org.apache.commons.jexl3.parser.ASTSetModNode;
-import org.apache.commons.jexl3.parser.ASTSetMultNode;
-import org.apache.commons.jexl3.parser.ASTSetOrNode;
-import org.apache.commons.jexl3.parser.ASTSetShiftLeftNode;
-import org.apache.commons.jexl3.parser.ASTSetShiftRightNode;
-import org.apache.commons.jexl3.parser.ASTSetShiftRightUnsignedNode;
-import org.apache.commons.jexl3.parser.ASTSetSubNode;
-import org.apache.commons.jexl3.parser.ASTSetXorNode;
-import org.apache.commons.jexl3.parser.ASTShiftLeftNode;
-import org.apache.commons.jexl3.parser.ASTShiftRightNode;
-import org.apache.commons.jexl3.parser.ASTShiftRightUnsignedNode;
-import org.apache.commons.jexl3.parser.ASTSizeFunction;
-import org.apache.commons.jexl3.parser.ASTStringLiteral;
-import org.apache.commons.jexl3.parser.ASTSubNode;
-import org.apache.commons.jexl3.parser.ASTTernaryNode;
-import org.apache.commons.jexl3.parser.ASTTrueNode;
-import org.apache.commons.jexl3.parser.ASTUnaryMinusNode;
-import org.apache.commons.jexl3.parser.ASTUnaryPlusNode;
-import org.apache.commons.jexl3.parser.ASTVar;
-import org.apache.commons.jexl3.parser.ASTWhileStatement;
-import org.apache.commons.jexl3.parser.JexlNode;
-import org.apache.commons.jexl3.parser.ParserVisitor;
+import org.apache.commons.jexl3.parser.*;
 
 /**
  * Concrete visitor base, used for feature and operator controllers.
@@ -187,6 +101,11 @@ public class ScriptVisitor extends ParserVisitor {
         return visitNode(node, data);
     }
 
+    @Override
+    protected Object visit(final ASTTryStatement node, final Object data) {
+        return visitNode(node, data);
+    }
+
     @Override
     protected Object visit(final ASTReturnStatement node, final Object data) {
         return visitNode(node, data);
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ASTTryStatement.java 
b/src/main/java/org/apache/commons/jexl3/parser/ASTTryStatement.java
new file mode 100644
index 00000000..dde63c65
--- /dev/null
+++ b/src/main/java/org/apache/commons/jexl3/parser/ASTTryStatement.java
@@ -0,0 +1,49 @@
+/*
+ * 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.parser;
+
+/**
+ * Declares a try/catch/finally statement.
+ */
+public class ASTTryStatement extends JexlLexicalNode {
+    private static final long serialVersionUID = 1L;
+    /** try {}=0; try(expr()) &=2; try(let x = expr()); &=1 . */
+    /** catch() &= 4, finally &= 8. */
+    private int tryForm;
+
+    void setTryForm(final int form) {
+        tryForm = form;
+    }
+
+    public int getTryForm() {
+        return tryForm;
+    }
+
+    public ASTTryStatement(final int id) {
+        super(id);
+    }
+
+    public ASTTryStatement(final Parser p, final int id) {
+        super(p, id);
+    }
+
+    @Override
+    public Object jjtAccept(final ParserVisitor visitor, final Object data) {
+        return visitor.visit(this, data);
+    }
+
+}
diff --git 
a/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java 
b/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java
index 73d1c3b1..2dfa2b0a 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/FeatureController.java
@@ -131,6 +131,14 @@ public class FeatureController extends ScriptVisitor {
         return data;
     }
 
+    @Override
+    protected Object visit(final ASTTryStatement node, final Object data) {
+//        if (!features.supportsLoops()) {
+//            throwFeatureException(JexlFeatures.LOOP, node);
+//        }
+        return data;
+    }
+
     @Override
     protected Object visit(final ASTConstructorNode node, final Object data) {
         if (!features.supportsNewInstance()) {
diff --git a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt 
b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
index 527f1e30..7dbcebaa 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -29,8 +29,8 @@ options
    TRACK_TOKENS=true;
    CACHE_TOKENS=true;
    ERROR_REPORTING=false;
-  // DEBUG_PARSER=true;
-   //DEBUG_TOKEN_MANAGER=true;
+//   DEBUG_PARSER=true;
+//   DEBUG_TOKEN_MANAGER=true;
 }
 
 PARSER_BEGIN(Parser)
@@ -219,6 +219,11 @@ TOKEN_MGR_DECLS : {
     | < NULL : "null" >
     | < TRUE : "true" >
     | < FALSE : "false" >
+    | < THROW : "throw" >
+    | < TRY : "try" >
+    | < CATCH : "catch" >
+    | < FINALLY : "finally" >
+
 }
 
 /***************************************
@@ -379,6 +384,8 @@ void StatementNoVar() #void : {}
     | LOOKAHEAD(<RETURN>) ReturnStatement()
     | LOOKAHEAD(<CONTINUE>) Continue()
     | LOOKAHEAD(<BREAK>) Break()
+    | LOOKAHEAD(<THROW>) ThrowStatement()
+    | LOOKAHEAD(<TRY>) TryStatement()
     | LOOKAHEAD(Expression()) ExpressionStatement()
     | Block()
     | LOOKAHEAD(<VAR>, {!getFeatures().isLexical()} ) Var()
@@ -407,6 +414,22 @@ void IfStatement() : {}
     ( LOOKAHEAD(1) <ELSE>  (LOOKAHEAD(1) Block() | StatementNoVar()) )?
 }
 
+void TryStatement() : {
+ int tryForm = 0;}
+{
+    {
+        pushUnit(jjtThis);
+    }
+    <TRY> (<LPAREN> (LOOKAHEAD(3) InlineVar() <assign> { tryForm |= 1; })? 
Expression() <RPAREN> { tryForm |= 2; })?
+     ((LOOKAHEAD(1) Block() | StatementNoVar())  { tryForm |= 4; })
+     (LOOKAHEAD(1) <CATCH> <LPAREN> InlineVar() <RPAREN>  (LOOKAHEAD(1) 
Block() | StatementNoVar()) { tryForm |= 8; })?
+     (LOOKAHEAD(1) <FINALLY>  (LOOKAHEAD(1) Block() | StatementNoVar()) { 
tryForm |= 16; })?
+    {
+        jjtThis.setTryForm(tryForm);
+        popUnit(jjtThis);
+    }
+}
+
 void WhileStatement() : {}
 {
     <WHILE> <LPAREN> Expression() <RPAREN>  { loopCount += 1; }  (LOOKAHEAD(1) 
Block() | StatementNoVar()) { loopCount -= 1; }
@@ -422,6 +445,11 @@ void ReturnStatement() : {}
     <RETURN> (LOOKAHEAD(2) ExpressionStatement() )?
 }
 
+void ThrowStatement() : {}
+{
+    <THROW> (LOOKAHEAD(2) ExpressionStatement() )?
+}
+
 void Continue() #Continue : {
     Token t;
 }
@@ -445,7 +473,7 @@ void ForeachStatement() : {
     }
     <FOR> <LPAREN>
     (
-        LOOKAHEAD(3) ForEachVar() <COLON> Expression() { loopForm = 0; }
+        LOOKAHEAD(3) InlineVar() <COLON> Expression() { loopForm = 0; }
     |
         ((LOOKAHEAD(1) Var() | Expression()) { loopForm = 1; })? <SEMICOL>
         (Expression() { loopForm |= 2; })? <SEMICOL>
@@ -463,7 +491,7 @@ void ForeachStatement() : {
     }
 }
 
-void ForEachVar() #Reference : {}
+void InlineVar() #Reference : {}
 {
     <VAR> DeclareVar(false, false)
 |
diff --git a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java 
b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
index 66479e23..1851180a 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
+++ b/src/main/java/org/apache/commons/jexl3/parser/ParserVisitor.java
@@ -56,6 +56,8 @@ public abstract class ParserVisitor {
 
     protected abstract Object visit(ASTForeachStatement node, Object data);
 
+    protected abstract Object visit(ASTTryStatement node, Object data);
+
     protected abstract Object visit(ASTReturnStatement node, Object data);
 
     protected abstract Object visit(ASTAssignment node, Object data);
diff --git a/src/test/java/org/apache/commons/jexl3/TryCatchFinallyTest.java 
b/src/test/java/org/apache/commons/jexl3/TryCatchFinallyTest.java
new file mode 100644
index 00000000..38535f64
--- /dev/null
+++ b/src/test/java/org/apache/commons/jexl3/TryCatchFinallyTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.junit.Assert;
+import org.junit.Test;
+
+public class TryCatchFinallyTest extends JexlTestCase {
+  public TryCatchFinallyTest() {
+    super(TryCatchFinallyTest.class.getSimpleName());
+  }
+
+  @Test
+  public void testForm0x14() {
+    String src = "try { 42; } finally { 169; }";
+    JexlScript script = JEXL.createScript(src);
+    Assert.assertNotNull(script);
+    Object result = script.execute(null);
+    Assert.assertEquals(42, result);
+  }
+  @Test
+  public void testForm0x17() {
+    String src = "try(let x = 42) { x; } finally { 169; }";
+    JexlScript script = JEXL.createScript(src);
+    Assert.assertNotNull(script);
+    Object result = script.execute(null);
+    Assert.assertEquals(42, result);
+  }
+
+
+  @Test
+  public void testEdgeTry() {
+    int i = 0;
+    while(i++ < 5) {
+      System.out.println("i: " + i);
+      try {
+        throw new JexlException.Continue(null);
+      } finally {
+        continue;
+      }
+    }
+    System.out.println("iii: " + i);
+  }
+}


Reply via email to