PyParsing OnlyOnce - PullRequest
       19

PyParsing OnlyOnce

1 голос
/ 21 октября 2019

Я анализирую файл с pyparsing. Это работает отлично, но я думаю, что время обработки можно улучшить, используя класс OnlyOnce вместо OneOrMore в строке "parse_file = pp.OneOrMore (dbuPerMicron | diearea | components) + pp.StringEnd ()". После раздела компонентов файла def есть другие разделы, которые для меня бесполезны, и синтаксический анализатор занимает много времени из-за этих строк. Используя OnlyOnce в файле pase_file, он выдает: «AttributeError: у объекта 'NoneType' нет атрибута 'searchString'".

Я ценю любые предложения.

def parse_def(self):
        ifile = open("path_to.def",'r')
        def_string = ifile.read()
        ifile.close()

        EOL              = pp.LineEnd().suppress()
        linebreak        = pp.Suppress(";" + pp.LineEnd())
        identifier       = pp.Word(pp.alphanums+'_!<>/')
        number           = pp.Word(pp.nums + ".")
        word             = pp.Word(pp.alphas)

        # UNITS DISTANCE MICRONS
        dbuPerMicron_id  = pp.Keyword('UNITS DISTANCE MICRONS')
        dbuPerMicron     = pp.Group(dbuPerMicron_id + number('UnitsPerMicron')).setResultsName('dbuPerMicron')

        # DIEAREA
        diearea_id  = pp.Keyword('DIEAREA')
        diearea     = pp.Group(pp.Suppress(diearea_id) + pp.OneOrMore(pp.Suppress('(') + number + number + pp.Suppress(')')) + pp.Suppress(linebreak)).setResultsName('DIEAREA')

        # COMPONENTS
        components_id    = pp.Keyword('COMPONENTS')
        end_components   = pp.Keyword("END COMPONENTS").suppress()

        begin_comp       = pp.Keyword('-')
        ws_comp          = pp.Keyword('+')  # parameter division in componentes
        comment          = pp.Keyword('#')
        comp_name        = identifier
        compName         = (comp_name('comp_name') + identifier('cell')).setResultsName('compName')
        EEQMASTER        = (pp.Suppress(ws_comp) + identifier('EEQMASTER') + identifier('macroName')).setResultsName('EEQMASTER')

        SOURCE           = (pp.Suppress(ws_comp) + identifier('SOURCE') + identifier('source_type')).setResultsName('SOURCE')

        PLACEMENT_ids    = pp.Keyword('FIXED') | pp.Keyword('COVER') | pp.Keyword('PLACED') | pp.Keyword('UNPLACED')
        PLACEMENT_coord  = pp.Suppress('(') + number('placement_x') + number('placement_y') + pp.Suppress(')')
        PLACEMENT_orient = word('orientation')
        PLACEMENT        = PLACEMENT_ids + pp.ZeroOrMore(PLACEMENT_coord + PLACEMENT_orient)
        PLACEMENT        = (pp.Suppress(ws_comp) + PLACEMENT).setResultsName('PLACEMENT')

        HALO             = (pp.Suppress(ws_comp) + pp.Keyword('HALO') + pp.ZeroOrMore(pp.Keyword('SOFT')) + number('haloL') + number('haloB') + number('haloR') + number('haloT')).setResultsName('HALO')

        ROUTEHALO        = (pp.Suppress(ws_comp) + pp.Keyword('ROUTEHALO') + number('rhaloDist') + identifier('rhaloMinLayer') + identifier('rhaloMaxLayer')).setResultsName('ROUTEHALO')

        WEIGHT           = (pp.Suppress(ws_comp) + pp.Keyword('WEIGHT') + number('weight')).setResultsName('WEIGHT')

        REGION           = (pp.Suppress(ws_comp) + pp.Keyword('REGION') + identifier('region')).setResultsName('REGION')

        PROPERTY         = (pp.Suppress(ws_comp) + pp.Keyword('PROPERTY') + identifier('propName') + identifier('propVal')).setResultsName('PROPERTY')

        subcomponent     = pp.Group(pp.Suppress(begin_comp)
                                  + pp.OneOrMore(compName)
                                  + pp.ZeroOrMore(EEQMASTER)
                                  + pp.ZeroOrMore(SOURCE)
                                  + pp.OneOrMore(PLACEMENT)
                                  + pp.ZeroOrMore(HALO)
                                  + pp.ZeroOrMore(ROUTEHALO)
                                  + pp.ZeroOrMore(WEIGHT)
                                  + pp.ZeroOrMore(REGION)
                                  + pp.ZeroOrMore(PROPERTY)
                                  + pp.Suppress(linebreak)).setResultsName('subcomponents', listAllMatches=True)

        components       = pp.Group(pp.Suppress(components_id) + number('numComps') + pp.Suppress(linebreak)
                                  + pp.OneOrMore(subcomponent )
                                  + pp.Suppress(end_components)).setResultsName('components')


        dbuPerMicron.setParseAction(self.handle_dbuPerMicron)
        diearea.setParseAction(self.handle_diearea)
        components.setParseAction(self.handle_components)

        parse_file       = pp.OneOrMore(dbuPerMicron | diearea | components) + pp.StringEnd()
        # parse_file       = pp.OnlyOnce(dbuPerMicron | diearea | components) + pp.StringEnd()  # It doesn't work

        return parse_file.searchString(def_string)

