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

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


The following commit(s) were added to refs/heads/JEXL-445 by this push:
     new 39628e59 JEXL-445 : revised grammar by Marc Mazas (thanks :-)), few 
remaining constructs to check; - update test error check;
39628e59 is described below

commit 39628e59a64e7b5df532d11bcb226b64d2c590ba
Author: Henrib <[email protected]>
AuthorDate: Sun Sep 21 19:16:35 2025 +0200

    JEXL-445 : revised grammar by Marc Mazas (thanks :-)), few remaining 
constructs to check;
    - update test error check;
---
 .../org/apache/commons/jexl3/parser/Parser.jjt     | 337 +++++++++++++--------
 .../java/org/apache/commons/jexl3/LambdaTest.java  |   2 +-
 2 files changed, 206 insertions(+), 133 deletions(-)

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 220b23cf..a5fa5e71 100644
--- a/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
+++ b/src/main/java/org/apache/commons/jexl3/parser/Parser.jjt
@@ -32,6 +32,7 @@ options
    ERROR_REPORTING=false;
 //   DEBUG_PARSER=true;
 //   DEBUG_TOKEN_MANAGER=true;
+  // FORCE_LA_CHECK = true; // (JavaCC - default false)
 }
 
 PARSER_BEGIN(Parser)
@@ -53,7 +54,7 @@ public final class Parser extends JexlParser
         public Parser(JexlParser parser) {
             super(parser);
             jj_input_stream = new SimpleCharStream(new StringProvider(""), 1, 
1);
-            token_source = new ParserTokenManager(jj_input_stream, DEFAULT);
+            token_source = new ParserTokenManager(jj_input_stream);
             token = new Token();
             token.next = jj_nt = token_source.getNextToken();
         }
@@ -93,10 +94,13 @@ public final class Parser extends JexlParser
             throw new JexlException.Tokenization(info, xtme).clean();
         } catch (ParseException xparse) {
             Token errortok = errorToken(jj_lastpos, jj_scanpos, token.next, 
token);
-            throw new JexlException.Parsing(info.at(errortok.beginLine, 
errortok.beginColumn), errortok.image).clean();
+            String msg = xparse.getMessage();
+            if (msg.endsWith(";")) {
+              msg = errortok.image;
+            }
+            throw new JexlException.Parsing(info.at(errortok.beginLine, 
errortok.beginColumn), msg).clean();
         } finally {
             //token_source.defaultLexState = DEFAULT;
-            token_source.SwitchTo(DEFAULT);
             cleanup(previous);
             jjtree.reset();
         }
