PyParsing строки разной длины

Я пишу парсер для файла конфигурации брандмауэра. Я новичок в PyParsing и Python в целом.

Вопрос в том, как мне разобрать, если встречается более 3 аргументов, (xxxx, xxxx, xxxx) != (xxxx, xxxx, xxxx, xxxx), все правила работают нормально и анализируют все правильно, если каждая строка содержит не более 3 строки, но мы видим, что [брандмауэр [F1]] содержит «NAT» после поля адреса и игнорируется независимо от того, как мы меняем правило.

Использование (def printTokens(s,loc,toks): #s=исходная строка, loc=местоположение, toks=совпадающие токены)

Пожалуйста, посмотрите 2 выхода при использовании 4-го аргумента ("NAT") и когда мы его стираем. Заранее спасибо! Необходимо разобрать все, включая "NAT" с реализованными правилами.

from pyparsing import *

#===================================GRAMMER==================================
zone = Literal("zone")    
zoneid = Word(alphanums)
host = Literal("host")
hostid = Word(alphanums)
interface = Literal("interface")
interfaceid = Word(alphanums)
firewall = Literal("firewall")
firewallid = Word(alphanums)
router = Literal("router")
routerid = Word(alphanums)

fstop = Literal(".")
comma = Suppress(",") #Converter for ignoring the results of a parsed expression.
slash = Literal("/")
ocbracket = Literal("{")
ccbracket = Literal("}")
sobracket = Literal("[")
scbracket = Literal("]")
hyphen = Literal("-")
underline = Literal("_") 
word = Word(alphas)


#===================================IP-TYPE=================================

ip=Combine(Word(nums)+            
        fstop+ Word(nums) + 
        fstop+ Word(nums) + 
        fstop + Word(nums))

subnet = Combine(slash +Word(nums))

address = ip + Optional(subnet)


#===================================RULES===================================

#adword = address + word

zoneRule = zone + zoneid + address
hostRule = host + hostid + ocbracket
interfaceRule = interface + interfaceid + address 
interfaceRule2 = interface + interfaceid + address + word
firewallRule = firewall + firewallid + ocbracket
routerRule = router + routerid + ocbracket

endRule = ccbracket


rule = zoneRule | hostRule | interfaceRule | interfaceRule2 | firewallRule | routerRule | endRule 
rules = OneOrMore(rule)

#===================================DATA=====================================
details = """zone zone1 10.1.0.0/24                   
         zone backbone 10.254.0.0/24
         zone zone 10.2.0.0/24
         host ha {
             interface iha 10.1.0.1
         }
         host hb {
            interface ihb 10.2.0.1
         }
         firewall f1 {
            interface ifla 10.1.0.254 
            interface iflback 10.254.0.101 nat
         }
         router r2 {
            interface ir2back 10.254.0.102
         }
         router r3 {
            interface ir3b 10.2.0.103
         }"""

#==================================METHODS==================================

    def printTokens(s,loc,toks):   #s=orig string, loc=location, toks=matched tokens
    print (toks)

zoneRule.setParseAction(printTokens) 
hostRule.setParseAction(printTokens)
interfaceRule.setParseAction(printTokens)
interfaceRule2.setParseAction(printTokens) #takes in 4 instances where as 3 declared
firewallRule.setParseAction(printTokens)
routerRule.setParseAction(printTokens)
endRule.setParseAction(printTokens)

rules.parseString(details)


#================================OUTPUT RESULT WITH NAT=================================
"""
['zone', 'zone1', '10.1.0.0', '/24']
['zone', 'backbone', '10.254.0.0', '/24']
['zone', 'zone', '10.2.0.0', '/24']
['host', 'ha', '{']
['interface', 'iha', '10.1.0.1']        
['}']
['host', 'hb', '{']
['interface', 'ihb', '10.2.0.1']
['}']
['firewall', 'f1', '{']
['interface', 'ifla', '10.1.0.254']
['interface', 'iflback', '10.254.0.101']"""
#================================OUTPUT RESULT WITHOUT NAT=================================
"""['zone', 'zone1', '10.1.0.0', '/24']
['zone', 'backbone', '10.254.0.0', '/24']
['zone', 'zone', '10.2.0.0', '/24']
['host', 'ha', '{']
['interface', 'iha', '10.1.0.1']
['}']
['host', 'hb', '{']
['interface', 'ihb', '10.2.0.1']
['}']
['firewall', 'f1', '{']
['interface', 'ifla', '10.1.0.254']
['interface', 'iflback', '10.254.0.101']
['}']
['router', 'r2', '{']
['interface', 'ir2back', '10.254.0.102']
['}']
['router', 'r3', '{']
['interface', 'ir3b', '10.2.0.103']
['}']"""

person vVv    schedule 20.07.2016    source источник
comment
Попробуйте изменить порядок rule = zoneRule | hostRule | interfaceRule | interfaceRule2 | firewallRule | routerRule | endRule на rule = zoneRule | hostRule | interfaceRule2 | interfaceRule | firewallRule | routerRule | endRule. Кроме того, попробуйте сделать interface2 более конкретным, если это возможно, например, interfaceRule2 = interface + interfaceid + address + CaselessLiteral('nat') или interfaceRule2 = interface + interfaceid + address + oneOf("nat ext ipv6 other1 other2"). Также обратите внимание на версию 2.1.5, включая определения pyparsing_common для адресов IPv4 и IPv6.   -  person PaulMcG    schedule 20.07.2016


Ответы (1)


Если вы хотите сопоставить любое количество выражений с определенным разделителем, используйте PyParsing. список с разделителями. По умолчанию разрешены пробелы вокруг разделителей; добавьте combine=True, чтобы не требовать пробелов.

Однако, если вы хотите разрешить необязательные элементы в своей грамматике, вам следует просто добавить необязательный элемент. Для ваших правил интерфейса вы можете заменить:

interfaceRule = interface + interfaceid + address 
interfaceRule2 = interface + interfaceid + address + word

С участием:

interfaceRule = interface + interfaceid + address + Optional(word)

Наконец, фактическая проблема с кодом, который вы разместили, заключается в том, что вы используете оператор |, который является сокращенной формой для MatchFirst. MatchFirst попробует заданные параметры по порядку и вернет результат первого, который соответствует. Если вместо этого вы используете Или, для которого короткий -hand является оператором ^, тогда он вместо этого попытается использовать все варианты и вернет вариант с самым длинным совпадением.

person taleinat    schedule 20.07.2016
comment
Optional — хорошее предложение для этой проблемы, но может потребоваться более конкретное значение, чем просто word, поскольку оно соответствует любой группе слов альфа, которая может включать начальное interface следующего правила брандмауэра. - person PaulMcG; 20.07.2016
comment
Большое спасибо за количество решений, перепробовал их все, и правила переупорядочения делают свое дело, фактически использование опционального (слова) в соответствии с приведенным выше комментарием заменяется текстовым интерфейсом, поэтому использование ClasslessLiteral ('nat') является одним из способов решение. Единственная проблема будет заключаться в том, что если я когда-нибудь столкнусь с более чем «натуральным», мне придется вручную добавлять эти литералы. Но ответ на вопрос есть. Большое Вам спасибо . Оценил вашу помощь! - person vVv; 20.07.2016