PyParsing Optional() 挂起

PyParsing Optional() hanging

仅使用 Optional 或 ZeroOrMore 时,pyparsing 似乎进入了无限循环。以下代码有效,但“# Should work with pp.Optional()”部分确实应该是可选的,而不是 OneOrMore。在这种情况下我应该设置某种 stopOn 吗?

字典如下:

其中[expr]表示Optional expr,[expr]...表示可以重复ZeroOrMore的optional expr:

[PINS numPins ;
  [ – pinName + NET netName
  [+ SPECIAL]
  [+ DIRECTION {INPUT | OUTPUT | INOUT | FEEDTHRU}]
  [+ NETEXPR "netExprPropName defaultNetName"]
  [+ SUPPLYSENSITIVITY powerPinName]
  [+ GROUNDSENSITIVITY groundPinName]
  [+ USE {SIGNAL | POWER | GROUND | CLOCK | TIEOFF | ANALOG | SCAN | RESET}]
  [+ ANTENNAPINPARTIALMETALAREA value [LAYER layerName]] ...
  [+ ANTENNAPINPARTIALMETALSIDEAREA value [LAYER layerName]] ...
  [+ ANTENNAPINPARTIALCUTAREA value [LAYER layerName]] ...
  [+ ANTENNAPINDIFFAREA value [LAYER layerName]] ...
  [+ ANTENNAMODEL {OXIDE1 | OXIDE2 | OXIDE3 | OXIDE4}] ...
  [+ ANTENNAPINGATEAREA value [LAYER layerName]] ...
  [+ ANTENNAPINMAXAREACAR value LAYER layerName] ...
  [+ ANTENNAPINMAXSIDEAREACAR value LAYER layerName] ...
  [+ ANTENNAPINMAXCUTCAR value LAYER layerName] ...
  [ # The code shows only this section
    [+ PORT]
    [+ LAYER layerName
      [MASK maskNum]
      [SPACING minSpacing | DESIGNRULEWIDTH effectiveWidth] pt pt
    |+ POLYGON layerName
      [MASK maskNum]
      [SPACING minSpacing | DESIGNRULEWIDTH effectiveWidth] pt pt pt ...
    |+ VIA viaName
      [MASK viaMaskNum] pt
    ] ...
    [+ COVER pt orient | FIXED pt orient | PLACED pt orient]  # This must be Optional
    ]...
; ] ...
END PINS]

这是解析器(只显示 PLACEMENT_PINS 部分)。

