Как ограничить повторы в грамматике textx?
Я пытаюсь создать грамматику в textx.
Синтаксис должен выглядеть так:
name_a
name_a, name_b
name_a, name_b: name_c, *
name_a, name_b: name_c, *, name_d
*, name_d
*
Где звездочка (*
) означает "все". Я хочу, чтобы это не повторялось. Текущая грамматика такова:
Subsets: ColumnsSet*;
ColumnsSet: SetItem (',' ColumnsSet)*;
SetItem: ColumnName | Star;
Star: '*';
ColumnName: name=ID (':' rename=ID)*;
Позволяет повторять звездочку. Я хочу предотвратить такой случай, чтобы эти строки были недействительными:
name_a, *, *
name_a, *, name_b, *
*, name_a, *
Как мне переписать грамматику?
Есть ли способ сгладить вывод вложенного правила:
ColumnsSet: SetItem (',' ColumnsSet)*;
?
1 ответ
Есть несколько проблем с вашей грамматикой. Сначала в textX вам нужно использовать назначения для сбора соответствующих данных. Использовать
*=
,
+=
стиль присвоения для обозначения множества ноль / один или более. Используйте модификаторы повторения разделителя, чтобы избежать
Something (',' Something)*
шаблон.
Предотвращать
*
происходит многократно, вы можете зарегистрировать объектный процессор, который может проверять семантические ошибки.
Кроме того, чтобы убедиться, что язык ориентирован на строки, вам может потребоваться изучить модификатор правила noskipws.
textX - это не просто парсер, он из грамматики выводит метамодель языка, которую вы можете визуализировать
Вот одно (возможно, неполное) решение, которое может стать хорошим началом.
from textx import metamodel_from_str, TextXSemanticError
from textx.scoping.tools import get_location
grammar = r'''
Subsets: col_sets+=ColumnsSet;
ColumnsSet: set_items+=SetItem[','];
SetItem: ColumnName | Star;
Star: '*';
ColumnName: name=ID (':' rename=ID)?;
'''
def column_set_proc(cs):
if len([x for x in cs.set_items if x == '*']) > 1:
raise TextXSemanticError('Cannot use multiple * in a single line', **get_location(cs))
mm = metamodel_from_str(grammar)
mm.register_obj_processors({'ColumnsSet': column_set_proc})
# This will pass
model = mm.model_from_str(r'''name_a
name_a, name_b
name_a, name_b: name_c, *
name_a, name_b: name_c, *, name_d
*, name_d
*
''')
# Each of these raise TextXSemanticError
count = 0
for invalid in ['name_a, *, *', ' name_a, *, name_b, *', ' *, name_a, *']:
try:
mm.model_from_str(invalid)
except TextXSemanticError:
count += 1
assert count == 3