如何限制textx语法中的重复?

问题描述

我正在尝试在textx中创建语法。

语法应如下所示:

name_a
name_a,name_b
name_a,name_b: name_c,*
name_a,*,name_d
*,name_d
*

星号(*)表示“全部”。我不想重复。当前的语法是这样的:

Subsets: ColumnsSet*;
ColumnsSet: SetItem (',' ColumnsSet)*;
SetItem: ColumnName | Star;
Star: '*';
ColumnName: name=ID (':' rename=ID)*;

它允许重复星号。我想防止这种情况,以使这些行无效:

name_a,name_b,*
*,name_a,*

我应该如何重写语法?

有没有一种方法可以使嵌套规则的输出变平:ColumnsSet: SetItem (',' ColumnsSet)*;

解决方法

您的语法有几个问题。首先,在textX中,您需要使用assignments来收集相关数据。使用*=+=分配样式表示许多零/一个或多个。使用separator repetition modifiers来避免Something (',' Something)*的重复。

为防止*多次发生,您可以注册object processor来检查语义错误。

此外,要确保语言是面向行的,您可能需要研究noskipws规则修饰符。

textX不仅仅是语法分析器,它还从语法中推断出you can visualize

的语言元模型。

这是一个(可能是不完整的)解决方案,可能是一个很好的开始。

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_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