# PLACEMENT_PINS
    PORT = (ws_pin
            + pp.Keyword('PORT')('PORT')
           )

    MASK = pp.Group(pp.Keyword('MASK')
                    + number('maskNum')
                   ).setResultsName('MASK')

    SPACING = pp.Group(pp.Keyword('SPACING')
                       + number('minSpacing')
                      ).setResultsName('SPACING')

    DESIGNRULEWIDTH = pp.Group(pp.Keyword('DESIGNRULEWIDTH')
                               + number('effectiveWidth')
                              ).setResultsName('DESIGNRULEWIDTH')

    LAYER = pp.Group(ws_pin
                     + pp.Suppress(pp.Keyword('LAYER')) + identifier('layerName')
                     + pp.Optional(MASK)
                     + pp.Optional(SPACING | DESIGNRULEWIDTH)
                     + pp.OneOrMore(pp.Group(pt))('coord')
                    ).setResultsName('LAYER')

    POLYGON =  pp.Group(ws_pin
                        + pp.Suppress(pp.Keyword('POLYGON')) + identifier('layerName')
                        + pp.Optional(MASK)
                        + pp.Optional(SPACING | DESIGNRULEWIDTH)
                        + pp.OneOrMore(pp.Group(pt))('coord')
                       ).setResultsName('POLYGON')

    VIA =  pp.Group(ws_pin
                    + pp.Suppress(pp.Keyword('VIA')) + identifier('viaName')
                    + pp.Optional(MASK)
                    + pp.Group(pt)('coord')
                   ).setResultsName('VIA')

    COVER = pp.Group(ws_pin
                     + pp.Keyword('COVER')
                     + pp.Group(pt)('coord')
                     + ORIENT('orient')
                    ).setResultsName('COVER')
    FIXED = pp.Group(ws_pin
                     + pp.Keyword('FIXED')
                     + pp.Group(pt)('coord')
                     + ORIENT('orient')
                    ).setResultsName('FIXED')
    PLACED = pp.Group(ws_pin
                      + pp.Keyword('PLACED')
                      + pp.Group(pt)('coord')
                      + ORIENT('orient')
                     ).setResultsName('PLACED')

    PLACEMENT_PINS = pp.Group(pp.Optional(PORT)
                              + pp.ZeroOrMore(LAYER | POLYGON | VIA)
                              + pp.OneOrMore(COVER | FIXED | PLACED)  # Should work with pp.Optional(), but it doesn't.
                             )

    pin = pp.Group(pp.Suppress(begin_pin)
                   + pinName
                   + pp.Optional(SPECIAL)
                   + pp.Optional(DIRECTION)
                   + pp.Optional(NETEXPR)
                   + pp.Optional(SUPPLYSENSITIVITY)
                   + pp.Optional(GROUNDSENSITIVITY)
                   + pp.Optional(USE)
                   + pp.ZeroOrMore(ANTENNAPINPARTIALMETALAREA)
                   + pp.ZeroOrMore(ANTENNAPINPARTIALMETALSIDEAREA)
                   + pp.ZeroOrMore(ANTENNAPINPARTIALCUTAREA)
                   + pp.ZeroOrMore(ANTENNAPINDIFFAREA)
                   + pp.ZeroOrMore(ANTENNAMODEL)
                   + pp.ZeroOrMore(ANTENNAPINGATEAREA)
                   + pp.ZeroOrMore(ANTENNAPINMAXAREACAR)
                   + pp.ZeroOrMore(ANTENNAPINMAXSIDEAREACAR)
                   + pp.ZeroOrMore(ANTENNAPINMAXCUTCAR)
                   + pp.ZeroOrMore(PLACEMENT_PINS).setResultsName('PLACEMENT')
                   + pp.Suppress(linebreak)
                  ).setResultsName('pin', listAllMatches=True)

    pins = pp.Group(pp.Suppress(pins_id) + number('numPins') + pp.Suppress(linebreak)
                    + pp.ZeroOrMore(pin)
                    + pp.Suppress(end_pins_id)
                   ).setResultsName('PINS')

下面是要解析的文本示例:

PINS 165 ;
- clk + NET clk + DIRECTION INPUT + USE SIGNAL
  + LAYER M2 ( -25 0 ) ( 25 220 )
  + PLACED ( 0 81500 ) E ;
- rst + NET rst + DIRECTION INPUT + USE SIGNAL
  + LAYER M5 ( -25 0 ) ( 25 220 )
  + PLACED ( 96300 140000 ) S ;
- im_rsc_CSN + NET im_rsc_CSN + DIRECTION OUTPUT + USE SIGNAL
  + LAYER M3 ( -25 0 ) ( 25 220 )
  + PLACED ( 80300 140000 ) S ;
END PINS

在此示例中,如果删除行“+ PLACED”,则解析器将无法工作,因为它是 "pp.OneOrMore(COVER | FIXED | PLACED)" 而不是 "pp.Optional(COVER | FIXED | PLACED)"。

要解析的其他部分是UNITS。所有表达式都是可选的,即文件可以包含"TIME NANOSECONDS 1000"或不包含等

[UNITS
    [TIME NANOSECONDS convertFactor ;]
    [CAPACITANCE PICOFARADS convertFactor ;]
    [RESISTANCE OHMS convertFactor ;]
    [POWER MILLIWATTS convertFactor ;]
    [CURRENT MILLIAMPS convertFactor ;]
    [VOLTAGE VOLTS convertFactor ;]
    [DATABASE MICRONS LEFconvertFactor ;]
    [FREQUENCY MEGAHERTZ convertFactor ;]
END UNITS]

