ANTLR4语法性能非常差

ANTLR4语法性能非常差,antlr,antlr4,Antlr,Antlr4,根据下面的语法,我发现解析较长字符串时的性能非常差,以秒为单位。在Python和Go实现中都会出现这种情况,这种语法中有什么原因导致这种情况吗 示例输出: 0.000061s LEXING "hello world" 0.014349s PARSING "hello world" 0.000052s LEXING 5 + 10 0.015384s PARSING 5 + 10 0.000061s LEXING FIRST_WORD(WORD_SLICE(contact.blerg, 2, 4))

根据下面的语法,我发现解析较长字符串时的性能非常差,以秒为单位。在Python和Go实现中都会出现这种情况,这种语法中有什么原因导致这种情况吗

示例输出:

0.000061s LEXING "hello world"
0.014349s PARSING "hello world"
0.000052s LEXING 5 + 10
0.015384s PARSING 5 + 10
0.000061s LEXING FIRST_WORD(WORD_SLICE(contact.blerg, 2, 4))
0.634113s PARSING FIRST_WORD(WORD_SLICE(contact.blerg, 2, 4))
0.000095s LEXING (DATEDIF(DATEVALUE("01-01-1970"), date.now, "D") * 24 * 60 * 60) + ((((HOUR(date.now)+7) * 60) + MINUTE(date.now)) * 60))
1.552758s PARSING (DATEDIF(DATEVALUE("01-01-1970"), date.now, "D") * 24 * 60 * 60) + ((((HOUR(date.now)+7) * 60) + MINUTE(date.now)) * 60))
这是关于Python的。。虽然我不期望有出色的性能,但我希望任何输入都能达到亚秒级。我做错了什么

grammar Excellent;

parse
  : expr EOF
  ;

expr
  :  atom                                                    # expAtom
  |  concatenationExpr                                       # expConcatenation
  |  equalityExpr                                            # expEquality
  |  comparisonExpr                                          # expComparison
  |  additionExpr                                            # expAddition
  |  multiplicationExpr                                      # expMultiplication
  |  exponentExpr                                            # expExponent
  |  unaryExpr                                               # expUnary
  ;

path
  :  NAME (step)*
  ;

step
  : LBRAC expr RBRAC
  | PATHSEP NAME
  | PATHSEP NUMBER
  ;

parameters
  : expr (COMMA expr)*                                       # functionParameters
  ;

concatenationExpr
  : atom (AMP concatenationExpr)?                            # concatenation
  ;

equalityExpr
  :  comparisonExpr op=(EQ|NE) comparisonExpr                # equality
  ;

comparisonExpr
  :  additionExpr (op=(LT|GT|LTE|GTE) additionExpr)?         # comparison
  ;

additionExpr
  :  multiplicationExpr (op=(ADD|SUB) multiplicationExpr)*   # addition
  ;

multiplicationExpr
  :  exponentExpr (op=(MUL|DIV) exponentExpr)*               # multiplication
  ;

exponentExpr
  :  unaryExpr (EXP exponentExpr)?                           # exponent
  ;

unaryExpr
  : SUB? atom                                                # negation
  ;

funcCall
  : function=NAME LPAR parameters? RPAR                      # functionCall
  ;

funcPath
  : function=funcCall (step)*                                # functionPath
  ;

atom
  :  path                                                    # contextReference
  |  funcCall                                                # atomFuncCall
  |  funcPath                                                # atomFuncPath
  |  LITERAL                                                 # stringLiteral
  |  NUMBER                                                  # decimalLiteral
  |  LPAR expr RPAR                                          # parentheses
  |  TRUE                                                    # true
  |  FALSE                                                   # false
  ;

NUMBER
  :  DIGITS ('.' DIGITS?)?
  ;

fragment
DIGITS
  :  ('0'..'9')+
  ;

TRUE
  : [Tt][Rr][Uu][Ee]
  ;

FALSE
  : [Ff][Aa][Ll][Ss][Ee]
  ;

PATHSEP
       :'.';
LPAR
       :'(';
RPAR
       :')';
LBRAC
       :'[';
RBRAC
       :']';
SUB
       :'-';
