20
20
import java .util .function .BiFunction ;
21
21
import java .util .function .Function ;
22
22
23
+ import org .antlr .v4 .runtime .BailErrorStrategy ;
23
24
import org .antlr .v4 .runtime .CharStream ;
24
25
import org .antlr .v4 .runtime .CharStreams ;
25
26
import org .antlr .v4 .runtime .CommonTokenStream ;
28
29
import org .antlr .v4 .runtime .ParserRuleContext ;
29
30
import org .antlr .v4 .runtime .TokenStream ;
30
31
import org .antlr .v4 .runtime .atn .PredictionMode ;
32
+ import org .antlr .v4 .runtime .misc .ParseCancellationException ;
31
33
import org .antlr .v4 .runtime .tree .ParseTreeVisitor ;
34
+
32
35
import org .springframework .data .domain .Sort ;
33
36
import org .springframework .data .repository .query .ReturnedType ;
34
37
import org .springframework .lang .Nullable ;
@@ -68,9 +71,35 @@ class JpaQueryEnhancer<Q extends QueryInformation> implements QueryEnhancer {
68
71
this .projection = tokens .isEmpty () ? "" : new QueryRenderer .TokenRenderer (tokens ).render ();
69
72
}
70
73
74
+ /**
75
+ * Parse the query and return the parser context (AST). This method attempts parsing the query using
76
+ * {@link PredictionMode#SLL} first to attempt a fast-path parse without using the context. If that fails, it retries
77
+ * using {@link PredictionMode#LL} which is much slower, however it allows for contextual ambiguity resolution.
78
+ */
71
79
static <P extends Parser > ParserRuleContext parse (String query , Function <CharStream , Lexer > lexerFactoryFunction ,
72
80
Function <TokenStream , P > parserFactoryFunction , Function <P , ParserRuleContext > parseFunction ) {
73
81
82
+ P parser = getParser (query , lexerFactoryFunction , parserFactoryFunction );
83
+
84
+ parser .getInterpreter ().setPredictionMode (PredictionMode .SLL );
85
+ parser .setErrorHandler (new BailErrorStrategy ());
86
+
87
+ try {
88
+
89
+ return parseFunction .apply (parser );
90
+ } catch (BadJpqlGrammarException | ParseCancellationException e ) {
91
+
92
+ parser = getParser (query , lexerFactoryFunction , parserFactoryFunction );
93
+ // fall back to LL(*)-based parsing
94
+ parser .getInterpreter ().setPredictionMode (PredictionMode .LL );
95
+
96
+ return parseFunction .apply (parser );
97
+ }
98
+ }
99
+
100
+ private static <P extends Parser > P getParser (String query , Function <CharStream , Lexer > lexerFactoryFunction ,
101
+ Function <TokenStream , P > parserFactoryFunction ) {
102
+
74
103
Lexer lexer = lexerFactoryFunction .apply (CharStreams .fromString (query ));
75
104
P parser = parserFactoryFunction .apply (new CommonTokenStream (lexer ));
76
105
@@ -82,11 +111,11 @@ static <P extends Parser> ParserRuleContext parse(String query, Function<CharStr
82
111
83
112
configureParser (query , grammar .toUpperCase (), lexer , parser );
84
113
85
- return parseFunction . apply ( parser ) ;
114
+ return parser ;
86
115
}
87
116
88
117
/**
89
- * Apply common configuration (SLL prediction for performance, our own error listeners) .
118
+ * Apply common configuration.
90
119
*
91
120
* @param query the query input to parse.
92
121
* @param grammar name of the grammar.
@@ -100,8 +129,6 @@ static void configureParser(String query, String grammar, Lexer lexer, Parser pa
100
129
lexer .removeErrorListeners ();
101
130
lexer .addErrorListener (errorListener );
102
131
103
- parser .getInterpreter ().setPredictionMode (PredictionMode .SLL );
104
-
105
132
parser .removeErrorListeners ();
106
133
parser .addErrorListener (errorListener );
107
134
}
0 commit comments