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ę
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
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
@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