diff --git a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt index a2122b90a..18ccaa5d4 100644 --- a/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt +++ b/src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt @@ -12715,23 +12715,9 @@ ExpressionList ExpressionList() #ExpressionList: ExpressionList expressionList; } { - ( - LOOKAHEAD(3, { !interrupted }) expressionList = ComplexExpressionList() - | - LOOKAHEAD(3) expressionList = SimpleExpressionList() - | - LOOKAHEAD(3) expressionList = ParenthesedExpressionList() - | - // Fallback, see isUnparenthesizedSelectAhead(): the three probes above are - // syntactic and cannot see through the Pratt operator loop, which lives in a - // Java action. They all fail on a bracketed operand of a larger expression, - // e.g. "( 1 + a ) / ( 1 + b )" or "( 1 + a )" - the third token is an operator - // where the probe expects "," or ")". Such input is still a valid expression - // list, so parse it as one instead of raising a ParseException. - LOOKAHEAD({ !interrupted }) expressionList = ComplexExpressionList() - | - expressionList = SimpleExpressionList() - ) + // ComplexExpressionList() parses every list form, "()" and bracketed operands such as + // "( 1 + a ) / ( 1 + b )" included. + expressionList = ComplexExpressionList() { // Avoid redundant ExpressionLists containing only one ParenthesedExpressionList // return the Parenthesed Sub ExpressionList instead @@ -12763,11 +12749,7 @@ ParenthesedExpressionList ParenthesedExpressionList(): } { "(" - ( - LOOKAHEAD({ !interrupted }) expressions = ComplexExpressionList() - | - expressions = SimpleExpressionList() - )? + [ LOOKAHEAD({ getToken(1).kind != CLOSING_BRACKET }) expressions = ComplexExpressionList() ] ")" { return new ParenthesedExpressionList(expressions); @@ -12835,7 +12817,7 @@ ExpressionList ComplexExpressionList(): | LOOKAHEAD(2, { isNamedParameterAssignmentAhead() }) expr=PostgresNamedFunctionParameter() | - // JavaCC scans an unguarded alternative's first token; a semantic-only lookahead enters directly + // The semantic lookahead makes JavaCC enter Expression() with no scan of its first token. LOOKAHEAD({ !interrupted }) expr=Expression() ) { diff --git a/src/test/java/net/sf/jsqlparser/expression/FunctionTest.java b/src/test/java/net/sf/jsqlparser/expression/FunctionTest.java index deb508c19..6456918b1 100644 --- a/src/test/java/net/sf/jsqlparser/expression/FunctionTest.java +++ b/src/test/java/net/sf/jsqlparser/expression/FunctionTest.java @@ -157,4 +157,14 @@ void testAesDecryptWithKeyExpressionInSelect() throws JSQLParserException { TestUtils.assertSqlCanBeParsedAndDeparsed(sqlStr, true); } + + @Test + void testBracketedOperandAndEmptyListArguments() throws JSQLParserException { + TestUtils.assertSqlCanBeParsedAndDeparsed( + "SELECT f((1 + a) / (1 + b), (c + d) * 2) FROM t", true); + + TestUtils.assertSqlCanBeParsedAndDeparsed("SELECT f(()) FROM t", true); + + TestUtils.assertSqlCanBeParsedAndDeparsed("INSERT INTO t VALUES ()", true); + } }