手动解析一个简单的语法

时间:2016-03-07 19:01:39

标签: parsing grammar context-free-grammar text-parsing

我需要实现一个解析以下字符串的简单解析器: 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)在这种情况下更具可读性?

2 个答案:

答案 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语法的扩展名,可以很容易地编写语法。