PyParsing Необязательно () висит - PullRequest
4 голосов
/ 06 ноября 2019

При использовании только Optional или ZeroOrMore pyparsing, кажется, входит в бесконечный цикл. Следующий код работает, но часть «# Должна работать с pp.Optional ()» действительно должна быть Optional, а не OneOrMore. Должен ли я поставить какой-то stopOn в этом случае?

Словарь показан ниже:

В котором [expr] означает необязательный expr, а [expr] ... означает необязательный expr, который можетповторить так ZeroOrMore:

[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.Optional (DATABASE_MICRONS)" на "+ pp. OneOrMore (DATABASE_MICRONS) "(тогда файл должен теперь содержать это выражение), тогда он будет работать.

Пример раздела UNITS:

UNITS
 DATABASE MICRONS 1000 ;
END UNITS

Итак, как обращаться с грамматиками, в которых все выражения являются необязательными?

1 Ответ

3 голосов
/ 07 ноября 2019

Если все элементы в PLACEMENT_PINS являются необязательными, то это будет соответствовать пустой строке. Совпадение ZeroOrMore выражения, которое будет соответствовать пустой строке, зациклится навсегда.

Все ли там ZeroOrMore, потому что вы не знаете, какой будет порядок? Если это так, рассмотрите возможность использования оператора «&» вместо «+». a_expr & b_expr & c_expr будет соответствовать трем выражениям, но в любом порядке.

РЕДАКТИРОВАТЬ: Я понимаю, что все они являются необязательными, но потому что вы объединили их вместе в их собственное unit выражение со всем Optional (иможно сопоставить с пустой строкой) и затем OneOrMore их, это еще один бесконечный цикл.

Когда вы говорите «они все необязательны», я понимаю, что все они необязательны с точки зрения определенияUNITS раздел. Но OneOrMore в units уже заботится о повторении. Если пустой раздел UNITS действителен, то используйте ZeroOrMore.

Для меня это выглядит как «единичная фраза», что каждый из них является определителем из нескольких слов в единицах, любой или все из которых могут присутствовать, в любом числе.

Вместо того, чтобы добавлять их все в качестве необязательных, определите их как один MatchFirst - «единичная фраза - это одна из конкретных фраз». Внешний 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')

Если на самом деле все они могут быть необязательными, но должны происходить только один раз, тогда определение Each из Optional s - это то, чтоВы хотите, без повторений:

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')
...