@namorum
Студент IT-шного направления подготовки

Yargy-парсер | Как задать интерпретацию рекурсивного правила вывода для извлечения неизвестного числа терминалов в качестве repeatable-атрибута факта?

С целью более детально донести суть вопроса, в качестве примера для разбора будет использоваться следующий текст.
Условия окружающей среды при выполнении технологической операции
Температура окружающей среды
Влажность окружающей среды
Атмосферное давление


Есть факт Node для построения дерева фактов на основе определённого текста.
Node = fact(
    'Node', [
        'name',
        attribute('successors', None).repeatable()
    ]
)


Рассмотрим правило вывода раздела текста с описанием известного числа свойств.
SECTION -> SECTION_NAME + FEATURE + FEATURE + FEATURE
SECTION = rule(
    SECTION_NAME,
    FEATURE,
    FEATURE,
    FEATURE
)


Интерпретация правила вывода с целью извлечения фактов о свойствах не составит сложности.
SECTION = rule(
    SECTION_NAME.interpretation(Node.name),
    FEATURE.interpretation(Node.feature).repeatable(),
    FEATURE.interpretation(Node.feature).repeatable(),
    FEATURE.interpretation(Node.feature).repeatable()
).interpretation(Node)


Дерево разбора текста будет иметь следующий вид.
Условия окружающей среды при выполнении технологической операции
|----Температура окружающей среды
|----Влажность окружающей среды
|----Атмосферное давление


А теперь рассмотрим то же правило вывода, но с неопределённым количеством свойств.
SECTION -> SECTION_NAME + SECTION | FEATURE + SECTION | FEATURE
SECTION = forward().interpretation(Node)
SECTION.define(
    or_(
        rule(SECTION_NAME, SECTION),
        rule(FEATURE, SECTION),
        rule(FEATURE)
    )
)


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

Интерпретация 1
SECTION = or_(
    rule(
        SECTION_NAME.interpretation(Node.name), 
        SECTION),
    rule(
        FEATURE.interpretation(Node.successors).repeatable(), 
        SECTION),
    rule(
        FEATURE.interpretation(Node.successors).repeatable()
    )
)


Дерево разбора 1
Условия окружающей среды при выполнении технологической операции
|----Атмосферное давление


Интерпретация 2
SECTION = forward().interpretation(Node)
SECTION.define(
    or_(
        rule(
            SECTION_NAME.interpretation(Node.name),
            EOL,
            SECTION.interpretation(Node).interpretation(Node.successors).repeatable()
        ),
        rule(
            FEATURE.interpretation(Node.successors).repeatable(),
            EOL,
            SECTION.interpretation(Node).interpretation(Node.successors).repeatable()
        ),
        rule(
            FEATURE.interpretation(Node.successors).repeatable()
        )
    )
)


Дерево разбора 2
Условия окружающей среды при выполнении технологической операции
|----Температура окружающей среды
      |----Влажность окружающей среды
            |----Атмосферное давление


Вопрос! Как для правила вывода с неопределённым количеством свойств задать интерпретацию так, чтобы дерево разбора получилось такой же структуры, как и дерево разбора по интерпретации для правила вывода с определённым количеством свойств? Возможно ли это вообще?
  • Вопрос задан
  • 235 просмотров
Пригласить эксперта
Ответы на вопрос 2
Vindicar
@Vindicar
RTFM!
Как я подозреваю, проблема в том, что во всех твоих попытках при повторе SECTION повторяется и правило для заголовка секции - тогда как нужно повторять только правила для тела секции.
Т.е. я бы сделал что-то типа такого...
SECTION_CONTENT = forward().interpretation(Node.successors)
SECTION_NAME = ...
FEATURE = ...
SECTION_CONTENT = or_(
    rule(FEATURE, SECTION_CONTENT),
    FEATURE
)
SECTION = rule(SECTION_NAME, SECTION_CONTENT)
Ответ написан
@namorum Автор вопроса
Студент IT-шного направления подготовки
Пока что придумал только такое решение.
Описал интерпретацию правила через комментарии, чтобы много места здесь не занимало.
SECTION = or_(
    rule(SECTION_NAME, FEATURE, FEATURE, FEATURE, FEATURE, FEATURE, FEATURE),
    rule(SECTION_NAME, FEATURE, FEATURE, FEATURE, FEATURE, FEATURE),
    rule(SECTION_NAME, FEATURE, FEATURE, FEATURE, FEATURE),
    rule(SECTION_NAME, FEATURE, FEATURE, FEATURE),
    rule(SECTION_NAME, FEATURE, FEATURE),
    rule(SECTION_NAME, FEATURE)
)

# На SECTION используется interpretation(Node)
# SECTION_NAME - interpretation(Node.name)
# FEATURE -  interpretation(Node.successors).repeatable()


В моём случае это более-менее работающее решение, поскольку у меня в одной секции почти не встречается больше шести свойств. Однако оно, конечно, не подойдёт для более неопределённых случаев.

P. S. Ближе к концу написания этого комментария вспомнил, что в cookbook'е yargy-парсера встречал упоминание генераторов правил вывода. Постараюсь разобраться с этими генераторами, и в редакции этого ответа приведу вариант с генератором.

UPD (25 апр.)

Написал функцию, с помощью которой правила, подобные указанному выше, описываются в несколько строк.

'''
Генерирует интерпретированную правую часть правил конечной рекурсии следующего вида.
left -> right + sep + left | right

right – повторяющееся в правой части правило.
right_interpretation – интерпретация для right.
sep – правило-разделитель последовательности из правил вида right.
max_recursion_depth – глубина рекурсии.
'''
def get_recursive_interpreted_right_part(right, right_interpretation=None, sep=None, max_recursion_depth=10):
    if right_interpretation is not None:
        right = rule(right.interpretation(right_interpretation).repeatable())
    
    list_of_right_rules = []
    for cur_len in reversed(range(1, max_recursion_depth+1)):
        if sep is None:
            right_rule_args = [right] * cur_len
        else:
            right_rule_args = [right, sep] * (cur_len - 1)
            right_rule_args.append(right)
        list_of_right_rules.append(rule(*right_rule_args))
    
    return or_(*list_of_right_rules)


К примеру, вместо того, чтобы писать это:
FEATURE_BLOCK = or_(
    rule(
         FEATURE.interpretation(Node.successors).repeatable()
    ),
    rule(
         FEATURE.interpretation(Node.successors).repeatable(), EOL, FEATURE.interpretation(Node.successors).repeatable()
    ),
    rule(
         FEATURE.interpretation(Node.successors).repeatable(), EOL, FEATURE.interpretation(Node.successors).repeatable(), EOL, FEATURE.interpretation(Node.successors).repeatable()
    )
)

Можно написать это:
FEATURE_BLOCK = get_recursive_interpreted_right_part(FEATURE, Node.successors, EOL, 3)


Это гораздо более удобный вариант, хотя всё ещё рассчитанный на известное максимальное количество повторений правой части. Для моей задачи этого более-менее достаточно.

Но я по-прежнему надеюсь, что просто пропустил какую-либо из возможностей yargy-парсера, с помощью которой можно провернуть такое из коробки. Буду рад увидеть такой вариант в ответах.
Также буду рад, если кто-то посчитает, что описанную мною функцию можно описать более лаконично без потери читаемости, и предложит более качественное её описание.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы