我需要实现一个解析以下字符串的简单解析器: a [b [c,d],e],f [g,h [i],j [k,l]],......
在上面的示例中,有一个包含一个或多个OBJECTS的GROUP。 每个对象都有嵌套的OBJECTS(b中的一个AND h,j中的f)加上嵌套的VALUES(c,d,e,g,i,k,l)。
所以,它就像是:
GROUP : OBJECT (,OBJECT)*
OBJECT: NAME '[' OBJECTorVALUE (OBJECTorVALUE)* ']'
OBJECTorVALUE: OBJECT | VALUE
VALUE : NAME
手动解析这种语法的最简单方法是什么?
我尝试用递归下降解析器解析它并且它可行,但看起来很奇怪,因为你必须为OBJECTORVALUE解析生产:
OBJECTorVALUE: NAME '[' blabla ']'
OBJECTorVALUE: NAME
所以,为了使它成为LL语法(由rec-descent解析)我们应该将其解析为
OBJECTorVALUE: NAME Z
Z: eps | '[' blabla ']'
现在rec-desc解析器获得了一些不必要的复杂性并且感觉不自然,所以我必须引入一个rec-desc方法来在名称后面查找Z.所以,而不是简单易用的方法
parseGroup -> parseObjects
parseObj -> parseName consume[ parseObjectValueGroup consume ]
parseObjectValueGroup -> parseObjectValues
parseObjectValue -> look('[') -> parseObj OR parseValue
我得到相同的但最后一个陈述变为
parseObjectValue -> parseName parseZ
parseZ -> look('[') -> parseObjWithoutName OR return empty
parseObjWithoutName -> ...
对我来说这看起来很乱,所以我们可以在这里使用更简单的东西吗?我的意思是这个简单的语法,甚至可以通过字符串拆分进行解析,看起来很简单。也许简单LR解析器(SLR)在这种情况下更具可读性?
答案 0 :(得分:1)
我实际上认为尝试解析这种自上而下的方法不会太糟糕。如果您尝试将其写为正式语法,看起来会比实际情况差很多。例如,这是伪代码中的样子:
ParseGroup():
result = empty list
while true:
read token 'next'; it's some identifier.
if (there's no next token) {
append 'next' to result.
break;
}
else if (the next token is '[') {
let inner = ParseGroup();
read token; it's a ']';
append 'next[inner]' to result.
} else {
append 'next' to result.
}
read token; it's a comma.
append ',' to result.
}
}
这基本上是针对修改后的语法而稍作修改的LL(1)解析器。
答案 1 :(得分:1)
这确实是一个非常简单的语法。您的语法不完整,因为未定义NAME
。用ABNF(RFC 5234)语法定义的语法是这样的(假设使用NAME规则):
group = element *("," WS element)
element = name WS ["[" WS group "]" WS]
name = 1*(%x61-7A / %x41-5A)
WS = *(%x20 / %x9 / %xA / %xD)
您可以将其读取为:一组由用逗号分隔的元素组成。每个元素都有一个名称,在它后面可能会有一个在子括号中的子元素组。 “名称”是一个或多个英文字母字符。在语法的每个部分之间,都允许有空格(零个或多个):空格(20十六进制),制表符(9十六进制),换行符(A十六进制)或回车符(D十六进制)。如果您不希望使用空格,则只需完全删除WS规则即可。
这是语法,但是有语义:如果element
仅具有名称,则其value
否则具有object
。这是一个简单的歧义消除,您应该在解析完成后执行此操作。
如果语法是如此简单,并且您不想自己编写代码,我已经制作了一个工具(Tunnel Grammar Studio),可以用C ++代码为您生成此语法。语法很简单,因此该工具的演示应足以满足您的需求。您也可以在线运行语法。 TGS生成迭代解析器,这意味着,如果您的组嵌套太多,则不必担心堆栈溢出问题。
ps。除了十六进制值之外,您还可以编写1*('a'-'z' / 'A'-'Z')
,因为TGS具有ABNF语法的扩展名,可以很容易地编写语法。