解析挑战:旧逻辑学家的点符号

时间:2013-08-29 12:07:15

标签: parsing grammar language-design

在J. Barkley Rosser的“Logic for Mathematicians”中,他用一种符号来避免括号太多。虽然我不知道逻辑学家什么时候开始使用这种表示法,但我知道这本书于1957年首次出版,而1916年出版的J. G. P. Nicod's paper也使用了这种表示法。显然,它有着悠久的历史,尽管如今这并不受现代逻辑学家的青睐。

在编程世界中,在LISP类似的编程语言中,程序员要跟踪正确(大量!)括号的数量是一个巨大的挑战。 Haskell提供了一个提供部分功能的运算符$,但由于你不能说2 * $ 3 + 4它不像点那样强大(见下面的例子)。 C语言序列使用传统的操作优先级,但在某些情况下仍然需要深嵌套括号。所以我想知道为什么没有实际语言使用这种策略?我试过了,但我发现甚至不能为它写一个语法!

让我举一个玩具计算器语言的例子,只有两个算子+*,所有术语都是整数。

使用这种表示法,翻译者应通过以下测试用例:

1 + 3 .* 2
= (1 + 3) * 2
1 *. 3 + 2
= 1 * (3 + 2)
1 *. 2 +. 2
= (1 * 2) + 2
2 *: 2 + 3 .* 4
= 2 * ((2 + 3) * 4)

我无法解释这种符号的所有细节,它在Rosser的书中花费了近5页。但是在genaral(和short)中,在运算符之前或之后的点.代表“分隔符”,将两边推开。冒号:是一个更强的分隔符,三个点.::.甚至更强,但小于::,依此类推。

我想知道如何为上述语言编写语法然后解析它?虽然这个符号已被淘汰,但我发现程序员的眼睛看起来非常清楚。那它的优点和缺点是什么呢?

3 个答案:

答案 0 :(得分:8)

罗素和怀特黑德在Principia Mathematica(1910-1913)中最常使用点符号,但该符号来自Guiseppe Peano。它也被Alonzo Church,Willard Van Orman Quine和其他有影响力的逻辑学家使用(参见斯坦福大学哲学百科全书中的this entry)。

通过一些练习,用点符号读取公式并不困难,但它并不像第一次出现那么优雅。首先,Russell和Whitehead仍然发现将括号与否定运算符~一起使用很有用:

*3·01. p.q .=. ~(~p v ~q)

如上例所示,点既用作连词运算符又用于表示优先级。因此,更强的连接可以写为:或甚至:.

最后,为了减少视觉混乱(我想),Russell和Whitehead也使用优先关系,其中运算符集被划分为三个优先级组,使得优先级较高的运算符的点占主导地位等于优先级较低的运算符的点数。在具有相同优先级的运算符之间,点数相等是不合法的,但是Russell和Whitehead还定义了一些三元运算符,例如p v q v r,以便能够避免必须指定不重要的优先级。 (据我所知,这些表达式的精确解析规则从未正式列出,但定义出现在PM中。)

说了这么多,使用分流码算法的变体解析点符号并不是非常困难。不幸的是,它无法使用无上下文语法进行解析,这使得它对使用自动化工具生成的语法不太有用。甚至GLR解析器也仅限于CFG。 (点符号不是无上下文的事实可以通过泵浦引理证明;它比通常的泵浦引理应用程序更有趣,至少是imho。)

如果允许CFG具有无限数量的(点)符号和相应的无限数量的规则,那么编写语法(或者更准确地说,语法模板是非常简单的,因为大多数规则都是由点数参数化)。因此,理论上您可以通过首先扫描来解析点表达式,以找到所使用的最大点数,然后从模板生成相应的有限CFG。 (只要为每个点序列定义一个单独的终端,结果就是LALR(1)。)实际上,这可以通过在CFG中使用谓词来完成,因此您可以创建一个包含解析器生成器的解析器谓词(例如,ANTLR,但我个人使用自下而上的生成器来避免摆弄左递归消除。)

重要的是要注意点符号有自己的“冗余括号”变体,因为至少在理论上你可以使用比必要更多的点。当我在使用上面的(理论上但不是真正有用的)算法 - 自动生成CFG时 - 我发现更容易要求点最小化,因为否则你最终会创建更多的单位规则。我无法找到机器可读的PM副本进行测试,但在我做的所有搜索中,我从未发现过一个不是最小点的表达式。我不知道这是强迫整洁的结果还是只有点极小表达才合法的想法。

答案 1 :(得分:2)

这是一个有趣的应用程序。 Perl6允许您扩展语言,添加新运算符并定义它们相对于现有运算符的优先级。以下代码示例定义了运算符*. .*等。下面的测试通过。

use v6;
use Test;

sub infix:<*.>  ($a, $b) is looser(&infix:<+>)  { $a * $b }
sub infix:<.*>  ($a, $b) is looser(&infix:<+>)  { $a * $b }
sub infix:<*:>  ($a, $b) is looser(&infix:<*.>) { $a * $b }
sub infix:<:*>  ($a, $b) is looser(&infix:<.*>) { $a * $b }

sub infix:<+.>  ($a, $b) is looser(&infix:<*.>) { $a + $b }
sub infix:<.+>  ($a, $b) is looser(&infix:<.*>) { $a + $b }
sub infix:<+:>  ($a, $b) is looser(&infix:<*:>) { $a + $b }
sub infix:<:+>  ($a, $b) is looser(&infix:<:*>) { $a + $b }

# Tests

is 1 + 3 .* 2,
    (1 + 3) * 2;

is 1 *. 3 + 2,
    1 * (3 + 2);

is 1 *. 2 +. 2,
    (1 * 2) + 2;

is 2 *: 2 + 3 .* 4,
    2 * ((2 + 3) * 4);

答案 2 :(得分:0)

ISO核心标准Prolog不允许直接解析 旧的逻辑学家点表示法。虽然可以形成符号 包括一个点,其中一个突出的是univ运算符

(=..)/2。用作运算符的不同符号导致 解析术语中的不同函子。减轻 我已经介绍了sys_alias/1运算符

我的Prolog系统中的

属性。这是一个示例定义:

:- op(750, xfx, .=.).
:- set_oper_property(infix(.=.), sys_alias(=)).

在上面,我们使用(=)/2的第二个变体。通常的(=)/2有 运算符级别700,第二个变体具有更高的运算符 级别750,导致期望的括号。

这是一个示例运行:

Jekejeke Prolog 4, Runtime Library 1.4.5

?- write(p = q .=. q = p), nl.
(p = q) = (q = p)
Yes

可能有助于解析该野兽:

关于假肢的单个公理。 II。
博莱斯瓦夫Sobociński-1961

https://projecteuclid.org/euclid.ndjfl/1093956834