我正在尝试自学序言,并为简单的算术cfg实现解释器:

<expression> --> number
<expression> --> ( <expression> )
<expression> --> <expression> + <expression>
<expression> --> <expression> - <expression>
<expression> --> <expression> * <expression>
<expression> --> <expression> / <expression> 

到目前为止,我已经在swi-prolog中编写了此代码,但遇到了以下所述的许多错误。

expression(N) --> number(Cs), { number_codes(N, Cs) }.
expression(N) --> "(", expression(N), ")".
expression(N) --> expression(X), "+", expression(Y), { N is X + Y }.
expression(N) --> expression(X), "-", expression(Y), { N is X - Y }.

number([D|Ds]) --> digit(D), number(Ds).
number([D])    --> digit(D).

digit(D) --> [D], { code_type(D, digit) }.

用测试

phrase(expression(X), "12+4"). 

给出X = 16,这很好。

phrase(expression(X), "(12+4)"). 

作品和词组(表达式(X),“ 12 + 4 + 5”)。 还可以

但是尝试

phrase(expression(X), "12-4"). 

导致“错误:超出本地堆栈”,除非我注释掉“ +”规则。 虽然我可以添加两个以上的数字,但括号不能递归工作(即“(1 + 2)+3”挂起)。

我敢肯定解决方案很简单,但是我无法从找到的在线教程中找出解决方案。

#1楼 票数:1 已采纳

您所做的一切原则上都是正确的。 您说对了:答案很简单。

但。

左递归在定句语法中是致命的。 症状恰好是您所看到的行为。

如果在expression上设置监视点并使用跟踪工具,则可以观察到堆栈不断增长,而解析器根本没有任何进展。

gtrace.
spy(expression).
phrase(expression(N),"12-4").