ADD
       :'+';
MUL
       :'*';
DIV
       :'/';
COMMA
       :',';
LT
       :'<';
GT
       :'>';
EQ
       :'=';
NE
       :'!=';
LTE
       :'<=';
GTE
       :'>=';
QUOT
       :'"';
EXP
       : '^';
AMP
       : '&';

LITERAL
  :  '"' ~'"'* '"'
  ;

Whitespace
  :  (' '|'\t'|'\n'|'\r')+ ->skip
  ;

NAME
  :  NAME_START_CHARS NAME_CHARS*
  ;

fragment
NAME_START_CHARS
  :  'A'..'Z'
  |   '_'
  |  'a'..'z'
  |  '\u00C0'..'\u00D6'
  |  '\u00D8'..'\u00F6'
  |  '\u00F8'..'\u02FF'
  |  '\u0370'..'\u037D'
  |  '\u037F'..'\u1FFF'
  |  '\u200C'..'\u200D'
  |  '\u2070'..'\u218F'
  |  '\u2C00'..'\u2FEF'
  |  '\u3001'..'\uD7FF'
  |  '\uF900'..'\uFDCF'
  |  '\uFDF0'..'\uFFFD'
  ;

fragment
NAME_CHARS
  :  NAME_START_CHARS
  | '0'..'9'
  |  '\u00B7' | '\u0300'..'\u036F'
  |  '\u203F'..'\u2040'
  ;

ERRROR_CHAR
  : .
  ;

您始终可以首先尝试使用SLL*进行解析,只有当解析失败时,您才需要使用默认的LL*进行解析

请参阅ANTLR的GitHub上的ticket以获得进一步的解释,这是一个使用此策略的实现


当解析语法正确的输入时,此方法将为您节省大量时间。

您始终可以尝试首先使用SLL*进行解析,并且只有当解析失败时,您才需要使用默认的LL*进行解析

请参阅ANTLR的GitHub上的ticket以获得进一步的解释,这是一个使用此策略的实现


当解析语法正确的输入时,此方法将为您节省大量时间。

这种性能似乎是由于加法/乘法等运算符中使用了左递归。相反,将这些规则重写为二进制规则会产生即时的性能。见下文

grammar Excellent;

COMMA      : ',';
LPAREN     : '(';
RPAREN     : ')';
LBRACK     : '[';
RBRACK     : ']';

DOT        : '.';

PLUS       : '+';
MINUS      : '-';
TIMES      : '*';
DIVIDE     : '/';
EXPONENT   : '^';

EQ         : '=';
NEQ        : '!=';

LTE        : '<=';
LT         : '<';
GTE        : '>=';
GT         : '>';

AMPERSAND  : '&';

