2016-03-10 3 views
14

Я пытаюсь написать синтаксические анализаторы для списков управления доступом Juniper/srx router. Ниже грамматика я использую:ANTLR4 Python синтаксический анализ больших файлов

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 ; 

Когда я пытаюсь использовать ACL с ~ 80000 строк, это занимает ДО ~ 10 минут для создания дерева синтаксического разбора. Я использую следующий код для создания дерева синтаксического анализа:

Я использую Python 2.7 в качестве целевого языка. Я также запускал cProfile, чтобы определить, какой код занимает больше всего времени. Ниже первые несколько записей, отсортированных по времени:

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

Я не могу сделать много смысла из него, кроме InputStream.LA занимает около полуминуты. Я предполагаю, что это связано с тем, что вся текстовая строка загружается/загружается сразу. Есть ли альтернативный/более ленивый способ разбора или загрузки данных для цели Python? Есть ли какие-либо улучшения, которые я могу сделать для грамматики, чтобы быстрее разобрать парсинг?

Спасибо

+1

Это не ответ, но вы пытались использовать PyPy или что-то еще? Просто знать, сколько бремени приходится на питон? – Divisadero

+0

Я еще не использовал PyPy, но сделал еще несколько исследований со вчерашнего дня. Похоже, что класс входного потока ANTLR преобразует весь ввод текста в символ байтового буфера по символу. Кажется, что это займет больше минуты. Есть ли более быстрый способ сделать это? Я уверен, что могу переопределить реализацию потока ввода, пока я могу найти лучший способ сделать это. – prthrokz

+1

@prthrokz, я бы посоветовал вам попробовать «старый» Antlr 3. Antlr 4 пытается разобрать практически каждую грамматику, но для чрезмерного использования во время выполнения нужно разбирать даже очень простые грамматики. Antlr 3 более ограничительный, но быстрый. – kay

ответ

0

Это мое понимание того, что ваш IDENT может быть нулевым размером благодаря * вместо +. Это отправляет ваш парсер в циклы для каждого отдельного символа, генерируя нулевые размеры IDENT узлов.