如果仔细考虑Prolog执行模型,您可以看到正在发生的事情。

  1. 我们尝试将“ 12-4”解析为一个表达式。

    我们的调用堆栈包含对步骤1中对expression调用,我将编写expression (1)。

  2. 通过“表达式”的第一个子句,我们成功地将“ 12”解析为表达式,并记录了一个选择点,以备日后需要回溯时使用。 实际上,我们需要立即回溯,因为涉及phrase的父请求说我们想解析整个字符串,但我们没有:我们还有“ -4”。 因此,我们失败了,回到选择点。 我们已经表明“表达式”的第一个子句不会成功,因此我们尝试对第二个子句进行重试。

    调用堆栈: expression (1)。

  3. 我们尝试使用第二个子句为“ expression”解析“ 12-4”,但是立即失败(初始字符不是“(”)。因此,我们失败了,然后针对第三个子句重试。

    调用堆栈: expression (1)。

  4. 第三个子句要求我们从输入的开头解析一个表达式,然后找到“ +”和另一个表达式。 因此,我们现在必须尝试将输入的开头解析为表达式。

    调用堆栈: expression (4) expression (1)。

  5. 我们尝试将“ 12-4”的开头解析为一个表达式,然后以“ 12”开头,就像在步骤1中一样。我们记录一个选择点,以备日后需要回溯时使用。

    调用堆栈: expression (4) expression (1)。

  6. 现在,我们恢复从步骤4开始的尝试,以将“ 12-4”解析为针对“表达式”的第3条的表达式。 我们已经完成了第一步。 现在我们必须尝试将“ -4”解析为“ expression”子句3右侧的其余部分,即"+", expression(Y) 但是“-”不是“ +”,因此我们立即失败,并返回到最近记录的选择点,即在步骤5中记录的选择点。接下来的事情是尝试找到一种不同的方法来解析起始位置。输入为表达式。 我们使用“表达式”的第二个子句恢复搜索。

    调用堆栈: expression (4) expression (1)。

  7. 第二个子句再次失败。 因此,我们继续“表达式”的第三个子句。 这要求我们在输入的开头查找表达式(作为弄清楚我们当前对“ expression”的两个调用是成功还是失败的一部分)。 因此,我们再次称呼“表达”。

    调用堆栈: expression (7) expression (4) expression (1)。

您可以看到,每次我们向堆栈添加对expression的调用时,我们都会成功,寻找加号,失败,然后再试一次,最终到达第三个子句,这时我们将把另一个调用推入堆栈然后再试一次。

简短的答案:左递归在DCG中是致命的。

在递归下降解析器中它也是致命的,解决方案大体相同:不要向左递归。

语法的非左递归版本为:

expression(N) --> term(N).
expression(N) --> term(X), "+", expression(Y), { N is X + Y }.
expression(N) --> term(X), "-", expression(Y), { N is X - Y }.
term(N) --> number(Cs), { number_codes(N, Cs) }.
term(N) --> "(", expression(N), ")".

但是,这使“-”具有正确的关联性,并且在许多情况下都需要重复重新定义初始术语,因此,用于生产的代码中的常见方法是做的事情不像您开始时使用的BNF,而应像以下EBNF版本那样:

expression = term {("+"|"-") term}
term = number | "(" expression ")".

我学会写它的方式(很久以前,我不再记得要归功于谁了)是这样的(我一开始发现它很丑,但它会在您身上成长):

expression(N) --> term(X), add_op_sequence(X,N).
add_op_sequence(LHS0, Result) -->
    "+", term(Y),
    {LHS1 is LHS0 + Y},
    add_op_sequence(LHS1,Result).
add_op_sequence(LHS0, Result) -->
    "-", term(Y),
    {LHS1 is LHS0 - Y},
    add_op_sequence(LHS1,Result).
add_op_sequence(N,N) --> [].

term(N) --> number(Cs), { number_codes(N, Cs) }.
term(N) --> "(", expression(N), ")".

到目前为止累积的值在add_op_sequence的左侧参数中向下传递,并最终(当序列以空生产结束时)最终向上传递。

称为“左角解析”的解析策略是解决此问题的一种方法。 关于在自然语言处理中使用Prolog的书籍几乎都会讨论它。

  ask by joeblog translate from so

未解决问题?本站智能推荐:

2回复

右手语境符号[DCG]

在这个网站中 ,我找到了一个部分,解释了如何使用DCG构建右手上下文表示法 有人帮助我通过示例找出这个方法,并说明它解析无上下文语法的好处
1回复

在 PROLOG 中创建 DCG 解析器

我必须在 PROLOG 中实现一个上下文无关的解析器,它使用可以生成的语法: (我知道这在语法上不正确,但我需要看看如何匹配模式) 我收到一个输入作为查询 - 让我们假设它是第一句话 - 我必须打印规则应用程序的数量才能成功解析,否则为 false。 为了实现这一点,我找到了这些语法:
3回复

扩展到CFG,它是什么?

考虑以下对无上下文语法的扩展,该语法允许规则在左侧,非终端右侧的一个(或多个)终端。 也就是说,形式的规则: 右侧可以是任何东西,例如在无上下文的语法中。 特别是, 不要求右侧的末端具有完全相同的终端符号。 在这种情况下,此扩展将是上下文相关的。 但终端不仅仅是一个背景。 有时
1回复

Prolog如何将DCG规则转换为定句?

我正在研究定语从句语法,但是我有一些问题需要了解Prolog如何将DCG规则转换为定句。 例如,这是一个写为DCG的小语法: 如果我提出查询: 它回答我: 这是什么意思? 为什么有两个论点? 另外,“ C”在这里是什么意思: ? 谢谢!
1回复

将小的正则表达式转换为DCG

我知道Prolog程序员通常使用DCG而不是正则表达式来匹配字符串中的模式。 在Perl中,可能会写 如何在Prolog中匹配相同的模式?
1回复

前言-DCG转换

E -> T是什么意思? 变量E表示变量T ? 这是链接的代码: 谢谢你的帮助
1回复

关于Prolog如何自动将DCG语法转换为一组规则的一些疑问

我正在使用Ivan Bratko的著作《人工智能编程》在Prolog中研究DCG语法,并且发现一些问题以了解Prolog如何将DCG语法自动转换为一组Prolog规则。 例如,我有以下DCG语法: 哪里: moove是可能的mooves的列表,而step是可以向上或向下移动的
1回复

将EBNF转换为BNF并在Prolog上将其用作DCG格式

作为项目的一部分,我应该将EBNF转换为BNF,并使用DCG在SWI-Prolog中对BNF进行编程。 EBNF如下: 我的程序应将源文件作为输入并打印一条消息,指出该程序在语法上是否正确。 由于我没有序言方面的经验,也没有在Youtube上观看许多视频以及阅读完全没有帮助的教程