equalityExpression and functionCallOrPathExpression in the grammar/Kql.g4 each have several alternatives that begin with the same prefix, forcing ANTLR to parse entire expressions before finding the correct one. During testing in Python, left-factoring both improved cold-start parse time by ~7x.
Four of the five alternatives in equalityExpression begin with relationalExpression:
equalityExpression:
relationalExpression
| equalsEqualityExpression
| listEqualityExpression
| betweenEqualityExpression
| starEqualityExpression
;
equalsEqualityExpression:
Left=relationalExpression OperatorToken=('==' | '<>' | '!=') Right=relationalExpression;
listEqualityExpression:
Left=relationalExpression OperatorToken=(IN | NOT_IN | IN_CI | NOT_IN_CI | HAS_ANY | HAS_ALL) '(' Expressions+=invocationExpression (',' Expressions+=invocationExpression)* ')';
betweenEqualityExpression:
Left=relationalExpression OperatorToken=(BETWEEN | NOT_BETWEEN) '(' StartExpression=invocationExpression '..' EndExpression=invocationExpression ')';
Two of the three alternatives in functionCallOrPathExpression begin with functionCallOrPathRoot:
functionCallOrPathExpression:
functionCallOrPathRoot
| functionCallOrPathPathExpression
| toTableExpression
;
functionCallOrPathPathExpression:
Expression=functionCallOrPathRoot (Operations+=functionCallOrPathOperation)+;
Proposed patch:
--- upstream/Kql.g4 2026-09-20 13:35:23.381480187 +0000
+++ upstream_patched/Kql.g4 2026-09-20 13:36:24.993425830 +0000
@@ -896,21 +896,15 @@
AND Right=equalityExpression;
equalityExpression:
- relationalExpression
- | equalsEqualityExpression
- | listEqualityExpression
- | betweenEqualityExpression
- | starEqualityExpression
+ starEqualityExpression
+ | Left=relationalExpression (Tail=equalityExpressionTail)?
;
-equalsEqualityExpression:
- Left=relationalExpression OperatorToken=('==' | '<>' | '!=') Right=relationalExpression;
-
-listEqualityExpression:
- Left=relationalExpression OperatorToken=(IN | NOT_IN | IN_CI | NOT_IN_CI | HAS_ANY | HAS_ALL) '(' Expressions+=invocationExpression (',' Expressions+=invocationExpression)* ')';
-
-betweenEqualityExpression:
- Left=relationalExpression OperatorToken=(BETWEEN | NOT_BETWEEN) '(' StartExpression=invocationExpression '..' EndExpression=invocationExpression ')';
+equalityExpressionTail:
+ OperatorToken=('==' | '<>' | '!=') Right=relationalExpression # equalsEqualityExpression
+ | OperatorToken=(IN | NOT_IN | IN_CI | NOT_IN_CI | HAS_ANY | HAS_ALL) '(' Expressions+=invocationExpression (',' Expressions+=invocationExpression)* ')' # listEqualityExpression
+ | OperatorToken=(BETWEEN | NOT_BETWEEN) '(' StartExpression=invocationExpression '..' EndExpression=invocationExpression ')' # betweenEqualityExpression
+ ;
starEqualityExpression:
'*' '==' Expression=relationalExpression;
@@ -987,9 +981,8 @@
(OperatorToken=('+' | '-'))? Expression=functionCallOrPathExpression;
functionCallOrPathExpression:
- functionCallOrPathRoot
- | functionCallOrPathPathExpression
- | toTableExpression
+ toTableExpression # toTablePathExpression
+ | Expression=functionCallOrPathRoot (Operations+=functionCallOrPathOperation)* # functionCallOrPathPathExpression
;
functionCallOrPathRoot:
@@ -998,9 +991,6 @@
| toScalarExpression
;
-functionCallOrPathPathExpression:
- Expression=functionCallOrPathRoot (Operations+=functionCallOrPathOperation)+;
-
functionCallOrPathOperation:
functionCallOrPathPathOperation
| functionCallOrPathElementOperation
equalityExpressionandfunctionCallOrPathExpressionin the grammar/Kql.g4 each have several alternatives that begin with the same prefix, forcing ANTLR to parse entire expressions before finding the correct one. During testing in Python, left-factoring both improved cold-start parse time by ~7x.Four of the five alternatives in
equalityExpressionbegin withrelationalExpression:Two of the three alternatives in
functionCallOrPathExpressionbegin withfunctionCallOrPathRoot:Proposed patch: