Условный SkipTo + Дополнительный матч - PullRequest
1 голос
/ 09 июля 2019

Я пытаюсь проанализировать некоторые файлы .LIB, используя pyparsing.У меня есть сценарий, в котором у меня есть некоторые строковые структуры, которые следуют аналогичной схеме, но есть варианты внутри, которые могут изменить требуемую грамматику.

TL; DR проблемы: мне нужно иметь возможность обойти частиСтрока до следующего токена, который может отсутствовать.

Ниже приведен фрагмент файла LIB.

PIN EXAMPLE WITH NO TIMING
pin (core_c_sysclk ) { 
  clock : true ; 
  direction : input ;
  capacitance :  0.0040;
  max_transition :  0.1000;
  related_ground_pin :   "vss" ;
  related_power_pin :   "vcc" ;
  fanout_load :  1.0000;
  min_pulse_width_low :  0.1853;
  min_pulse_width_high :  0.1249;

} /* End of pin core_c_sysclk */

bus (core_tx_td ){
  bus_type :  bus2 ;

  /* Start of pin core_tx_td[9] */ 
  PIN EXAMPLE WITH  TIMING
  pin (core_tx_td[9] ) { 
    direction : output ;
    capacitance :  0.0005;
    max_transition :  0.1000;
    related_ground_pin :   "vss" ;
    related_power_pin :   "vcc" ;
    max_fanout :  15.0000;
    max_capacitance :  0.1000;

    /* Start of rising_edge arc of pin core_tx_td[9] wrt pin core_tx_tclk */
    timing() {                        <----WHAT I WANT (to know if this is in the pin)
      timing_type : rising_edge ;
      timing_sense : non_unate ;
      min_delay_arc :   "true" ;
      related_pin :" core_tx_tclk ";  <----WHAT I WANT (core_tx_tclk in this case)
    rise_transition (lut_timing_4 ){
       values(\
        REMOVED FOR CLARITY
        );
      }
    fall_transition (lut_timing_4 ){
       values(\
        REMOVED FOR CLARITY
        );
      }
    cell_rise (lut_timing_4 ){
       values(\
        REMOVED FOR CLARITY
        );
      }
    cell_fall (lut_timing_4 ){
       values(\
        REMOVED FOR CLARITY
        );
      }
    } /* End of rising_edge arc of pin core_tx_td[9] wrt pin core_tx_tclk */
    .....More but not really needed for example

Основными интересующими вас значениями являются имя 'pin', тип часов, направление и, если время () существует,связанный вывод.

Итак, вот что у меня есть для анализа строк:

LP          = '('
RP          = ')'
LCB         = '{'
RCB         = '}'
COM         = ','


#Pins/Signals
pin_dec       = (Keyword('pin') + LP + Word(alphanums+'_/[]').setResultsName('name') + RP).setResultsName('pin_dec')
pin_clk       = (Keyword('clock') + ':' + Word(alphanums+'_/').setResultsName('is_clk') + ';').setResultsName('pin_clk')
pin_dir       = (Keyword('direction') + ':' + Word(alphanums+'_/').setResultsName('dir') + ';').setResultsName('pin_dir')
pin_arc       = (Keyword('related_pin') + ':' + '"' + Word(alphanums+'_/[]').setResultsName('name') + '"' + ';').setResultsName('pin_arc')
pin_timing    = (Keyword('timing') + LP + RP + LCB + SkipTo(pin_arc) + Optional(pin_arc)).setResultsName('pin_timing')
pin_end       =  Keyword('} /* End of pin') + SkipTo('*/')
pin           = pin_dec + LCB + Optional(pin_clk) + Optional(pin_dir) + SkipTo(Optional(pin_timing))  + SkipTo(pin_end) + pin_end

Вывод (), проверка часов и проверка направления просты и, кажется, работают.Моя проблема связана с проверкой pin_timing и pin_arc.В некоторых случаях, как видно из кода, вы можете иметь дополнительные строки информации, которые не нужны.Я пытался использовать SkipTo (pin_timing), однако возможно, что элемент pin_timing не мог быть там, поэтому я хотел бы пропустить его, если это возможно.

Я пытался сделать Optional(SkipTo(pin_timing)) и SkipTo(Optional(pin_timing))Но ни один из них, кажется, не дает мне надлежащих результатов.Вот фрагмент кода для проверки строки примера:

for bla in pin.searchString(test_str):
  print('========')
  print('Pin name: ' + bla.pin_dec.name)
  if bla.pin_dir:
    print('Pin Dir: ' + bla.pin_dir.dir)
  if bla.pin_clk:
    print('Pin Clk: ' + bla.pin_clk.is_clk)
  #if bla.pin_timing: just trying to print for debug
  print('Pin Timing: ' + bla.pin_timing)

Вывод следующий:

========
Pin name: core_c_sysclk
Pin Dir: input
Pin Clk: true
Pin Timing: 
========
Pin name: core_tx_pwr_st[2]
Pin Dir: output
Pin Timing: 
========
Pin name: core_tx_pwr_st[1]
Pin Dir: output
Pin Timing: 
========
Pin name: core_tx_pwr_st[0]
Pin Dir: output
Pin Timing: 
========
Pin name: core_tx_td[9]
Pin Dir: output
Pin Timing: 

Настройка отладки на pin_timing (с использованием pin_timing.setDebug()), Iполучить следующий вывод:

Match {"timing" "(" ")" "{" SkipTo:({"related_pin" ":" """ W:(abcd...) """ ";"}) [{"related_pin" ":" """ W:(abcd...) """ ";"}]} at loc 596(22,7)
Exception raised:Expected "timing" (at char 596), (line:22, col:7)

Исходя из этого, он вызывает исключение в строке max_transition.Я не смог понять, почему он это делает.Также интересно, почему он не дает того же исключения в строке capacitance.Я предполагаю, что я либо неправильно использую Optional + SkipTo, поэтому, если есть какой-либо пример, который можно использовать для перехода к дополнительному токену и обхода, если он недоступен, было бы неплохо это увидеть.Я просмотрел документы PyParsing и несколько SO-тем, однако большинство из них, похоже, не ответили на этот конкретный вопрос.

Я подумал, нужно ли мне получить всю строку pin() из файла изатем выполните рекурсивный анализ / поиск для извлечения хронирования / related_pin, однако я собирался выяснить, есть ли более простое решение, прежде чем пытаться это сделать.

Спасибо

1 Ответ

1 голос
/ 13 июля 2019

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 с.Остальное в значительной степени так, как вы это сделали.

...