pyparsing nestedExpr 和双结束字符

pyparsing nestedExpr and double closing characters

我正在尝试解析嵌套的列类型定义,例如

1  string
2  struct<col_1:string,col_2:int>
3  row(col_1 string,array(col_2 string),col_3 boolean)
4  array<struct<col_1:string,col_2:int>,col_3:boolean>
5  array<struct<col_1:string,col2:int>>

使用 nestedExpr 在情况 1-4 中按预期工作,但在情况 5 上会抛出解析错误。在双右括号之间添加 space,如“> >”似乎可行,并且可能由此解释引自作者。

By default, nestedExpr will look for space-delimited words of printables https://sourceforge.net/p/pyparsing/bugs/107/

我主要是在寻找预处理和 post 处理输入字符串的替代方法

type_str = type_str.replace(">", "> ")
# parse string here
type_str = type_str.replace("> ", ">")

我试过使用 infix_notation 但我一直无法弄清楚在这种情况下如何使用它。我可能只是用错了方法...

代码片段

array_keyword = pp.Keyword('array')
row_keyword = pp.Keyword('row')
struct_keyword = pp.Keyword('struct')

nest_open = pp.Word('<([')
nest_close = pp.Word('>)]')

col_name = pp.Word(pp.alphanums + '_')
col_type = pp.Forward()
col_type_delimiter = pp.Word(':') | pp.White(' ')
column = col_name('name') + col_type_delimiter + col_type('type')
col_list = pp.delimitedList(pp.Group(column))

struct_type = pp.nestedExpr(
    opener=struct_keyword + nest_open, closer=nest_close, content=col_list | col_type, ignoreExpr=None
)


row_type = pp.locatedExpr(pp.nestedExpr(
    opener=row_keyword + nest_open, closer=nest_close, content=col_list | col_type, ignoreExpr=None
))

array_type = pp.nestedExpr(
    opener=array_keyword + nest_open, closer=nest_close, content=col_type, ignoreExpr=None
)

col_type <<= struct_type('children') | array_type('children') | row_type('children') | scalar_type('type')

nestedExprinfixNotation 不太适合这个项目。 nestedExpr 通常是您不想深入分析的内容的快捷表达式,您只想检测并跨过一些恰好在开始和结束标点符号中嵌套的文本块。 infixNotation 用于解析带有一元和二元运算符的表达式,通常是某种算术。您可能 能够将语法中的标点符号视为运算符,但这是一种延伸,并且肯定是用困难的方式来做事。

对于你的项目,你确实需要定义不同的元素,这将是一个递归语法(因为数组和结构类型本身将根据其他类型定义,也可以是数组或结构).

我尝试了一个 BNF,用于使用标量类型 intfloatbooleanstring 以及复合类型的语法子集arraystruct,只有“<”和“>”嵌套标点符号。数组将采用单个类型参数,以定义数组中元素的类型。一个结构将采用一个或多个结构字段,其中每个字段是一对 identifier:type

scalar_type ::= 'int' | 'float' | 'string' | 'boolean'
array_type ::= 'array' '<' type_defn '>'
struct_type ::= 'struct' '<' struct_element (',' struct_element)... '>'
struct_element ::= identifier ':' type_defn
type_defn ::= scalar_type | array_type | struct_type

(如果您稍后还想添加行定义,请考虑该行应该是什么样子,以及它的元素将如何定义,然后将其添加到此 BNF。)

你看起来对 pyparsing 的基础知识很熟悉,所以我先从一些介绍开始,然后让你填写其余部分。

# define punctuation
LT, GT, COLON = map(pp.Suppress, "<>:")
ARRAY = pp.Keyword('array')
STRUCT = pp.Keyword('struct')

# create a Forward that will be used in other type expressions
type_defn = pp.Forward()

# here is the array type, you can fill in the other types following this model
# and the definitions in the BNF
array_type = pp.Group(ARRAY + LT + type_defn + GT)

...

# then finally define type_defn in terms of the other type expressions
type_defn <<= scalar_type | array_type | struct_type

完成后,进行一些测试:

type_defn.runTests("""\
    string
    struct<col_1:string,col_2:int>
    array<struct<col_1:string,col2:int>>
    """, fullDump=False)

你应该得到类似的东西:

string
['string']

struct<col_1:string,col_2:int>
['struct', [['col_1', 'string'], ['col_2', 'int']]]

array<struct<col_1:string,col2:int>>
['array', ['struct', [['col_1', 'string'], ['col2', 'int']]]]>

一旦你有了它,你就可以尝试将它扩展到其他类型,例如你的行类型,可能是联合,或者采用多种类型的数组(如果这是你发布的示例中的意图)。始终从更新 BNF 开始 - 然后您需要在代码中进行的更改通常会随之而来。