2016-03-10 39 views
14

Ardıç/srx yönlendirici erişim denetim listeleri için ayrıştırıcılar yazmaya çalışıyorum.ANTLR4 Python büyük dosyaları ayrıştırma

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 ; 

Ben ~ 80,000 hatları ile bir ACL kullanmayı deneyin, bu ayrıştırma ağacı oluşturmak için ~ 10 dakika kadar sürer: Aşağıda kullanıyorum dilbilgisi olduğunu. Ayrıştırmak için aşağıdaki kodu kullanıyorum:

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) 

Hedef dil olarak Python 2.7 kullanıyorum. Ayrıca, en çok hangi kodun alındığını tanımlamak için cProfile'ı çalıştırdım. Aşağıda zamanında olanlar önce birkaç kayıtları geçerli: InputStream.LA etrafında yarım dakika sürer dışında

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__) 

Gerçekten bunun dışında pek bir anlamı olamaz. Sanırım bu, tüm metin dizesinin bir kerede arabelleğe/yüklenmesine bağlı olduğu için. Python hedefi için verilerin ayrıştırılması veya yüklenmesi için alternatif/daha tembel bir yol var mı? Dilimlemeyi daha hızlı yapabilmek için dilbilgisinde yapabileceğim bir gelişme var mı?

Benim anlayış sizi

+1

Bu bir cevap değil, PyPy'yi veya başka bir şeyi kullanmayı denediniz mi? Sadece bir yükün pitona ne kadar düştüğünü bilmek? – Divisadero

+0

PyPy'yi kullanmadım ama dünden bu yana biraz daha araştırma yaptım. ANTLR'nin giriş akışı sınıfının, tüm metin girişini bir byte buffer karakterine karakter olarak dönüştürdüğü görülür. Bu bir dakikadan fazla sürüyor. Bunu yapmanın daha hızlı bir yolu var mı? Bunu yapmanın daha iyi bir yolunu bulabildiğim sürece giriş akışının uygulanmasını geçersiz kılabileceğime eminim. – prthrokz

+1

@prthrokz, "eski" Antlr 3'ü denemenizi tavsiye ederim. Antlr 4 hemen hemen her grameri ayrıştırmaya çalışır ancak çok basit gramerleri ayrıştırmak için çok fazla çalışma zamanı çabası gerektirir. Antlr 3 daha kısıtlayıcı fakat hızlıdır. – kay

cevap

0

ederiz ki onun sorumlusu da IDENT sıfır boyutlu nedeniyle * yerine + olabilir. Bu, her bir karakter için ayrıştırıcınızı döngülere gönderir ve IDENT düğümlerini sıfır boyutunda üretir.