2017-04-19 12 views
0

以下の文法を考えれば、長い文字列を解析するときのパフォーマンスは非常に劣ります(秒オーダー)。 (これはPythonとGoの両方の実装にあります)この文法には何かがありますか?ANTLR4文法パフォーマンスが非常に悪い

出力例:

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 
    : . 
    ; 
+0

あなたの 'contact.blerg'とは何ですか? –

+0

使用しているANTLR4のバージョンは? –

+0

私はAntlr 4.7を使用しています –

答えて

0

あなたはいつもそれはあなたが(デフォルトで)LL(*)とそれを解析する必要が失敗した場合SLL(*)のみ最初として解析しようとすることができます。

さらなる説明のためにANTLRのGitHub上のthisチケットを参照し、hereはこの戦略を使用する実装です。

この方法は、構文的に訂正された入力を解析するときに(多くの)時間を節約します。

+0

'SLL(*)'と 'LL(*)'との間には実用的な性能差がありますか? LL(*)は、トップダウン解析を行う際にあいまいさを増すことがありますが、このあいまい性がパフォーマンスにどのような影響を与えますか? –

+0

はい、あります。私は、 'LL(*)'と 'SLL(*)'で約600msを解析するのに約20秒かかったファイルを持っていました。そして、文法的に正しい入力のために 'SLL(*)'が失敗した事例は一度もありません。もしそうであれば、パーサーは 'LL(*)'を元に戻すので、前に行われた 'SLL(*)'の構文解析に使われた時間はその場合に "無駄になる"でしょう。しかし、私が言ったように、私は決して有効な入力のためにそれを経験していません – Raven

0

このようなパフォーマンスは、加算/乗算などの演算子で使用される左回帰によるものです。これらをバイナリルールに書き換える代わりに、即時のパフォーマンスが得られます。 (下記参照)

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 
      ; 
+0

ここで文法を混乱させることができますか?この答えの文法は左回帰を使用しますが、あなたの質問の文法は使用しません。これは、左回帰除去がより良いパフォーマンスにつながると言う回答テキストと矛盾します。 –

+0

完全に私は私の命名法を間違えているので、他の人がそれを理解することができるように例と文法を例として残します。 –

関連する問題