Skip to content

Commit 2d2ee4b

Browse files
feat: PRATT 7.1 ms/ops, no errors
1 parent 365078d commit 2d2ee4b

1 file changed

Lines changed: 187 additions & 57 deletions

File tree

src/main/jjtree/net/sf/jsqlparser/parser/JSqlParserCC.jjt

Lines changed: 187 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -294,29 +294,146 @@ public class CCJSqlParser extends AbstractJSqlParser<CCJSqlParser> {
294294
}
295295
}
296296

297+
/**
298+
* Pratt arithmetic operator precedence loop.
299+
* Handles: *, /, %, ^, DIV (prec=6) and +, -, ||, |, &, <<, >> (prec=5)
300+
* Called only in real-parse mode (not syntactic-LOOKAHEAD mode) so action
301+
* blocks execute correctly. Safe now that no syntactic production LOOKAHEADs
302+
* scan through arithmetic expressions.
303+
*/
304+
/**
305+
* Pratt boolean operator precedence loop.
306+
* Handles OR (prec=2), XOR (prec=3), AND/&& (prec=4).
307+
* Safe to use now that no syntactic production LOOKAHEADs scan through expressions.
308+
*/
309+
protected Expression prattExpressionRest(Expression left, int minPrec) throws ParseException {
310+
while (!interrupted) {
311+
int op = getToken(1).kind;
312+
int prec;
313+
if (op == K_AND || op == OP_DOUBLEAND) prec = 4;
314+
else if (op == K_XOR || op == K_OR) prec = 2;
315+
else break;
316+
if (prec < minPrec) break;
317+
jj_consume_token(op, getToken(1).image);
318+
// +1 makes OR/AND/XOR left-associative
319+
Expression right = prattExpressionRest(Condition(), prec + 1);
320+
if (op == K_AND) {
321+
left = new AndExpression(left, right);
322+
} else if (op == OP_DOUBLEAND) {
323+
AndExpression a = new AndExpression(left, right);
324+
a.setUseOperator(true); left = a;
325+
} else if (op == K_XOR) {
326+
left = new XorExpression(left, right);
327+
} else {
328+
left = new OrExpression(left, right);
329+
}
330+
}
331+
return left;
332+
}
333+
334+
protected Expression prattArithRest(Expression left, int minPrec) throws ParseException {
335+
while (!interrupted) {
336+
Token t = getToken(1);
337+
int op = t.kind;
338+
int prec;
339+
340+
// Named tokens: OP_SLASH(/), OP_CARET(^), K_DIV, OP_CONCAT(||),
341+
// OP_PIPE(|), OP_LSHIFT(<<), OP_RSHIFT(>>)
342+
// String-literal tokens: *, +, -, %, & (unnamed in JavaCC grammar)
343+
if (op == OP_SLASH || op == OP_CARET || op == K_DIV) prec = 6;
344+
else if (op == OP_CONCAT || op == OP_PIPE
345+
|| op == OP_LSHIFT || op == OP_RSHIFT) prec = 5;
346+
else {
347+
// Handle unnamed tokens by image
348+
String img = t.image;
349+
if ("*".equals(img) || "%".equals(img)) prec = 6;
350+
else if ("+".equals(img) || "-".equals(img) || "&".equals(img)) prec = 5;
351+
else break; // not an arithmetic operator
352+
}
353+
354+
if (prec < minPrec) break;
355+
356+
// OP_PIPE: distinguish " | | " (space-sep concat) from single " | " (bitwise OR)
357+
if (op == OP_PIPE && getToken(2).kind == OP_PIPE) {
358+
jj_consume_token(OP_PIPE, getToken(1).image);
359+
jj_consume_token(OP_PIPE, getToken(1).image);
360+
Expression right = prattArithRest(PrimaryExpression(), prec + 1);
361+
Concat r = new Concat(); r.setLeftExpression(left); r.setRightExpression(right); left = r;
362+
continue;
363+
}
364+
365+
jj_consume_token(op, getToken(1).image);
366+
Expression right = prattArithRest(PrimaryExpression(), prec + 1);
367+
368+
if (op == OP_SLASH) { Division r = new Division(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
369+
else if (op == OP_CARET) { net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor r = new net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
370+
else if (op == K_DIV) { IntegerDivision r = new IntegerDivision(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
371+
else if (op == OP_CONCAT) { Concat r = new Concat(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
372+
else if (op == OP_PIPE) { BitwiseOr r = new BitwiseOr(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
373+
else if (op == OP_LSHIFT) { BitwiseLeftShift r = new BitwiseLeftShift(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
374+
else if (op == OP_RSHIFT) { BitwiseRightShift r = new BitwiseRightShift(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
375+
else {
376+
String img = t.image;
377+
if ("*".equals(img)) { Multiplication r = new Multiplication(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
378+
else if ("+".equals(img)) { Addition r = new Addition(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
379+
else if ("-".equals(img)) { Subtraction r = new Subtraction(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
380+
else if ("%".equals(img)) { Modulo r = new Modulo(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
381+
else if ("&".equals(img)) { BitwiseAnd r = new BitwiseAnd(); r.setLeftExpression(left); r.setRightExpression(right); left = r; }
382+
}
383+
}
384+
return left;
385+
}
386+
387+
// True when "(" opens a parenthesised FROM item (table/function/lateral/join)
388+
// rather than a SELECT subquery. Cheap 2-token check replaces expensive
389+
// syntactic LOOKAHEAD(ParenthesedFromItem()).
390+
protected boolean isParenthesedFromItemAhead() {
391+
try {
392+
if (getToken(1).kind != OPENING_BRACKET) return false;
393+
int k2 = getToken(2).kind;
394+
// Direct subquery: ( SELECT/WITH ) → ParenthesedSelect handles it
395+
// ( VALUES ) can be ParenthesedFromItem(Values) or ParenthesedSelect;
396+
// ParenthesedFromItem is checked first so Values-as-FROM-item works
397+
if (k2 == K_SELECT || k2 == K_WITH) return false;
398+
// ( ( SELECT ) UNION ( SELECT ) ) → isNestedSetOperationAhead handles it
399+
// Other (( patterns: ( (SELECT...) JOIN ... ) or ( (table) ) → ParenthesedFromItem
400+
if (k2 == K_LATERAL) return false; // handled by LateralSubSelect
401+
if (k2 == CLOSING_BRACKET) return false; // empty "()" is not valid
402+
// EXASOL: ( IMPORT ... ) is a SubImport; handled at the outer FromItem level
403+
// by LOOKAHEAD(2, Dialect.EXASOL...) SubImport() - must NOT go into ParenthesedFromItem
404+
if (k2 == K_IMPORT) return false;
405+
return true;
406+
} catch (TokenMgrError e) { return false; }
407+
}
408+
297409
// True when ( (SELECT ...) UNION/INTERSECT/EXCEPT (SELECT ...) ) — a set-operation
298410
// subquery used as IN RHS. Bracket-scans past the inner select to verify a set
299411
// operator follows, distinguishing from comma-separated ((SELECT...), (SELECT...)).
300412
protected boolean isNestedSetOperationAhead() {
301413
try {
302414
if (getToken(1).kind != OPENING_BRACKET) return false;
303415
if (getToken(2).kind != OPENING_BRACKET) return false;
304-
int k3 = getToken(3).kind;
305-
if (k3 != K_SELECT && k3 != K_WITH && k3 != K_VALUES) return false;
306-
// Scan forward counting brackets to find end of inner ( SELECT ... )
307-
int depth = 1; // inside the inner (
308-
int i = 4;
416+
// Skip any run of opening brackets: ( ( ( ... ( SELECT
417+
int i = 3;
418+
while (getToken(i).kind == OPENING_BRACKET) i++;
419+
int k = getToken(i).kind;
420+
if (k != K_SELECT && k != K_WITH && k != K_VALUES) return false;
421+
// Scan forward counting brackets to find the matching ) for token(2)
422+
// We are now inside all the opening brackets; depth = number of brackets skipped - 1
423+
// (token(1) is the outer one, token(2..i-1) are the inner ones we scanned)
424+
int depth = i - 2; // brackets from position 2 to i-1 inclusive
425+
i++;
309426
while (i <= 500) {
310427
Token t; try { t = getToken(i); } catch (TokenMgrError e) { return false; }
311-
if (t == null || t.kind == 0) return false; // EOF
428+
if (t == null || t.kind == 0) return false;
312429
if (t.kind == OPENING_BRACKET) depth++;
313430
else if (t.kind == CLOSING_BRACKET) {
314431
if (--depth == 0) break;
315432
}
316433
i++;
317434
}
318435
if (i > 500) return false;
319-
// Token at position i+1 is what follows the inner closing )
436+
// Token at position i+1 follows the innermost closing )
320437
Token next; try { next = getToken(i + 1); } catch (TokenMgrError e) { return false; }
321438
if (next == null) return false;
322439
return next.kind == K_UNION || next.kind == K_INTERSECT ||
@@ -5474,9 +5591,7 @@ SelectItem<?> SelectItem() #SelectItem:
54745591
(
54755592
LOOKAHEAD( 3 ) expression = ConnectByPriorOperator()
54765593
|
5477-
LOOKAHEAD( 3 ) expression = XorExpression()
5478-
|
5479-
expression=Expression()
5594+
( expression=Condition() { expression = prattExpressionRest(expression, 2); } )
54805595
)
54815596
[ LOOKAHEAD({ isAliasAhead() }) alias=Alias() ]
54825597
{
@@ -5962,7 +6077,7 @@ FromItem FromItem() #FromItem:
59626077
[ LOOKAHEAD(2) unpivot=UnPivot() { fromItem.setUnPivot(unpivot); } ]
59636078
)
59646079
|
5965-
LOOKAHEAD(ParenthesedFromItem()) fromItem = ParenthesedFromItem()
6080+
LOOKAHEAD({ isParenthesedFromItemAhead() }) fromItem = ParenthesedFromItem()
59666081
|
59676082
LOOKAHEAD(3, { !getAsBoolean(Feature.allowUnparenthesizedSubSelects) }) (
59686083
fromItem=ParenthesedSelect()
@@ -6088,8 +6203,8 @@ Join JoinerExpression() #JoinerExpression:
60886203
[
60896204
LOOKAHEAD(2) (
60906205
[ <K_WITHIN> "(" joinWindow = JoinWindow() ")" {join.setJoinWindow(joinWindow);} ]
6091-
( <K_ON> onExpression=Expression() { join.addOnExpression(onExpression); }
6092-
( LOOKAHEAD(2) <K_ON> onExpression=Expression() { join.addOnExpression(onExpression); } )*
6206+
( <K_ON> onExpression=Condition() { onExpression = prattExpressionRest(onExpression, 2); join.addOnExpression(onExpression); }
6207+
( LOOKAHEAD(2) <K_ON> onExpression=Condition() { onExpression = prattExpressionRest(onExpression, 2); join.addOnExpression(onExpression); } )*
60936208
)
60946209
|
60956210
(
@@ -6183,16 +6298,17 @@ Expression WhereClause():
61836298
Expression retval = null;
61846299
}
61856300
{
6186-
<K_WHERE> retval=Expression()
6187-
{ return retval; }
6301+
<K_WHERE> retval=Condition()
6302+
{ retval = prattExpressionRest(retval, 2); return retval; }
61886303
}
61896304

61906305
Expression PreWhereClause():
61916306
{
61926307
Expression retval = null;
61936308
}
61946309
{
6195-
<K_PREWHERE> retval=Expression()
6310+
<K_PREWHERE> retval=Condition()
6311+
{ retval = prattExpressionRest(retval, 2); }
61966312
{ return retval; }
61976313
}
61986314

@@ -6763,7 +6879,8 @@ Expression Expression() #Expression :
67636879
Expression expression = null;
67646880
}
67656881
{
6766-
expression=XorExpression()
6882+
expression=Condition()
6883+
{ expression = prattExpressionRest(expression, 2); }
67676884
{
67686885
linkAST(expression,jjtThis);
67696886
return expression;
@@ -6778,9 +6895,12 @@ Expression XorExpression():
67786895
{
67796896
result=AndChain()
67806897
(
6781-
LOOKAHEAD(2) <K_OR> right=AndChain() { result = new OrExpression(result, right); }
6782-
|
6783-
LOOKAHEAD(2) <K_XOR> right=AndChain() { result = new XorExpression(result, right); }
6898+
LOOKAHEAD(2)
6899+
(
6900+
<K_OR> right=AndChain() { result = new OrExpression(result, right); }
6901+
|
6902+
<K_XOR> right=AndChain() { result = new XorExpression(result, right); }
6903+
)
67846904
)*
67856905
{ return result; }
67866906
}
@@ -6794,14 +6914,17 @@ Expression AndChain():
67946914
{
67956915
result=Condition()
67966916
(
6797-
LOOKAHEAD(2) <K_AND> right=Condition() {
6798-
AndExpression a = new AndExpression(result, right); result = a;
6799-
}
6800-
|
6801-
LOOKAHEAD(2) <OP_DOUBLEAND> right=Condition() {
6802-
AndExpression a = new AndExpression(result, right);
6803-
a.setUseOperator(true); result = a;
6804-
}
6917+
LOOKAHEAD(2)
6918+
(
6919+
<K_AND> right=Condition() {{
6920+
AndExpression a = new AndExpression(result, right); result = a;
6921+
}}
6922+
|
6923+
<OP_DOUBLEAND> right=Condition() {{
6924+
AndExpression a = new AndExpression(result, right);
6925+
a.setUseOperator(true); result = a;
6926+
}}
6927+
)
68056928
)*
68066929
{ return result; }
68076930
}
@@ -6816,16 +6939,16 @@ Expression Condition():
68166939
int oracleJoin = EqualsTo.NO_ORACLE_JOIN;
68176940
}
68186941
{
6819-
[ LOOKAHEAD(2) (<K_NOT> { not=true; } | "!" { not=true; exclamationMarkNot=true; })]
6942+
[ LOOKAHEAD(2, { getToken(1).kind == K_NOT || "!".equals(getToken(1).image) }) (<K_NOT> { not=true; } | "!" { not=true; exclamationMarkNot=true; })]
68206943
(
68216944
result=ExistsExpression()
68226945
|
6823-
[ LOOKAHEAD(2) <K_PRIOR> { oraclePrior = EqualsTo.ORACLE_PRIOR_START; } ]
6946+
[ LOOKAHEAD(2, { getToken(1).kind == K_PRIOR }) <K_PRIOR> { oraclePrior = EqualsTo.ORACLE_PRIOR_START; } ]
68246947
left=SimpleExpression() { result = left; }
68256948

68266949
// Consume Oracle (+) once, before dispatching
68276950
[
6828-
LOOKAHEAD("(" "+" ")")
6951+
LOOKAHEAD("(" "+" ")", { getToken(1).kind == OPENING_BRACKET })
68296952
"(" "+" ")"
68306953
{
68316954
oracleJoin = EqualsTo.ORACLE_JOIN_RIGHT;
@@ -7476,8 +7599,9 @@ Expression SimpleExpression():
74767599
Token operation = null;
74777600
}
74787601
{
7479-
[ LOOKAHEAD( 5 ) user = UserVariable() ( operation = "=" | operation = ":=" ) ]
7480-
retval=AddChain()
7602+
[ LOOKAHEAD( 5, { getToken(1).kind == S_AT_IDENTIFIER } ) user = UserVariable() ( operation = "=" | operation = ":=" ) ]
7603+
retval=PrimaryExpression()
7604+
{ retval = prattArithRest(retval, 5); }
74817605
{
74827606
if (user != null) {
74837607
VariableAssignment assignment = new VariableAssignment();
@@ -7499,15 +7623,18 @@ Expression MulChain():
74997623
{
75007624
retval=PrimaryExpression()
75017625
(
7502-
LOOKAHEAD(2) "*" right=PrimaryExpression() { Multiplication r = new Multiplication(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7503-
|
7504-
LOOKAHEAD(2) <OP_SLASH> right=PrimaryExpression() { Division r = new Division(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7505-
|
7506-
LOOKAHEAD(2) "%" right=PrimaryExpression() { Modulo r = new Modulo(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7507-
|
7508-
LOOKAHEAD(2) <OP_CARET> right=PrimaryExpression() { net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor r = new net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7509-
|
7510-
LOOKAHEAD(2) <K_DIV> right=PrimaryExpression() { IntegerDivision r = new IntegerDivision(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7626+
LOOKAHEAD(2)
7627+
(
7628+
"*" right=PrimaryExpression() {{ Multiplication r = new Multiplication(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7629+
|
7630+
<OP_SLASH> right=PrimaryExpression() {{ Division r = new Division(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7631+
|
7632+
"%" right=PrimaryExpression() {{ Modulo r = new Modulo(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7633+
|
7634+
<OP_CARET> right=PrimaryExpression() {{ net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor r = new net.sf.jsqlparser.expression.operators.arithmetic.BitwiseXor(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7635+
|
7636+
<K_DIV> right=PrimaryExpression() {{ IntegerDivision r = new IntegerDivision(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7637+
)
75117638
)*
75127639
{ return retval; }
75137640
}
@@ -7521,21 +7648,24 @@ Expression AddChain():
75217648
{
75227649
retval=MulChain()
75237650
(
7524-
LOOKAHEAD(2) "+" right=MulChain() { Addition r = new Addition(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7525-
|
7526-
LOOKAHEAD(2) "-" right=MulChain() { Subtraction r = new Subtraction(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7527-
|
7528-
LOOKAHEAD(2) <OP_CONCAT> right=MulChain() { Concat r = new Concat(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7529-
|
7530-
LOOKAHEAD(2) <OP_PIPE> <OP_PIPE> right=MulChain() { Concat r = new Concat(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7531-
|
7532-
LOOKAHEAD(2) <OP_PIPE> right=MulChain() { BitwiseOr r = new BitwiseOr(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7533-
|
7534-
LOOKAHEAD(2) "&" right=MulChain() { BitwiseAnd r = new BitwiseAnd(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7535-
|
7536-
LOOKAHEAD(2) <OP_LSHIFT> right=MulChain() { BitwiseLeftShift r = new BitwiseLeftShift(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7537-
|
7538-
LOOKAHEAD(2) <OP_RSHIFT> right=MulChain() { BitwiseRightShift r = new BitwiseRightShift(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }
7651+
LOOKAHEAD(2)
7652+
(
7653+
"+" right=MulChain() {{ Addition r = new Addition(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7654+
|
7655+
"-" right=MulChain() {{ Subtraction r = new Subtraction(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7656+
|
7657+
<OP_CONCAT> right=MulChain() {{ Concat r = new Concat(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7658+
|
7659+
LOOKAHEAD(2) <OP_PIPE> <OP_PIPE> right=MulChain() {{ Concat r = new Concat(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7660+
|
7661+
<OP_PIPE> right=MulChain() {{ BitwiseOr r = new BitwiseOr(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7662+
|
7663+
"&" right=MulChain() {{ BitwiseAnd r = new BitwiseAnd(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7664+
|
7665+
<OP_LSHIFT> right=MulChain() {{ BitwiseLeftShift r = new BitwiseLeftShift(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7666+
|
7667+
<OP_RSHIFT> right=MulChain() {{ BitwiseRightShift r = new BitwiseRightShift(); r.setLeftExpression(retval); r.setRightExpression(right); retval = r; }}
7668+
)
75397669
)*
75407670
{ return retval; }
75417671
}

0 commit comments

Comments
 (0)