这是挂起的解析器,因为所有表达式都是可选的:

# DATABASE_MICRONS
DATABASE_MICRONS = (pp.Keyword('DATABASE MICRONS')
                    + number('convertFactor')
                    + linebreak
                   )
unit = pp.Group(pp.Optional(TIME_NANOSECONDS)
                        + pp.Optional(CAPACITANCE_PICOFARADS)
                        + pp.Optional(RESISTANCE_OHMS)
                        + pp.Optional(POWER_MILLIWATTS)
                        + pp.Optional(CURRENT_MILLIAMPS)
                        + pp.Optional(VOLTAGE_VOLTS)
                        + pp.Optional(DATABASE_MICRONS)
                        + pp.Optional(FREQUENCY_MEGAHERTZ)
                       ).setResultsName('unit', listAllMatches=True)

units = pp.Group(pp.Suppress(units_id)
                 + pp.OneOrMore(unit)
                 + pp.Suppress(end_units_id)
                ).setResultsName('UNITS')

但是,如果我用“+ pp.OneOrMore(DATABASE_MICRONS)”替换其中一行,例如“+ pp.Optional(DATABASE_MICRONS)”(然后该文件现在必须包含此表达式)然后它将起作用。

UNITS 部分示例:

UNITS
 DATABASE MICRONS 1000 ;
END UNITS

那么,如何处理所有表达式都是可选的文法?

如果PLACEMENT_PINS中的所有元素都是可选的,那么它将匹配空字符串。匹配空字符串的表达式的匹配 ZeroOrMore 将永远循环。

是否所有的 ZeroOrMore 都在那里,因为您不知道顺序是什么?如果是这样,请考虑使用“&”运算符而不是“+”。 a_expr & b_expr & c_expr 将匹配三个表达式,但顺序不限。

编辑: 我知道它们都是可选的,但是因为你已经将它们与所有 Optional 一起放入它们自己的 unit 表达式中(因此可以与空字符串匹配) 然后 OneOrMoreing 他们,这是另一个无限循环。

当您说 "they are all optional" 时,我理解从定义 UNITS 部分的角度来看它们都是可选的。但是 units 中的 OneOrMore 已经 照顾重复。如果空 UNITS 部分有效,则使用 ZeroOrMore.

对我来说,这些看起来像是“单位短语”,每个都是单位上的一些多词限定词,其中任何一个或全部可能以任意数量出现。

与其将它们全部添加为可选,不如将它们定义为单个 MatchFirst - "a unit phrase is one of the specific phrases"。 外部 OneOrMore 将负责重复和可选化:

unit_phrase = pp.Group(TIME_NANOSECONDS
                        | CAPACITANCE_PICOFARADS
                        | RESISTANCE_OHMS
                        | POWER_MILLIWATTS
                        | CURRENT_MILLIAMPS
                        | VOLTAGE_VOLTS
                        | DATABASE_MICRONS
                        | FREQUENCY_MEGAHERTZ)

units = pp.Group(pp.Suppress(units_id)
                 + pp.OneOrMore(unit_phrase)('unit')
                 + pp.Suppress(end_units_id)
                ).setResultsName('UNITS')

如果事实上这些都是可选的但必须只出现一次,那么定义一个 OptionalEach 就是你想要的,没有重复:

unit = pp.Group(pp.Optional(TIME_NANOSECONDS)
                        & pp.Optional(CAPACITANCE_PICOFARADS)
                        & pp.Optional(RESISTANCE_OHMS)
                        & pp.Optional(POWER_MILLIWATTS)
                        & pp.Optional(CURRENT_MILLIAMPS)
                        & pp.Optional(VOLTAGE_VOLTS)
                        & pp.Optional(DATABASE_MICRONS)
                        & pp.Optional(FREQUENCY_MEGAHERTZ)
                       )

units = pp.Group(pp.Suppress(units_id)
                 + unit.setResultsName('unit')  # <-- no OneOrMore repetition now, let Each do the orderless matching
                 + pp.Suppress(end_units_id)
                ).setResultsName('UNITS')