2016-03-10 36 views
14

Próbuję pisać parsery dla list kontroli dostępu routera jałowca/srx. Poniżej znajduje się gramatyki używam:ANTLR4 Python parsowanie dużych plików

grammar SRXBackend; 

acl: 
    'security' '{' 'policies' '{' COMMENT* replaceStmt '{' policy* '}' '}' '}' 
      applications 
      addressBook 
; 

replaceStmt: 
    'replace:' IDENT 
| 'replace:' 'from-zone' IDENT 'to-zone' IDENT 
; 

policy: 
    'policy' IDENT '{' 'match' '{' fromStmt* '}' 'then' (action | '{' action+ '}') '}' 
; 

fromStmt: 
    'source-address' addrBlock      # sourceAddrStmt 
| 'destination-address' addrBlock    # destinationAddrStmt 
| 'application' (srxName ';' | '[' srxName+ ']') # applicationBlock 
; 

action: 
    'permit' ';' 
| 'deny' ';' 
| 'log { session-close; }' 
; 

addrBlock: 
    '[' srxName+ ']' 
| srxName ';' 
; 

applications: 
    'applications' '{' application* '}' 
| 'applications' '{' 'apply-groups' IDENT ';' '}' 'groups' '{' replaceStmt '{' 'applications' '{' application* '}' '}' '}' 
; 

addressBook: 
    'security' '{' 'address-book' '{' replaceStmt '{' addrEntry* '}' '}' '}' 
| 'groups' '{' replaceStmt '{' 'security' '{' 'address-book' '{' IDENT '{' addrEntry* '}' '}' '}' '}' '}' 'security' '{' 'apply-groups' IDENT ';' '}' 
; 

application: 
    'replace:'? 'application' srxName '{' applicationStmt+ '}' 
; 

applicationStmt: 
    'protocol' srxName ';'   #applicationProtocol 
| 'source-port' portRange ';'  #applicationSrcPort 
| 'destination-port' portRange ';' #applicationDstPort 
; 

portRange: 
    NUMBER    #portRangeOne 
| NUMBER '-' NUMBER #portRangeMinMax 
; 

addrEntry: 
    'address-set' IDENT '{' addrEntryStmt+ '}' #addrEntrySet 
| 'address' srxName cidr ';'     #addrEntrySingle 
; 

addrEntryStmt: 
    ('address-set' | 'address') srxName ';' 
; 

cidr: 
    NUMBER '.' NUMBER '.' NUMBER '.' NUMBER ('/' NUMBER)? 
; 

srxName: 
    NUMBER 
| IDENT 
| cidr 
; 

COMMENT : '/*' .*? '*/' ; 
NUMBER : [0-9]+ ; 
IDENT : [a-zA-Z][a-zA-Z0-9,\-_:\./]* ; 
WS  : [ \t\n]+ -> skip ; 

Kiedy próbuję użyć ACL z ~ 80.000 linii, trwa do września ~ 10 minut, aby wygenerować drzewo przetworzenia. Korzystam z następującego kodu do tworzenia drzewa analizy:

from antlr4 import * 
from SRXBackendLexer import SRXBackendLexer 
from SRXBackendParser import SRXBackendParser 
import sys 


    def main(argv): 
     ipt = FileStream(argv[1]) 
     lexer = SRXBackendLexer(ipt) 
     stream = CommonTokenStream(lexer) 
     parser = SRXBackendParser(stream) 
     parser.acl() 

    if __name__ == '__main__': 
     main(sys.argv) 

Używam Pythona 2.7 jako języka docelowego. Przeprowadziłem również cProfile, aby określić, który kod zajmuje najwięcej czasu. Poniżej jest kilka pierwszych rekordy posortowane na czas:

ncalls tottime percall cumtime percall filename:lineno(function) 
    608448 62.699 0.000 272.359 0.000 LexerATNSimulator.py:152(execATN) 
    5007036 41.253 0.000 71.458 0.000 LexerATNSimulator.py:570(consume) 
    5615722 32.048 0.000 70.416 0.000 DFAState.py:131(__eq__) 
11230968 24.709 0.000 24.709 0.000 InputStream.py:73(LA) 
    5006814 21.881 0.000 31.058 0.000 LexerATNSimulator.py:486(captureSimState) 
    5007274 20.497 0.000 29.349 0.000 ATNConfigSet.py:160(__eq__) 
10191162 18.313 0.000 18.313 0.000 {isinstance} 
10019610 16.588 0.000 16.588 0.000 {ord} 
    5615484 13.331 0.000 13.331 0.000 LexerATNSimulator.py:221(getExistingTargetState) 
    6832160 12.651 0.000 12.651 0.000 InputStream.py:52(index) 
    5007036 10.593 0.000 10.593 0.000 InputStream.py:67(consume) 
    449433 9.442 0.000 319.463 0.001 Lexer.py:125(nextToken) 
     1 8.834 8.834 16.930 16.930 InputStream.py:47(_loadString) 
    608448 8.220 0.000 285.163 0.000 LexerATNSimulator.py:108(match) 
    1510237 6.841 0.000 10.895 0.000 CommonTokenStream.py:84(LT) 
    449432 6.044 0.000 363.766 0.001 Parser.py:344(consume) 
    449433 5.801 0.000 9.933 0.000 Token.py:105(__init__) 

nie mogę sensu z tym wyjątkiem InputStream.LA trwa około pół minuty. Przypuszczam, że dzieje się tak dlatego, że cały ciąg tekstowy jest buforowany/ładowany jednocześnie. Czy istnieje alternatywny/bardziej leniwy sposób analizowania lub ładowania danych dla celu Pythona? Czy mogę poprawić gramatykę, aby przyspieszyć analizę?

Dziękuję

+1

To nie jest odpowiedź, ale czy próbowałeś użyć PyPy czy cokolwiek innego? Po prostu wiedzieć, ile ciężaru spada na Pythona? – Divisadero

+0

Nie użyłem PyPy, ale od wczoraj wykonałem trochę więcej badań. Wydaje się, że klasa strumienia wejściowego ANTLR konwertuje cały wpis tekstowy na znak bufora bajtowego według znaku. Wydaje się, że zajmuje to ponad minutę. Czy jest to szybszy sposób? Jestem pewien, że mogę przesłonić implementację strumienia wejściowego, dopóki znajdę lepszy sposób na zrobienie tego. – prthrokz

+1

@prthrokz, radzę ci wypróbować "starego" Antlr 3. Antlr 4 próbuje parsować prawie każdą gramatykę, ale musi włożyć zbyt dużo wysiłku w czasie wykonywania, aby parsować nawet bardzo proste gramatyki. Antlr 3 jest bardziej restrykcyjny, ale szybki. – kay

Odpowiedz

0

To jest moje zrozumienie, że Twój IDENT może wynosić zero wielkości powodu * zamiast +. Spowoduje to wysłanie parsera do pętli dla każdego znaku, generując zerowe rozmiary węzłów IDENT.