Пример грамматики файла def:

Grammar:
[UNITS DISTANCE MICRONS dbuPerMicron;]

[DIEAREA ptpt [pt] ... ;]

COMPONENTS numComps ;
        [– compName modelName
        [+ EEQMASTER macroName]
        [+ SOURCE {NETLIST | DIST | USER | TIMING}]
        [+ {FIXED pt orient | COVER pt orient | PLACED pt orient | UNPLACED} ]
        [+ HALO [SOFT] leftbottomrighttop]
        [+ ROUTEHALO haloDistminLayermaxLayer]
        [+ WEIGHT weight]
        [+ REGION regionName]
        [+ PROPERTY {propName propVal} ...]...;] ...
END COMPONENTS

Пример файла определения:

VERSION 5.7 ;
DIVIDERCHAR "/" ;
BUSBITCHARS "[]" ;
DESIGN c1908 ;
UNITS DISTANCE MICRONS 2000 ;

PROPERTYDEFINITIONS
    COMPONENTPIN designRuleWidth REAL ;
    DESIGN FE_CORE_BOX_LL_X REAL 0.000 ;
    DESIGN FE_CORE_BOX_UR_X REAL 23.425 ;
    DESIGN FE_CORE_BOX_LL_Y REAL 0.000 ;
    DESIGN FE_CORE_BOX_UR_Y REAL 19.600 ;
END PROPERTYDEFINITIONS

DIEAREA ( 0 0 ) ( 46850 39200 ) ;

COMPONENTS 248 ;
- U293 NOR2_X1 + PLACED ( 6080 0 ) N
 ;
- U294 FA_X1 + PLACED ( 0 0 ) N
 ;
- U295 NAND2_X1 + PLACED ( 4560 5600 ) N
 ;
- U296 FA_X1 + PLACED ( 20520 2800 ) N
 ;
- U297 NAND2_X1 + PLACED ( 26600 2800 ) N
 ;
- U298 NAND2_X1 + PLACED ( 27740 2800 ) N
 ;
- U299 NAND2_X1 + PLACED ( 22800 8400 ) N
 ;
- U300 NOR2_X1 + PLACED ( 25460 5600 ) N
 ;
- U301 HA_X1 + PLACED ( 33440 5600 ) N
 ;
- U540 INV_X1 + PLACED ( 760 28000 ) N
 ;
END COMPONENTS

PINS 58 ;
- N1 + NET N1 + DIRECTION INPUT + USE SIGNAL
  + LAYER metal3 ( -70 0 ) ( 70 140 )
And more thousands of lines that are useless to me.

1 Ответ

1 голос
/ 25 октября 2019

Если я создаю синтаксический анализатор в своем собственном методе, я пытаюсь просто выполнить определение синтаксического анализатора и вернуть его, а вызывающий абонент отвечает за применение синтаксического анализатора к входной строке. Это упрощает интерфейс вызова для метода parser() и значительно упрощает тестирование в отдельности.

Я изменил ваш метод parse() на parser() и поместил его в фиктивный класс X,но оставил содержимое почти дословно, просто изменив ваш последний оператор синтаксического анализа на:

    return dbuPerMicron | diearea | components

Затем я использовал этот код для запуска синтаксического анализатора с произвольно длинной выборкой (вашей опубликованной выборкой, плюс 10 000 000 случайных символов, включая пробелыи новые строки):

parser = X().parser()

# accumulate results using scanString
results = []
for t, s, e in parser.scanString(sample):
    results.append(t)
    # BUG! (sorry)
    # if len(t) == 3:
    if len(results) == 3:
        break

# use builtin sum() function to merge all the parsed results into one
results = sum(results)

# or here is the same code as the above loop using islice to do the
# range checking for us
from itertools import islice
results = sum(t for t, s, e in islice(parser.scanString(sample), 0, 3))

# what did we get?
print(results.dump())

Создание бита длиной 10 миллионов символов было самой трудоемкой задачей, но анализ был остановлен после анализа 3 соответствующих сегментов. Я записал явное зацикливание с использованием scanString, но с помощью itertools.islice вы можете свернуть его до одной строки.

Вывод из results.dump() выглядит следующим образом (длинные строки списка обрезаны для краткости публикации):

