Optional
и SkipTo
обычно требуют небольшой осторожности при совместном использовании.SkipTo
обычно ищет свое целевое выражение без учета того, какие другие выражения идут до или после него в синтаксическом анализаторе.
Вот пример.Используя SkipTo
для разбора этих строк:
a b c z
a d e 100 d z
Начиная с 'a', заканчивая 'z', и некоторыми промежуточными альфа, и, возможно, целым числом.
Мы можем написать этокак:
start = pp.Char('a').setName('start')
end = pp.Char('z').setName('end')
num = pp.Word(pp.nums).setName('num')
И мы будем использовать SkipTo
, потому что, кто знает, что еще там может быть?
expr = (start
+ pp.Optional(pp.SkipTo(num) + num)
+ pp.SkipTo(end)
+ end)
Проведите несколько тестов:
expr.runTests("""
a b c z
a d e 100 d z
a 100 b d z
""")
И все они выглядят довольно неплохо:
a b c z
['a', 'b c ', 'z']
a d e 100 d z
['a', 'd e ', '100', 'd ', 'z']
a 100 b d z
['a', '', '100', 'b d ', 'z']
Но если может быть несколько выражений, SkipTo может пропустить слишком много:
pp.OneOrMore(pp.Group(expr)).runTests("""
a b c z
a d e 100 d z
a 100 b d z
# not what we want
a b c z a d e 100 d z
""")
Дает:
a b c z
[['a', 'b c ', 'z']]
[0]:
['a', 'b c ', 'z']
a d e 100 d z
[['a', 'd e ', '100', 'd ', 'z']]
[0]:
['a', 'd e ', '100', 'd ', 'z']
a 100 b d z
[['a', '', '100', 'b d ', 'z']]
[0]:
['a', '', '100', 'b d ', 'z']
# not what we want
a b c z a d e 100 d z
[['a', 'b c z a d e ', '100', 'd ', 'z']]
[0]:
['a', 'b c z a d e ', '100', 'd ', 'z']
Последняя тестовая строка показывает SkipTo
, пропуская сразу за конец первой группы, пока не достигнет '100' во второй группе, и мы получим только одну большую группу вместо двух.
Мынужно указать SkipTo
, что он не может прочитать после конца группы, ищущей num.Чтобы сделать это, используйте failOn
:
expr = (start
+ pp.Optional(pp.SkipTo(num, failOn=end) + num)
+ pp.SkipTo(end)
+ end)
Мы хотим, чтобы пропуск не прошел, если он достигнет выражения end
, прежде чем найдет num
.Поскольку мы сказали, что это необязательно, это не проблема, и теперь наш тест выглядит следующим образом:
pp.OneOrMore(pp.Group(expr)).runTests("""
# better
a b c z a d e 100 d z
""")
# better
a b c z a d e 100 d z
[['a', 'b c ', 'z'], ['a', 'd e ', '100', 'd ', 'z']]
[0]:
['a', 'b c ', 'z']
[1]:
['a', 'd e ', '100', 'd ', 'z']
Теперь, глядя на ваш пример, вот ваша грамматика.Я внес некоторые изменения, в основном изменив expr.setResultsName("some_name")
на expr("some_name")
и Group
, отредактировав ваши выражения так, чтобы ваше иерархическое именование работало, в основном бот, добавив failOn
в ваш необязательный SkipTo
, чтобы он не пропускал мимоpin_end
выражение:
identifier = Word(alphanums+'_/[]')
pin_dec = Group(Keyword('pin') + LP + identifier('name') + RP)('pin_dec')
pin_clk = Group(Keyword('clock') + ':' + identifier('is_clk') + ';')('pin_clk')
pin_dir = Group(Keyword('direction') + ':' + identifier('dir') + ';')('pin_dir')
pin_arc = Group(Keyword('related_pin')
+ ':'
+ '"' + identifier('name') + '"'
+ ';')('pin_arc')
pin_timing = Group(Keyword('timing')
+ LP + RP
+ LCB
+ SkipTo(pin_arc)
+ Optional(pin_arc))('pin_timing')
pin_end = RCB + Optional(cStyleComment)
pin = Group(pin_dec
+ LCB
+ Optional(pin_clk)
+ Optional(pin_dir)
+ Optional(SkipTo(pin_timing, failOn=pin_end))
+ SkipTo(pin_end)
+ pin_end
for parsed in pin.searchString(sample):
print(parsed.dump())
print()
Дача:
[[['pin', '(', 'core_c_sysclk', ')'], '{', ['clock', ':', 'true', ';'], ['direction', ':', 'input', ';'], 'capacitance : 0.0040;\n max_transition : 0.1000;\n related_ground_pin : "vss" ;\n related_power_pin : "vcc" ;\n fanout_load : 1.0000;\n min_pulse_width_low : 0.1853;\n min_pulse_width_high : 0.1249;', '', '}', '/* End of pin core_c_sysclk */']]
[0]:
[['pin', '(', 'core_c_sysclk', ')'], '{', ['clock', ':', 'true', ';'], ['direction', ':', 'input', ';'], 'capacitance : 0.0040;\n max_transition : 0.1000;\n related_ground_pin : "vss" ;\n related_power_pin : "vcc" ;\n fanout_load : 1.0000;\n min_pulse_width_low : 0.1853;\n min_pulse_width_high : 0.1249;', '', '}', '/* End of pin core_c_sysclk */']
- pin_clk: ['clock', ':', 'true', ';']
- is_clk: 'true'
- pin_dec: ['pin', '(', 'core_c_sysclk', ')']
- name: 'core_c_sysclk'
- pin_dir: ['direction', ':', 'input', ';']
- dir: 'input'
[[['pin', '(', 'core_tx_td[9]', ')'], '{', ['direction', ':', 'output', ';'], 'capacitance : 0.0005;\n max_transition : 0.1000;\n related_ground_pin : "vss" ;\n related_power_pin : "vcc" ;\n max_fanout : 15.0000;\n max_capacitance : 0.1000;\n\n /* Start of rising_edge arc of pin core_tx_td[9] wrt pin core_tx_tclk */\n ', 'timing() { <----WHAT I WANT (to know if this is in the pin)\n timing_type : rising_edge ;\n timing_sense : non_unate ;\n min_delay_arc : "true" ;\n related_pin :" core_tx_tclk "; <----WHAT I WANT (core_tx_tclk in this case)\n rise_transition (lut_timing_4 ){\n values( REMOVED FOR CLARITY\n );\n ', '}']]
[0]:
[['pin', '(', 'core_tx_td[9]', ')'], '{', ['direction', ':', 'output', ';'], 'capacitance : 0.0005;\n max_transition : 0.1000;\n related_ground_pin : "vss" ;\n related_power_pin : "vcc" ;\n max_fanout : 15.0000;\n max_capacitance : 0.1000;\n\n /* Start of rising_edge arc of pin core_tx_td[9] wrt pin core_tx_tclk */\n ', 'timing() { <----WHAT I WANT (to know if this is in the pin)\n timing_type : rising_edge ;\n timing_sense : non_unate ;\n min_delay_arc : "true" ;\n related_pin :" core_tx_tclk "; <----WHAT I WANT (core_tx_tclk in this case)\n rise_transition (lut_timing_4 ){\n values( REMOVED FOR CLARITY\n );\n ', '}']
- pin_dec: ['pin', '(', 'core_tx_td[9]', ')']
- name: 'core_tx_td[9]'
- pin_dir: ['direction', ':', 'output', ';']
- dir: 'output'
Итак, вы действительно были достаточно близки, просто нужно правильно структурировать Optional
и SkipTo
и добавить failOn
и некоторые Group
с.Остальное в значительной степени так, как вы это сделали.