【问题标题】:Do I Have an LL(1) Grammar for This Subset of XML?我有这个 XML 子集的 LL(1) 语法吗?
【发布时间】:2017-02-22 00:58:54
【问题描述】:

我即将使用以下 EBNF 语法为虚构的 XML 子集制作解析器:

DOCUMENT  ::=  ELEMENT
ELEMENT   ::=  START_TAG (ELEMENT | DATA)* END_TAG | EMPTY_TAG
START_TAG ::=  < NAME ATTRIBUTE* >
END_TAG   ::=  </ NAME >
EMPTY_TAG ::=  < NAME ATTRIBUTE* />
ATTRIBUTE ::=  NAME = STRING

以上是“原样”的语法,没有任何变化。 这是我将其转换为 LL(1) 的尝试:

DOCUMENT         ::=    ELEMENT EOF 
ELEMENT          ::=    PREFIX > ELEMENT_OR_DATA END_TAG
                      | PREFIX />
PREFIX           ::=    < NAME OPT_ATTR 
ELEMENT_OR_DATA  ::=      OPT_ELEMENT ELEMENT_OR_DATA 
                        | OPT_DATA ELEMENT_OR_DATA 
                        | epsilon
OPT_ELEMENT      ::=    ELM_LIST | epsilon
ELM_LIST         ::=    ELEMENT  | ELEMENT ELM_LIST
OPT_DATA         ::=    DATA_LIST | epsilon
DATA_LIST        ::=    DATA | DATA DATA_LIST
END_TAG          ::=    </ NAME >
OPT_ATTR         ::=    ATTR_LIST | epsilon
ATTR_LIST        ::=    ATTRIBUTE | ATTRIBUTE ATTR_LIST
ATTRIBUTE        ::=    NAME = STRING 
EOF              ::=         &$

这是原版的 LL(1) 版本吗?如果没有,我哪里做错了?如果是这样,有没有办法在不改变含义的情况下“简化”?我不相信我有最简单的版本。

希望这很清楚。

【问题讨论】:

    标签: xml parsing grammar context-free-grammar


    【解决方案1】:

    LL(1) 解析器不能仅通过查看下一个标记来为 ELEMENT 的两个规则选择正确的规则。 根据语法,解析器应该尝试第一条规则: ELEMENT ::= PREFIX > ELEMENT_OR_DATA END_TAG 如果它不起作用,它必须从递归(回溯)返回以尝试第二条规则: ELEMENT ::= PREFIX />

    问题是两个规则都从同一个“对象”前缀开始。 在这种情况下,它甚至更“糟糕”,因为它不是终端。

    当然,这不是 LL(1) 语法。让我们尝试构建一个。

    我们首先通过删除 TAG 来简化原始语法: DOCUMENT ::= ELEMENT ELEMENT ::= < NAME ATTRIBUTE* > (ELEMENT | DATA)* </ NAME > ELEMENT ::= < NAME ATTRIBUTE* /> ATTRIBUTE ::= NAME = STRING

    下一步是为 ELEMENT 拆分规则以获得第一个标记,这将有助于解析器选择正确的规则。 DOCUMENT ::= ELEMENT ELEMENT ::= < NAME ATTRIBUTE* ELEMENT1 ELEMENT1 ::= > (ELEMENT | DATA)* </ NAME > ELEMENT1 ::= /> ATTRIBUTE ::= NAME = STRING

    现在解析器可以成功开始解析元素。它“推迟”决定是扩展元素还是短元素,并将这个问题委托给 ELEMENT1 规则。后者可以通过检查下一个标记是&gt;还是/&gt;来确定正在解析的元素类型。

    让我们继续转换: DOCUMENT ::= ELEMENT ELEMENT ::= < NAME ATTRIBUTES ELEMENT1 ELEMENT1 ::= > ELEMENT_OR_DATA </ NAME > ELEMENT1 ::= /> ELEM_OR_DATA ::= ELEMENT ELEM_OR_DATA ELEM_OR_DATA ::= DATA ELEM_OR_DATA ELEM_OR_DATA ::= epsilon ATTRIBUTES ::= NAME = STRING ATTRIBUTES ATTRIBUTES ::= epsilon

    我们只是用正确的 LL 语法替换了 *-operator。 最后一个语法仍然存在一些问题:前两个 ELEM_OR_DATA 规则可能会“混淆”解析器,因为它无法猜测应用哪一个(与我们开始讨论的问题类似)。

    让我们通过给解析器一个提示来解决这个问题: DOCUMENT ::= ELEMENT EOF ELEMENT ::= < ELEMENT0 ELEMENT0 ::= NAME ATTRIBUTES ELEMENT1 ELEMENT1 ::= > ELEMENT_OR_DATA </ NAME > ELEMENT1 ::= /> ELEM_OR_DATA ::= < ELEMENT0 ELEM_OR_DATA ELEM_OR_DATA ::= DATA ELEM_OR_DATA ELEM_OR_DATA ::= epsilon ATTRIBUTES ::= NAME = STRING ATTRIBUTES ATTRIBUTES ::= epsilon

    我们在第一个 ELEM_OR_DATA 规则中拆分了 ELEMENT1 并使用了 ELEMENT0。现在假设 DATA 是一个标记,解析器只需查看下一个标记即可轻松确定要应用哪个规则。

    【讨论】:

    • 非常感谢!标记是 NAME、STRING、DATA、、、/> 和 =,所以你的假设是正确的。不过,最后一个问题是:这也是 LR(1) 语法吗?也就是说,我可以使用您在此处为 LL(1) 和 LR(1) 解析提供的相同语法吗?
    • 任何 LL 语法也是 LR。
    • 此外,如果您以明显的方式简单地扩展 Kleene 星,您的原始语法将是 LR(1)。 LR(k) 文法在左递归方面没有问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多