Daniel Sun created GROOVY-12398:
-----------------------------------

             Summary: Improve lexer and parser performance
                 Key: GROOVY-12398
                 URL: https://issues.apache.org/jira/browse/GROOVY-12398
             Project: Groovy
          Issue Type: Improvement
            Reporter: Daniel Sun


The Groovy ANTLR4 grammar already uses left-recursive expressions, which is the 
main idea of the [optimized Java 
grammar|https://github.com/antlr/grammars-v4/tree/master/java/java]. Several 
remaining lexer and parser costs still match patterns that grammar was written 
to avoid.

h3. Problem

* Number literals are assembled from a chain of one-character fragments 
({{Digit}}, {{Zero}}, {{NonZeroDigit}}, ...). The invalid-octal alternative 
also counts digits in a mid-rule action loop, which prevents DFA compilation of 
{{IntegerLiteral}}.
* Every identifier, including ASCII names such as {{Foo}} and {{foo}}, 
evaluates {{Character.isUpperCase}} to choose {{CapitalizedIdentifier}} vs 
{{Identifier}}.
* GString and comment predicates ({{isFollowedByJavaLetterInGString}}, 
{{isFollowedByWhiteSpaces}}) allocate a one-character {{String}} and run 
{{Pattern.matcher}} on each lookahead.
* {{elementValues}} is ambiguous with assignment: {{@Foo(a = 1)}} can be named 
pairs or an assignment expression, so {{AdaptivePredict}} explores both (see 
[antlr/grammars-v4#215|https://github.com/antlr/grammars-v4/issues/215]).

h3. Approach

Keep token types and language semantics unchanged.

* Flatten number and float fragments to character classes, as in the optimized 
Java lexer. Validate invalid octal once at the end of the alternative.
* Split ASCII identifiers on a predicate-free DFA path: uppercase {{A-Z}} 
becomes {{CapitalizedIdentifier}}; {{a-z}}, dollar and underscore become 
{{Identifier}}. Unicode still uses predicates.
* Replace the regex-based predicates with direct character tests.
* Gate {{elementValue}} with {{!isIdentifierAssign(_input)}}, the same idea as 
Java's {{IsNotIdentifierAssign}}.

h3. Result

Local parse-only measurement (SLL then LL) of {{src/main/groovy}} + 
{{src/test-resources/core}} (245 files, approximately 570k characters):

||Run||Best||Mean||
|before|0.231s|0.401s|
|after|0.146s|0.169s|

Best run is about 1.6x faster. Mean improves more because prediction is more 
stable after DFA warmup.


Related: GROOVY-10454, GROOVY-9588




--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to