DECIMAL    : [0-9]+('.'[0-9]+)?;
STRING     : '"' (~["] | '""')* '"';

TRUE       : [Tt][Rr][Uu][Ee];
FALSE      : [Ff][Aa][Ll][Ss][Ee];

NAME       : [a-zA-Z][a-zA-Z0-9_.]*;    // variable names, e.g. contact.name or function names, e.g. SUM

WS         : [ \t\n\r]+ -> skip;        // ignore whitespace

ERROR      : . ;

parse      : expression EOF;

atom       : fnname LPAREN parameters? RPAREN             # functionCall
           | atom DOT atom                                # dotLookup
           | atom LBRACK expression RBRACK                # arrayLookup
           | NAME                                         # contextReference
           | STRING                                       # stringLiteral
           | DECIMAL                                      # decimalLiteral
           | TRUE                                         # true
           | FALSE                                        # false
           ;

expression : atom                                         # atomReference
           | MINUS expression                             # negation
           | expression EXPONENT expression               # exponentExpression
           | expression (TIMES | DIVIDE) expression       # multiplicationOrDivisionExpression
           | expression (PLUS | MINUS) expression         # additionOrSubtractionExpression
           | expression (LTE | LT | GTE | GT) expression  # comparisonExpression
           | expression (EQ | NEQ) expression             # equalityExpression
           | expression AMPERSAND expression              # concatenation
           | LPAREN expression RPAREN                     # parentheses
           ;

fnname     : NAME
           | TRUE
           | FALSE
           ;

parameters : expression (COMMA expression)*               # functionParameters
           ;

这种性能似乎是由于加法/乘法等运算符中使用了左递归。相反,将这些规则重写为二进制规则会产生即时的性能。见下文

grammar Excellent;

COMMA      : ',';
LPAREN     : '(';
RPAREN     : ')';
LBRACK     : '[';
RBRACK     : ']';

DOT        : '.';

PLUS       : '+';
MINUS      : '-';
TIMES      : '*';
DIVIDE     : '/';
EXPONENT   : '^';

EQ         : '=';
NEQ        : '!=';

LTE        : '<=';
LT         : '<';
GTE        : '>=';
GT         : '>';

AMPERSAND  : '&';

DECIMAL    : [0-9]+('.'[0-9]+)?;
STRING     : '"' (~["] | '""')* '"';

TRUE       : [Tt][Rr][Uu][Ee];
FALSE      : [Ff][Aa][Ll][Ss][Ee];

NAME       : [a-zA-Z][a-zA-Z0-9_.]*;    // variable names, e.g. contact.name or function names, e.g. SUM

WS         : [ \t\n\r]+ -> skip;        // ignore whitespace

ERROR      : . ;

parse      : expression EOF;

atom       : fnname LPAREN parameters? RPAREN             # functionCall
           | atom DOT atom                                # dotLookup
           | atom LBRACK expression RBRACK                # arrayLookup
           | NAME                                         # contextReference
           | STRING                                       # stringLiteral
           | DECIMAL                                      # decimalLiteral
           | TRUE                                         # true
           | FALSE                                        # false
           ;

expression : atom                                         # atomReference
           | MINUS expression                             # negation
           | expression EXPONENT expression               # exponentExpression
           | expression (TIMES | DIVIDE) expression       # multiplicationOrDivisionExpression
           | expression (PLUS | MINUS) expression         # additionOrSubtractionExpression
           | expression (LTE | LT | GTE | GT) expression  # comparisonExpression
           | expression (EQ | NEQ) expression             # equalityExpression
           | expression AMPERSAND expression              # concatenation
           | LPAREN expression RPAREN                     # parentheses
           ;

fnname     : NAME
           | TRUE
           | FALSE
           ;

parameters : expression (COMMA expression)*               # functionParameters
           ;

您的联系人是什么。blerg?您使用的是哪一版本的ANTLR4?我使用的是Antlr 4.7您的联系人是什么。blerg?您使用的是哪一版本的ANTLR4?我使用的是Antlr 4.7。在实际情况下,SLL*和LL*之间是否存在显著的性能差异?LL*在进行自上而下的解析时可能会有更多的歧义,这种歧义会如何影响性能?是的,有。。。我用LL*解析文件需要约20秒,用SLL*解析文件需要约600毫秒。我从来没有遇到过SLL*在语法正确的输入方面失败的情况。如果这样做了,解析器将切换回LL*,因此在这种情况下,以前完成的SLL*解析所用的时间将被浪费。但正如我所说,对于有效输入,在实际情况下,SLL*和LL*之间是否存在显著的性能差异?LL*在进行自上而下的解析时可能会有更多的歧义,这种歧义会如何影响性能?是的,有。。。我用LL*解析文件需要约20秒,用SLL*解析文件需要约600毫秒。我从来没有遇到过SLL*在语法正确的输入方面失败的情况。如果这样做了,解析器将切换回LL*,因此在这种情况下,以前完成的SLL*解析所用的时间将被浪费。但正如我所说的,我从未体验过有效输入的这种情况,难道你把语法搞混了吗?这个答案中的语法使用左递归,而你问题中的语法不使用左递归。这与您的答案文本相矛盾,您说左递归移除会导致更好的性能。完全可能我的命名不正确,将把示例和语法作为示例,以便其他人能够理解。您是否会在这里混淆语法?这个答案中的语法使用左递归,而你问题中的语法不使用左递归。这与你的答案文本相矛盾,你说左递归移除会导致更好的性能。完全可能我的命名不正确,我会将示例和语法作为示例,以便其他人能够理解。