[['UNITS DISTANCE MICRONS', '2000'], ['0', '0', ...
- DIEAREA: ['0', '0', '46850', '39200']
- components: ['248', ['U293', 'NOR2_X1', 'PLACED', '6080', ...
  - numComps: '248'
  - subcomponents: [['U293', 'NOR2_X1', 'PLACED', '6080', ... 
    [0]:
      ['U293', 'NOR2_X1', 'PLACED', '6080', '0', 'N']
      - PLACEMENT: ['PLACED', '6080', '0', 'N']
      - cell: 'NOR2_X1'
      - compName: ['U293', 'NOR2_X1']
      - comp_name: 'U293'
      - orientation: 'N'
      - placement_x: '6080'
      - placement_y: '0'
    [1]:
      ['U294', 'FA_X1', 'PLACED', '0', '0', 'N']
      - PLACEMENT: ['PLACED', '0', '0', 'N']
      - cell: 'FA_X1'
      - compName: ['U294', 'FA_X1']
      - comp_name: 'U294'
      - orientation: 'N'
      - placement_x: '0'
      - placement_y: '0'
    [2]:
      ['U295', 'NAND2_X1', 'PLACED', '4560', '5600', 'N']
      - PLACEMENT: ['PLACED', '4560', '5600', 'N']
      - cell: 'NAND2_X1'
      - compName: ['U295', 'NAND2_X1']
      - comp_name: 'U295'
      - orientation: 'N'
      - placement_x: '4560'
      - placement_y: '5600'
    [3]:
      ['U296', 'FA_X1', 'PLACED', '20520', '2800', 'N']
      - PLACEMENT: ['PLACED', '20520', '2800', 'N']
      - cell: 'FA_X1'
      - compName: ['U296', 'FA_X1']
      - comp_name: 'U296'
      - orientation: 'N'
      - placement_x: '20520'
      - placement_y: '2800'
    [4]:
      ['U297', 'NAND2_X1', 'PLACED', '26600', '2800', 'N']
      - PLACEMENT: ['PLACED', '26600', '2800', 'N']
      - cell: 'NAND2_X1'
      - compName: ['U297', 'NAND2_X1']
      - comp_name: 'U297'
      - orientation: 'N'
      - placement_x: '26600'
      - placement_y: '2800'
    [5]:
      ['U298', 'NAND2_X1', 'PLACED', '27740', '2800', 'N']
      - PLACEMENT: ['PLACED', '27740', '2800', 'N']
      - cell: 'NAND2_X1'
      - compName: ['U298', 'NAND2_X1']
      - comp_name: 'U298'
      - orientation: 'N'
      - placement_x: '27740'
      - placement_y: '2800'
    [6]:
      ['U299', 'NAND2_X1', 'PLACED', '22800', '8400', 'N']
      - PLACEMENT: ['PLACED', '22800', '8400', 'N']
      - cell: 'NAND2_X1'
      - compName: ['U299', 'NAND2_X1']
      - comp_name: 'U299'
      - orientation: 'N'
      - placement_x: '22800'
      - placement_y: '8400'
    [7]:
      ['U300', 'NOR2_X1', 'PLACED', '25460', '5600', 'N']
      - PLACEMENT: ['PLACED', '25460', '5600', 'N']
      - cell: 'NOR2_X1'
      - compName: ['U300', 'NOR2_X1']
      - comp_name: 'U300'
      - orientation: 'N'
      - placement_x: '25460'
      - placement_y: '5600'
    [8]:
      ['U301', 'HA_X1', 'PLACED', '33440', '5600', 'N']
      - PLACEMENT: ['PLACED', '33440', '5600', 'N']
      - cell: 'HA_X1'
      - compName: ['U301', 'HA_X1']
      - comp_name: 'U301'
      - orientation: 'N'
      - placement_x: '33440'
      - placement_y: '5600'
    [9]:
      ['U540', 'INV_X1', 'PLACED', '760', '28000', 'N']
      - PLACEMENT: ['PLACED', '760', '28000', 'N']
      - cell: 'INV_X1'
      - compName: ['U540', 'INV_X1']
      - comp_name: 'U540'
      - orientation: 'N'
      - placement_x: '760'
      - placement_y: '28000'
- dbuPerMicron: ['UNITS DISTANCE MICRONS', '2000']
  - UnitsPerMicron: '2000'

Для элементов, которые, как вы знаете, будут целыми или действительными, вы можете использовать выражения для integer и real (или просто number, которые будут соответствовать всем числовым формам), определенным вpyparsing.pyparsing_common;эти выражения будут использовать быстрый Regex для синтаксического анализа и преобразовывать результат в правильный тип Python во время синтаксического анализа, чтобы вам не пришлось выполнять это преобразование позже.

...