@@ -116,14 +120,15 @@ TOKEN_MGR_DECLS : {
 
 <*> SKIP : /* WHITE SPACE */
 {
-      " "
-    | "\t"
-    | "\n"
-    | "\r"
-    | "\f"
-    | <"##" (~["\n","\r"])* ("\n" | "\r" | "\r\n")? >
-    | <"/*" (~["*"])* ( "*" ~["*","/"] (~["*"])* )* "*/">
-    | <"//" (~["\n","\r"])* ("\n" | "\r" | "\r\n")? >
+      < sp_ : " " >
+    | < tab_ : "\t" >
+    | < lf_: "\n" >
+    | < cr_ : "\r" >
+    | < ff_ : "\f" >
+    | < pslc_ : "##" (~["\n","\r"])* ("\n" | "\r" | "\r\n")? >
+//    | < mlc1_ : "/*" (~["*"])* "*" ("*" | ~["*","/"] (~["*"])* "*")* "/">
+    | < mlc2_ : "/*" (~["*"])* ( "*" ~["*","/"] (~["*"])* )* "*/" >
+    | < sslc_ : "//" (~["\n","\r"])* ("\n" | "\r" | "\r\n")? >
 }
 
 <DEFAULT> TOKEN : /* Exception handling. */
@@ -364,7 +369,7 @@ TOKEN_MGR_DECLS : {
   > : DEFAULT
 }
 
-<DEFAULT> TOKEN :
+<*> TOKEN :
 {
   < REGEX_LITERAL:
     "~" "/" (~["/","\n","\r","\t","\f","\b","\u2028","\u2029"] | "\\" "/" )* 
"/"
@@ -383,7 +388,7 @@ ASTJexlScript JexlScript(Scope frame, JexlFeatures 
features) : {
    {
         pushUnit(jjtThis);
    }
-        (LOOKAHEAD(<IMPORT>) Import())* ( LOOKAHEAD(<PRAGMA>) Pragma() | { 
controlPragmaAnywhere(); } Statement() )* <EOF>
+        ( Import() )* ( Pragma() | { controlPragmaAnywhere(); } Statement() )* 
<EOF>
    {
         popUnit(jjtThis);
         return jjtThis.script();
@@ -410,71 +415,75 @@ void Annotation() #Annotation :
     Token t;
 }
 {
-    t=<ANNOTATION> { jjtThis.setName(t.image); } (LOOKAHEAD(<LPAREN>) 
Arguments() )?
+    t=<ANNOTATION> { jjtThis.setName(t.image); } (LOOKAHEAD(2) Arguments() )?
 }
 
 void AnnotatedStatement() #AnnotatedStatement : {}
  {
-    (LOOKAHEAD(<ANNOTATION>) Annotation())+ (LOOKAHEAD(1) Block() | 
Statement())
+    (LOOKAHEAD(2) Annotation())+ Statement()
  }
 
 void Statement() #void : {}
 {
-    LOOKAHEAD(<LET>|<CONST>|<VAR>) Var()
+    LOOKAHEAD(3) Var()
     |
-    LOOKAHEAD(<FUNCTION> <IDENTIFIER>) FunctionStatement()
+    LOOKAHEAD(3) FunctionStatement()
     |
-    StatementNoVar()
+    StatementSink()
 }
 
-void StatementNoVar() #void : {}
+void StatementSink() #void : {} 
 {
     <SEMICOL>
-    | LOOKAHEAD(<ANNOTATION>) AnnotatedStatement()
-    | LOOKAHEAD(<IF>) IfStatement()
-    | LOOKAHEAD(<FOR>) ForeachStatement()
-    | LOOKAHEAD(<WHILE>) WhileStatement()
-    | LOOKAHEAD(<DO>) DoWhileStatement()
-    | LOOKAHEAD(<RETURN>) ReturnStatement()
-    | LOOKAHEAD(<CONTINUE>) Continue()
-    | LOOKAHEAD(<BREAK>) Break()
-    | LOOKAHEAD(<THROW>) ThrowStatement()
-    | LOOKAHEAD(<TRY>) TryStatement()
-    | LOOKAHEAD(<SWITCH>) SwitchStatement()
-    | LOOKAHEAD(LambdaLookahead()) Lambda()
-    | LOOKAHEAD(Expression()) ExpressionStatement()
+    | AnnotatedStatement()
+    | IfStatement()
+    | ForeachStatement()
+    | WhileStatement()
+    | DoWhileStatement()
+    | ReturnStatement()
+    | Continue()
+    | Break()
+    | ThrowStatement()
+    | TryStatement()
+    | LOOKAHEAD(3) SwitchStatement()
+    | LOOKAHEAD(LambdaLookahead()) Lambda() // changing to LA(3) causes 
AssertionFailedError: expected: <true> but was: <false> in 
LambdaTest.testFailParseFunc
+    | LOOKAHEAD(Expression()) ExpressionStatement() // changing to LA(3) 
produces 29 more errors and 1 failure
     | Block()
-    | LOOKAHEAD(<VAR>, { !getFeatures().isLexical()} ) Var()
+    | LOOKAHEAD(<VAR>, { !getFeatures().isLexical()} ) Var() // LA necessary 
(otherwise 2 failures in LambdaTest and LexicalTest) but strange
 }
 
 void Block() #Block : {}
 {
-    <LCURLY> { pushUnit(jjtThis); } ( LOOKAHEAD(<PRAGMA>)  Pragma() | 
Statement() )* { popUnit(jjtThis); } <RCURLY>
+    <LCURLY> { pushUnit(jjtThis); } ( Pragma() | Statement() )* { 
popUnit(jjtThis); } <RCURLY>
 }
 
 void FunctionStatement() #JexlLambda : {}
 {
-<FUNCTION> DeclareFunction() { beginLambda(jjtThis); } Parameters() ( 
LOOKAHEAD(3) Block() | Expression()) { endLambda(jjtThis); }
+    <FUNCTION> DeclareFunction() { beginLambda(jjtThis); } Parameters() ( 
LOOKAHEAD(3) Block() | Expression()) { endLambda(jjtThis); }
 }
 
 void ExpressionStatement() #void : {}
 {
-    Expression() (LOOKAHEAD(Expression(), { isAmbiguousStatement(SEMICOL) } ) 
Expression() #Ambiguous(1))* (LOOKAHEAD(1) <SEMICOL>)*
+    Expression() (LOOKAHEAD(2, { isAmbiguousStatement(SEMICOL) } ) 
Expression() #Ambiguous(1))* (LOOKAHEAD(2) <SEMICOL>)* // why 0-n and not just 
0-1? others could be parsed as StatementSink() as in Import()
 }
 
 
 void IfStatement() : {}
 {
-    <IF> <LPAREN> Expression() <RPAREN> (LOOKAHEAD(1) Block() | 
StatementNoVar())
-    ( LOOKAHEAD(2) <ELSE> <IF> <LPAREN> Expression() <RPAREN> (LOOKAHEAD(1) 
Block() | StatementNoVar()) )*
-    ( LOOKAHEAD(1) <ELSE>  (LOOKAHEAD(1) Block() | StatementNoVar()) )?
+  // StatementSink() already includes Block(), so either the following choices 
should be removed or Block() should be removed from StatementSink()
+  // However removing the choices produces assertion failures in 3 tests 
probably because of the resulting different node trees, and so these tests 
would need to be modified
+  // BlockTest.testEmptyBlock:76 Result is wrong ==> expected: <null> but was: 
<[]>;  Issues100Test.test108:307 expected: <true> but was: <false>;  
IssuesTest.test90:340 expected: <2> but was: <[2]>
+  // For the moment, we keep the old version, here and further 
(WhileStatement(), DoWhileStatement(), ForeachStatement, SwitchStatementCase)
+    <IF> <LPAREN> Expression() <RPAREN> (LOOKAHEAD(3) Block() | 
StatementSink())
+    ( LOOKAHEAD(2) <ELSE> <IF> <LPAREN> Expression() <RPAREN> (LOOKAHEAD(3) 
Block() | StatementSink()) )*
+    ( LOOKAHEAD(2) <ELSE>  (LOOKAHEAD(3) Block() | StatementSink()) )?
 }
 
 void TryStatement() : {}
 {
-     <TRY> (LOOKAHEAD(1) TryResources() | Block())
-     (LOOKAHEAD(1) <CATCH> { pushUnit(jjtThis); } <LPAREN> InlineVar() 
<RPAREN> Block() { jjtThis.catchClause(); popUnit(jjtThis);})?
-     (LOOKAHEAD(1) <FINALLY>  Block() { jjtThis.finallyClause(); })?
+     <TRY> ( TryResources() | Block())
+     ( <CATCH> { pushUnit(jjtThis); } <LPAREN> InlineVar() <RPAREN> Block() { 
jjtThis.catchClause(); popUnit(jjtThis);})?
+     ( <FINALLY>  Block() { jjtThis.finallyClause(); })?
 }
 
 void TryResources() : {}
@@ -482,9 +491,9 @@ void TryResources() : {}
     {
         pushUnit(jjtThis);
     }
-<LPAREN>
-    TryResource() ( LOOKAHEAD(2) <SEMICOL> TryResource() )* (<SEMICOL>)?
-<RPAREN>
+    <LPAREN>
+        TryResource() ( LOOKAHEAD(2) <SEMICOL> TryResource() )* (<SEMICOL>)?
+    <RPAREN>
     Block()
     {
         popUnit(jjtThis);
@@ -498,12 +507,12 @@ LOOKAHEAD(2) Var() | Identifier(true)
 
 void WhileStatement() : {}
 {
-    <WHILE> <LPAREN> Expression() <RPAREN>  { loopCount.incrementAndGet(); }  
(LOOKAHEAD(1) Block() | StatementNoVar()) { loopCount.decrementAndGet(); }
+    <WHILE> <LPAREN> Expression() <RPAREN>  { loopCount.incrementAndGet(); }  
(LOOKAHEAD(1) Block() | StatementSink()) { loopCount.decrementAndGet(); } // 
see IfStatement()
 }
 
 void DoWhileStatement() : {}
 {
-    <DO> { loopCount.incrementAndGet(); } (LOOKAHEAD(1) Block() | 
StatementNoVar()) <WHILE> <LPAREN> Expression() <RPAREN> { 
loopCount.decrementAndGet(); }
+    <DO> { loopCount.incrementAndGet(); } (LOOKAHEAD(1) Block() | 
StatementSink()) <WHILE> <LPAREN> Expression() <RPAREN> { 
loopCount.decrementAndGet(); } // see IfStatement()
 }
 
 void ReturnStatement() : {
@@ -545,7 +554,7 @@ void ForeachStatement() : {
     (
         LOOKAHEAD(3) InlineVar() <COLON> Expression() { loopForm = 0; }
     |
-        ((LOOKAHEAD(1) Var() | Expression()) { loopForm = 1; })? <SEMICOL>
+        ((LOOKAHEAD(3) Var() | Expression()) { loopForm = 1; })? <SEMICOL>
         (Expression() { loopForm |= 2; })? <SEMICOL>
         (Expression() { loopForm |= 4; })? { loopForm |= 8; }
     )
@@ -553,7 +562,7 @@ void ForeachStatement() : {
     {
         loopCount.incrementAndGet();
     }
-        (LOOKAHEAD(1) Block() | StatementNoVar() )
+        (LOOKAHEAD(1) Block() | StatementSink() ) // see IfStatement()
     {
         loopCount.decrementAndGet();
         jjtThis.setLoopForm(loopForm);
@@ -583,12 +592,12 @@ void Var() #void : {}
 
  void DefineVar() #void : {}
  {
-    DeclareVar(false, false) (LOOKAHEAD(1) <assign> Expression() 
#Assignment(2))?
+    DeclareVar(false, false) ( <assign> Expression() #Assignment(2))?
  }
 
 void DefineLet() #void : {}
 {
-    DeclareVar(true, false) (LOOKAHEAD(1) <assign> Expression() 
#Assignment(2))?
+    DeclareVar(true, false) ( <assign> Expression() #Assignment(2))?
 }
 
 void DefineConst() #void : {}
@@ -638,7 +647,7 @@ void SwitchStatementCase(SwitchSet cases) #CaseStatement :
         { constants = Collections.singletonList(constant); 
jjtThis.setValues(constants); cases.addAll(constants); } )+
         |
         <CASE_DEFAULT> <COLON> )
-    ( LOOKAHEAD(<LCURLY>) Block() | (StatementNoVar())+ )?
+    ( LOOKAHEAD(1) Block() | (StatementSink())+ )? // see IfStatement()
 {
     loopCount.decrementAndGet();
 }
@@ -665,7 +674,7 @@ void SwitchExpressionCase(SwitchSet cases) #CaseExpression :
     List<Object> constants = new ArrayList<Object>(1);
 }
 {
-    ( LOOKAHEAD(2)
+    (
         <CASE> ConstLiterals(constants) <LAMBDA> { cases.addAll(constants); 
jjtThis.setValues(constants); }
         |
         <CASE_DEFAULT> <LAMBDA>
@@ -678,7 +687,7 @@ void ConstLiterals(List<Object> constants) #void :
     Object constant;
 }
 {
-    constant=constLiteral() { constants.add(constant); } (LOOKAHEAD(2) <COMMA> 
constant=constLiteral() { constants.add(constant); })*
+    constant=constLiteral() { constants.add(constant); } ( <COMMA> 
constant=constLiteral() { constants.add(constant); } )*
 }
 
 
@@ -692,13 +701,13 @@ Object constLiteral() #void :
 {
   (
       LOOKAHEAD(2) (s=<plus>|s=<minus>)? v=<INTEGER_LITERAL> { result = 
NumberParser.parseInteger(s, v); }
-    | LOOKAHEAD(2) (s=<plus>|s=<minus>)? v=<FLOAT_LITERAL> { result = 
NumberParser.parseDouble(s, v); }
-    | LOOKAHEAD(1) v=<STRING_LITERAL> { result = Parser.buildString(v.image, 
true); }
-    | LOOKAHEAD(1) <TRUE> { result = true; }
-    | LOOKAHEAD(1) <FALSE> { result = false; }
-    | LOOKAHEAD(1) <NULL> { result = null; }
-    | LOOKAHEAD(1) <NAN_LITERAL> { result = Double.NaN; }
-    | LOOKAHEAD(1) propertyKey(lstr) { result = 
resolveConstant(stringify(lstr)); }
+    | (s=<plus>|s=<minus>)? v=<FLOAT_LITERAL> { result = 
NumberParser.parseDouble(s, v); }
+    | v=<STRING_LITERAL> { result = Parser.buildString(v.image, true); }
+    | <TRUE> { result = true; }
+    | <FALSE> { result = false; }
+    | <NULL> { result = null; }
+    | <NAN_LITERAL> { result = Double.NaN; }
+    | propertyKey(lstr) { result = resolveConstant(stringify(lstr)); }
   )
   {
     return result;
@@ -710,7 +719,7 @@ void Import() #void :
     List<String> imports = new ArrayList<String>();
 }
 {
-    <IMPORT> propertyKey(imports) (LOOKAHEAD(1) <SEMICOL>)?
+    <IMPORT> propertyKey(imports) (LOOKAHEAD(2) <SEMICOL>)?
     {
        addImport(stringify(imports));
     }
@@ -730,28 +739,28 @@ void propertyKey(List<String> lstr) #void :
     Token t;
 }
 {
-    t=<IDENTIFIER>  { lstr.add(t.image); } ( LOOKAHEAD(<DOT>) 
propertyKey(lstr) )*
+    t=<IDENTIFIER>  { lstr.add(t.image); } (LOOKAHEAD(1) <DOT> 
t=<DOT_IDENTIFIER> { lstr.add(t.image); } )*
    |
     <DOT> t=<DOT_IDENTIFIER> { lstr.add(t.image); }
 }
 
 Object pragmaValue() #void :
 {
-Token s = null;
-Token v;
-LinkedList<String> lstr = new LinkedList<String>();
-Object result;
+    Token s = null;
+    Token v;
+    LinkedList<String> lstr = new LinkedList<String>();
+    Object result;
 }
 {
   (
       LOOKAHEAD(2) (s=<plus>|s=<minus>)? v=<INTEGER_LITERAL> { result = 
NumberParser.parseInteger(s, v); }
-    | LOOKAHEAD(2) (s=<plus>|s=<minus>)? v=<FLOAT_LITERAL> { result = 
NumberParser.parseDouble(s, v); }
-    | LOOKAHEAD(1) v=<STRING_LITERAL> { result = Parser.buildString(v.image, 
true); }
-    | LOOKAHEAD(1)  propertyKey(lstr) { result = stringify(lstr); }
-    | LOOKAHEAD(1) <TRUE> { result = true; }
-    | LOOKAHEAD(1) <FALSE> { result = false; }
-    | LOOKAHEAD(1) <NULL> { result = null; }
-    | LOOKAHEAD(1) <NAN_LITERAL> { result = Double.NaN; }
+    | (s=<plus>|s=<minus>)? v=<FLOAT_LITERAL> { result = 
NumberParser.parseDouble(s, v); }
+    | v=<STRING_LITERAL> { result = Parser.buildString(v.image, true); }
+    | propertyKey(lstr) { result = stringify(lstr); }
+    | <TRUE> { result = true; }
+    | <FALSE> { result = false; }
+    | <NULL> { result = null; }
+    | <NAN_LITERAL> { result = Double.NaN; }
   )
   {
     return result;
@@ -957,7 +966,7 @@ void PostfixOperator() #void : {}
 
 void PostfixExpression() #void : {}
 {
-   ValueExpression() [ LOOKAHEAD(1) PostfixOperator() ]
+   ValueExpression() [ LOOKAHEAD(2) PostfixOperator() ]
 }
 
 /***************************************
@@ -1096,7 +1105,7 @@ void MapLiteral() : {}
     <LCURLY>
     (
         MapEntry() (LOOKAHEAD(2) <COMMA> MapEntry() )*
-            (<COMMA> (ExtendedLiteral(){ jjtThis.setExtended(true); })? )?
+            (<COMMA> (ExtendedLiteral() { jjtThis.setExtended(true); })? )?
     |
         <COLON>
     ) <RCURLY>
@@ -1104,7 +1113,7 @@ void MapLiteral() : {}
 
 void MapEntry() : {}
 {
-    LOOKAHEAD(2) Identifier(true) <COLON> Expression()
+    LOOKAHEAD(3/*2*/) Identifier(true) <COLON> Expression()
     |
     Expression() <COLON> Expression()
 }
@@ -1128,20 +1137,24 @@ void Arguments() #Arguments : {}
      <LPAREN> (Expression() (<COMMA> Expression())* )? <RPAREN>
 }
 
+// Quite unfrequent, a LA in a production called in a LA... need to check what 
is generated and executed.
 void FunctionCallLookahead() #void : {}
 {
-    LOOKAHEAD(4, <IDENTIFIER> <COLON> <IDENTIFIER> <LPAREN>,  { 
isNamespaceFuncall(getToken(1), getToken(2), getToken(3), getToken(4)) }) 
<IDENTIFIER> <COLON> <IDENTIFIER> <LPAREN>
-    |
-    LOOKAHEAD(2) <IDENTIFIER> <LPAREN>
-    |
-    LOOKAHEAD(2) <REGISTER> <LPAREN>
+    LOOKAHEAD(4, <IDENTIFIER> <COLON> <IDENTIFIER> <LPAREN>, { 
isNamespaceFuncall(getToken(1), getToken(2), getToken(3), getToken(4)) })
+    <IDENTIFIER> <COLON> <IDENTIFIER> <LPAREN>
+  |
+   <IDENTIFIER> <LPAREN>
+  |
+   <REGISTER> <LPAREN>
 }
 
 void FunctionCall() #void : {}
 {
-    LOOKAHEAD(4, <IDENTIFIER> <COLON> <IDENTIFIER> <LPAREN>,  { 
isNamespaceFuncall(getToken(1), getToken(2), getToken(3), getToken(4)) })  
NamespaceIdentifier() Arguments() #FunctionNode(2)
-    |
-    LOOKAHEAD(<IDENTIFIER> <LPAREN>) Identifier(true) Arguments() 
#FunctionNode(2)
+  // semantic LA already performed in FunctionCallLookahead(), just need to 
scan up to the COLON to resolve the following conflict
+    LOOKAHEAD(2)
+    NamespaceIdentifier() Arguments() #FunctionNode(2)
+  |
+    Identifier(true) Arguments() #FunctionNode(2)
 }
 
 void QualifiedIdentifier() #QualifiedIdentifier : {
@@ -1158,35 +1171,65 @@ void Constructor() #ConstructorNode : {}
   <NEW> QualifiedIdentifier()  <LPAREN> [ Expression() ( <COMMA> Expression() 
)* ] <RPAREN>
 }
 
+// Writing productions to be used only in LA (LambdaLookahead() & 
ParametersLookahead()) is really non-academic...
+// It is very hard to check whether they are coherent with the productions 
they are meant to look ahead (Lambda() & Parameters()),
+// so one needs to format lines in a way that eases this at the maximum... and 
bingo... see further
+
 void Parameter() #void :
 {
     Token t;
 }
 {
     (<VAR>)? t=<IDENTIFIER> { declareParameter(t, false, false); }
-    |
-    <LET> t=<IDENTIFIER> { declareParameter(t, true, false); }
-    |
-    <CONST> t=<IDENTIFIER> { declareParameter(t, true, true); }
+  |
+    <LET>    t=<IDENTIFIER> { declareParameter(t, true, false); }
+  |
+    <CONST>  t=<IDENTIFIER> { declareParameter(t, true, true); }
 }
 
 void Parameters() #void : {}
 {
-     <LPAREN> [Parameter() (<COMMA> Parameter())* ] <RPAREN>
+     <LPAREN>
+     [
+       Parameter()
+       ( <COMMA> Parameter() )*
+     ]
+     <RPAREN>
 }
 
 void ParametersLookahead() #void : {}
 {
-     <LPAREN> [(<VAR>|<LET>|<CONST>)? <IDENTIFIER> (<COMMA> 
((<VAR>|<LET>|<CONST>)? <IDENTIFIER>))*] <RPAREN>
+     <LPAREN>
+     [
+       ( <VAR> | <LET> | <CONST> )? <IDENTIFIER>
+       (
+         <COMMA>
+         ( <VAR> | <LET> | <CONST> )? <IDENTIFIER>
+       )*
+     ]
+     <RPAREN>
 }
 
 void LambdaLookahead() #void : {}
 {
-  <FUNCTION> ParametersLookahead()
+    <FUNCTION>
+// the following SHOULD be ADDED to correspond to ( DeclareFunction() )?, as 
it definitely looks missing
+// but LambdaTest.testFailParseFunc(String, String) fails with 
AssertionFailedError: expected: <true> but was: <false>
+// don't the test data first 2 lines expectedKeyword parameters should be 
changed to
+//    @CsvSource({
+//            "'if (false) function foo(x) { x + x }; var foo = 1', 'foo'",
+//            "'if (false) let foo = (x) -> { x + x }; var foo = 1', 'foo'",
+//            "'function foo(x) { x + x }; var foo = 42', 'foo'"
+//    })
+// which would be coherent with the 3rd line?
+    ( <IDENTIFIER> )?
+    ParametersLookahead()
   |
-  ParametersLookahead() (<LAMBDA> | <FATARROW>)
+    ParametersLookahead()
+    ( <LAMBDA> | <FATARROW> )
   |
-  <IDENTIFIER> (<LAMBDA> | <FATARROW>)
+    <IDENTIFIER>
+    ( <LAMBDA> | <FATARROW> )
 }
 
 void Lambda() #JexlLambda :
@@ -1194,12 +1237,24 @@ void Lambda() #JexlLambda :
    Token arrow;
 }
 {
-  <FUNCTION> (LOOKAHEAD(<IDENTIFIER>) DeclareFunction())? {
-   beginLambda(jjtThis); } Parameters() ( LOOKAHEAD(3) Block() | Expression()) 
{ endLambda(jjtThis); }
+    <FUNCTION>
+    ( DeclareFunction() )?
+    { beginLambda(jjtThis); }
+    Parameters()
+    ( LOOKAHEAD(3) Block() | Expression() )
+    { endLambda(jjtThis); }
   |
-  { beginLambda(jjtThis); } Parameters() (arrow=<LAMBDA> | arrow=<FATARROW>) ( 
LOOKAHEAD(3) Block() | Expression()) { checkLambda(arrow); endLambda(jjtThis); }
+    { beginLambda(jjtThis); }
+    Parameters()
+    ( arrow=<LAMBDA> | arrow=<FATARROW> )
+    ( LOOKAHEAD(3) Block() | Expression() )
+    { checkLambda(arrow); endLambda(jjtThis); }
   |
-  { beginLambda(jjtThis); } Parameter() (arrow=<LAMBDA> | arrow=<FATARROW>)( 
LOOKAHEAD(3) Block() | Expression()) { checkLambda(arrow); endLambda(jjtThis); }
+    { beginLambda(jjtThis); }
+    Parameter()
+    ( arrow=<LAMBDA> | arrow=<FATARROW> )
+    ( LOOKAHEAD(3) Block() | Expression() )
+    { checkLambda(arrow); endLambda(jjtThis); }
 }
 
 
@@ -1213,20 +1268,24 @@ void IdentifierAccess() #void :
     Token t;
 }
 {
-    <DOT> (
-        t=<DOT_IDENTIFIER> { jjtThis.setIdentifier(t.image); } 
#IdentifierAccess
+  // it looks that in v8 you need the enclosing '(' and ')' to delimit the 
nodes, otherwise <DOT_IDENTIFIER> is not included in it
+  // it looks to be a regression from v7, caused by the presence of the final 
actions not handled in the same way
+    <DOT>
+    (
+      ( t=<DOT_IDENTIFIER> { jjtThis.setIdentifier(t.image); }                 
          ) #IdentifierAccess
     |
-        t=<STRING_LITERAL> { jjtThis.setIdentifier(Parser.buildString(t.image, 
true)); } #IdentifierAccess
+      ( t=<STRING_LITERAL> { jjtThis.setIdentifier(Parser.buildString(t.image, 
true)); } ) #IdentifierAccess
     |
-        t=<JXLT_LITERAL> { jjtThis.setIdentifier(Parser.buildString(t.image, 
true), getScope()); } #IdentifierAccessJxlt
+       (t=<JXLT_LITERAL>   { jjtThis.setIdentifier(Parser.buildString(t.image, 
true), getScope()); } ) #IdentifierAccessJxlt
     )
+  |
+    <QDOT>
+    (
+      ( t=<DOT_IDENTIFIER> { jjtThis.setIdentifier(t.image); }                 
          ) #IdentifierAccessSafe
     |
-    <QDOT> (
-        t=<DOT_IDENTIFIER> { jjtThis.setIdentifier(t.image); } 
#IdentifierAccessSafe
-    |
-        t=<STRING_LITERAL> { jjtThis.setIdentifier(Parser.buildString(t.image, 
true)); } #IdentifierAccessSafe
+      ( t=<STRING_LITERAL> { jjtThis.setIdentifier(Parser.buildString(t.image, 
true)); } ) #IdentifierAccessSafe
     |
-        t=<JXLT_LITERAL> { jjtThis.setIdentifier(Parser.buildString(t.image, 
true), getScope()); } #IdentifierAccessSafeJxlt
+      ( t=<JXLT_LITERAL>   { jjtThis.setIdentifier(Parser.buildString(t.image, 
true), getScope()); } ) #IdentifierAccessSafeJxlt
     )
 }
 
@@ -1235,42 +1294,51 @@ void ArrayAccess() : {
  int s = 0;
  }
 {
-    (LOOKAHEAD(2) (<LBRACKET>|<QLBRACKET> { safe |= (1L << s++); }) 
Expression() <RBRACKET>)+ { jjtThis.setSafe(safe); }
+  ( LOOKAHEAD(2)
+    ( < LBRACKET > | < QLBRACKET > { safe |= (1L << s++); } )
+    Expression() < RBRACKET >
+  ) +
+  { jjtThis.setSafe(safe); }
 }
 
 void MemberAccess() #void : {}
 {
-    LOOKAHEAD(<LBRACKET>|<QLBRACKET>) ArrayAccess()
-    |
-    LOOKAHEAD(<DOT>) IdentifierAccess()
-    |
-    LOOKAHEAD(<QDOT>) IdentifierAccess()
+    /*LOOKAHEAD(<LBRACKET>|<QLBRACKET>)*/ ArrayAccess()
+    | // why differentiating both cases???
+    /*LOOKAHEAD(<DOT>)*/ IdentifierAccess()
+//    |
+//    LOOKAHEAD(<QDOT>) IdentifierAccess()
 }
 
 void ReferenceExpression() #MethodNode(>1) : {}
 {
-    <LPAREN> Expression() <RPAREN> #ReferenceExpression(1) ( 
LOOKAHEAD(<LPAREN>) Arguments() )*
+    <LPAREN> Expression() <RPAREN> #ReferenceExpression(1) ( 
LOOKAHEAD(2/*<LPAREN>*/) Arguments() )*
 }
 
 void PrimaryExpression() #void : {}
 {
-       LOOKAHEAD( LambdaLookahead() ) Lambda()
-    |
-       LOOKAHEAD( <LPAREN> ) ReferenceExpression()
-    |
-       LOOKAHEAD( <LCURLY> Expression() <COLON>) MapLiteral()
+       LOOKAHEAD(LambdaLookahead()) Lambda() // changing to LA(3) causes 22 
errors and 1 failure
     |
-       LOOKAHEAD( <LCURLY> <COLON>) MapLiteral()
+       /*LOOKAHEAD( <LPAREN> )*/ ReferenceExpression()
     |
-       LOOKAHEAD( <LCURLY> Expression() (<COMMA> | <RCURLY>)) SetLiteral()
+    // this LA does not accept MapLiteral() with 2 MapEntry() separated by a 
COMMA, which will be accepted by the 2nd next LA...
+//       LOOKAHEAD( <LCURLY> Expression() <COLON>) MapLiteral()
+//    |
+//       LOOKAHEAD( <LCURLY> <COLON>) MapLiteral()
+// so merge the previous 2 in 1 with a full syntactic LA
+       LOOKAHEAD( MapLiteral() ) MapLiteral()
     |
-       LOOKAHEAD( <LCURLY> <RCURLY> ) SetLiteral()
+    // no need to distinguish the following 2
+//       LOOKAHEAD( <LCURLY> Expression() (<COMMA> | <RCURLY>)) SetLiteral()
+//    |
+//       LOOKAHEAD( <LCURLY> <RCURLY> ) SetLiteral()
+       SetLiteral()
     |
-       LOOKAHEAD( <LBRACKET> ) ArrayLiteral()
+       /*LOOKAHEAD( <LBRACKET> )*/ ArrayLiteral()
     |
-       LOOKAHEAD( <NEW> ) Constructor()
+       /*LOOKAHEAD( <NEW> )*/ Constructor()
     |
-       LOOKAHEAD( <SWITCH> ) SwitchExpression()
+       /*LOOKAHEAD( <SWITCH> )*/ SwitchExpression()
     |
        LOOKAHEAD( FunctionCallLookahead() ) FunctionCall()
     |
@@ -1281,23 +1349,28 @@ void PrimaryExpression() #void : {}
 
 void MethodCall() #void : {}
 {
-    (MemberAccess() (LOOKAHEAD(<LPAREN>) Arguments())+) #MethodNode(>1)
+  (
+    MemberAccess()
+    ( LOOKAHEAD(2 /*<LPAREN>*/) Arguments() )+
+  ) #MethodNode(> 1)
 }
 
 
-void MethodCallLookahead() #void : {}
-{
-    MemberAccess() <LPAREN>
-}
+// Too simple to be worth isolated, more clear to inline it
+//void MethodCallLookahead() #void : {}
+//{
+//    MemberAccess() <LPAREN>
+//}
 
 
 void MemberExpression() #void : {}
 {
-    LOOKAHEAD(MethodCallLookahead()) MethodCall() | MemberAccess()
+//    LOOKAHEAD( MethodCallLookahead() ) MethodCall() | MemberAccess()
+    LOOKAHEAD( MemberAccess() <LPAREN> ) MethodCall() | MemberAccess()
 }
 
 void ValueExpression() #void : {}
 {
-    ( PrimaryExpression() ( LOOKAHEAD(2) MemberExpression() )*) #Reference(>1)
+    ( PrimaryExpression() ( LOOKAHEAD(2) MemberExpression() )* ) #Reference(>1)
 }
 
diff --git a/src/test/java/org/apache/commons/jexl3/LambdaTest.java 
b/src/test/java/org/apache/commons/jexl3/LambdaTest.java
index c8f6b39c..7e36f74c 100644
--- a/src/test/java/org/apache/commons/jexl3/LambdaTest.java
+++ b/src/test/java/org/apache/commons/jexl3/LambdaTest.java
@@ -217,7 +217,7 @@ class LambdaTest extends JexlTestCase {
 
     @ParameterizedTest
     @CsvSource({
-            "'if (false) function foo(x) { x + x }; var foo = 1', 'function'",
+            "'if (false) function foo(x) { x + x }; var foo = 1', 'foo'",
             "'if (false) let foo = (x) -> { x + x }; var foo = 1', 'let'",
             "'function foo(x) { x + x }; var foo = 42', 'foo'"
     })

Reply via email to