有条件的SkipTo +可选匹配

时间:2019-07-09 18:56:51

标签: python pyparsing

我正在尝试使用pyparsing解析一些.LIB文件。我有一种情况,我有一些字符串结构遵循相似的布局,但是里面有一些变体可以更改所需的语法。

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

感兴趣的主要值是“引脚”名称,时钟类型,方向,如果Timing()存在,则为相关引脚。

到目前为止,这里是解析字符串的内容:

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_timingpin_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()),我得到以下输出:

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()字符串,然后执行递归解析/搜索以提取Timing / related_pin,但是我之前想看看是否有更简单的解决方案尝试那个。

谢谢

1 个答案:

答案 0 :(得分:1)

OptionalSkipTo一起使用时通常需要加倍注意。 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了您的表达式,以便您的分层命名工作正常,大多数情况下,是在您的可选{{1}中添加了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。其余的几乎就是您拥有的方式。