Разбор xdot рисовать атрибуты с pyparsing - PullRequest
2 голосов
/ 28 марта 2012

Новое в PyParsing.Я пытаюсь разобраться, как разобрать атрибуты draw (и аналогичные) в файлах xdot.Есть ряд элементов, в которых число следующих элементов в начале задается как целое число - что-то вроде NetStrings.Я рассмотрел некоторые примеры кода для работы с конструкциями, подобными netstring, но, похоже, он не работает для меня.

Вот несколько примеров:

Полигон с 3 точками (цифра 3 после буквы P обозначает количество следующих пунктов):P 3 811 190 815 180 806 185 следует проанализировать до 'P', [[811, 190], [815, 180], [806, 185]]

Полигон с 2 точками:P 2 811 190 815 180 806 185 должен анализироваться на 'P', [[811, 190], [815, 180]] (с непарсированным текстом в конце)

Цвет заливки пера (4 после C указывает количество символов после '-' для использования):C 4 -blue должен проанализировать 'C', 'blue'


Обновленная информация: Я думаю, что вводил в заблуждение, помещая примеры в свои строки, без дополнительного контекста.Вот реальный пример:

S 5 -solid S 15 -setlinewidth(1) c 5 -black C 5 -black P 3 690 181 680 179 687 187

См. http://www.graphviz.org/doc/info/output.html#d:xdot для актуальной спецификации.

Обратите внимание, что в текстовых полях могут быть значительные пробелы - setlinewidth (1) вышеможет быть "abcd efgh hijk" и, если оно ровно 15 символов, оно должно быть связано с тегом 'S'.После тега 'P' должно быть ровно 7 цифр (начальный счетчик + 3 пары), и все остальное должно вызывать ошибку разбора, поскольку после тега может быть больше тегов (в одной строке), но сами по себе цифры не являютсядействительный.

Надеюсь, это немного прояснит ситуацию.

Ответы [ 2 ]

1 голос
/ 28 марта 2012

В ответ на редактирование ОП ответ ниже не является полным.

Я собираюсь попытаться понять суть вашего вопроса здесь и проигнорировать более тонкие детали.Надеюсь, это поможет вам правильно выбрать грамматику.По сути, вы спрашиваете, учитывая две строки:

P 3 811 190 815 180 806 185
P 2 811 190 815 180 806 185

как вы можете анализировать данные так, что во второй строке читаются только две точки?Лично я бы прочитал все данных и после разбора.Вы можете значительно облегчить себе работу, если вы назовете результаты .Например:

from pyparsing import *

EOL = LineEnd().suppress()

number = Word(nums).setParseAction(lambda x: int(x[0]))
point_pair = Group(number + number)

poly_flag  = Group(Literal("P") + number("length"))("flag")
poly_type  = poly_flag + Group(OneOrMore(point_pair))("data")

xdot_line = Group(poly_type) + EOL
grammar   = OneOrMore(xdot_line)

Обратите внимание, что у нас есть имена data, flag и length, это пригодится позже.Давайте разберем и обработаем строку:

S = "P 3 811 190 815 180 806 185\nP 2 811 190 815 180 806 185\n"
P = grammar.parseString(S)

for line in P:
    L = line["flag"]["length"]  
    while len(line["data"]) > L: 
        line["data"].pop()

Даем полезный и структурированный результат:

[['P', 3], [[811, 190], [815, 180], [806, 185]]]
[['P', 2], [[811, 190], [815, 180]]]

Расширяем грамматику

Отсюда вы можете самостоятельно строить кускиграмматики один за другим.Каждый раз, когда вы добавляете новый тип, добавляйте его к xdot_line, то есть

xdot_line = Group(poly_type | pen_fill_type) + EOL
1 голос
/ 28 марта 2012

Ну, это то, что я придумал в конце, используя scanString.

int_ = Word(nums).setParseAction(lambda t: int(t[0]))
float_ = Combine(Word(nums) + Optional('.' + ZeroOrMore(Word(nums, exact=1)))).setParseAction(lambda t: float(t[0]))
point = Group(int_ * 2 ).setParseAction(lambda t: tuple(t[0]))
ellipse = ((Literal('E') ^ 'e') + point + int_ + int_).setResultsName('ellipse')
n_points_start =  (Word('PpLBb', exact=1) + int_).setResultsName('n_points')
text_start = ((('T' + point + int_*3 ) ^ ('F' + float_ + int_) ^ (Word('CcS') + int_) ) + '-').setResultsName('text')
xdot_attr_parser = ellipse ^ n_points_start ^ text_start

def parse_xdot_extended_attributes(data):
    results = []
    while True:
        try:
            tokens, start, end = xdot_attr_parser.scanString(data, maxMatches = 1).next()
            data = data[end:]
            name = tokens.getName()
            if name == 'n_points':
                number_to_get = int(tokens[-1])
                points, start, end = (point * number_to_get).scanString(data, maxMatches = 1).next()
                result = tokens[:1]
                result.append(points[:])
                results.append(result)
                data = data[end:]
            elif name == 'text':
                number_to_get = int(tokens[-2])
                text, data = data[:number_to_get], data[number_to_get:]
                result = tokens[:-2]
                result.append(text)
                results.append(result)
            else:
                results.append(tokens)
        except StopIteration:
            break
    return results
...