作为运算符的邻接 - 任何词法分析器都可以处理它吗?



假设一种语言将两个数学Unicode字母数字符号的邻接定义为运算符。比如说,+1 表示 %adj + 1,其中 %adj 代表运算符邻接定义的任何内容,在本例中为乘法。我想知道,任何现有的词汇分析工具都可以处理这个问题吗?

不可见运算符无法通过词法分析识别,原因应该或多或少显而易见。您只能通过分析语法上下文来推断不可见运算符的存在,这是解析器的角色。

当然,大多数词法分析工具都允许为每个识别的令牌执行任意代码,因此没有什么能阻止您在词法扫描器中构建状态机,甚至是完整的解析器。这很少是好的设计。

如果你的语言是明确的,那么在你的语法中处理邻接是没有问题的。但必须小心一些。例如,你很少希望x-4被解析为x-4的乘法,而是一个朴素的语法,其中包括,例如,

expr -> term | expr '-' term
term -> factor | term factor | term '*' factor
factor -> ID | NUMBER | '(' expr ')' | '-' factor

将包括这种模棱两可。要解决此问题,您需要禁止使用以一元运算符开头的第二个操作数进行邻接生产:

expr -> term | expr '-' term
term -> factor | term item | term '*' factor
factor -> item | '-' factor
item -> ID | NUMBER | '(' expr ')'

注意term -> term '*' factor和不允许x - yx * - yterm -> term base(expr -> expr '-' termx - y识别为减法)之间的区别。

有关允许邻接作为运算符的上下文无关语法的示例,例如,请参阅 Awk,其中邻接表示字符串连接,以及 Haskell,其中邻接表示函数应用程序。


由于这个问题不时出现,因此SO已经有许多相关的答案。以下是一些:

  • 使用 yacc 解析表达式序列。隐形函数应用操作员。使用yacc/野牛;包括显式和基于优先级的解决方案

  • yacc - 没有运算符的规则的优先级?不可见的字符串串联运算符。使用Ply(Python解析器生成器)

  • 串联
  • 移位-减少冲突 另一个不可见的串联运算符。使用JavaCUP。

  • 使用 yacc 不可见函数应用程序运算符解析表达式序列。使用 fsyacc(F# 解析器生成器)

  • 对没有终端、只有非终端的规则使用 yacc 优先级。普通数学表达式中的邻接关系。使用带有优先规则的 yacc/bison。

  • 野牛/亚克 - 优先级设置的限制。类似哈斯克尔的函数应用程序邻接。使用带有优先规则的 yacc/bison。

下面是一个在 Python 中使用 pyparsing 的示例:

import pyparsing as pp
integer = pp.pyparsing_common.integer()
variable = pp.oneOf(list("abcdefghijklmnopqrstuvwxyz"))
base_operand = integer | variable
implied_multiplication = pp.Empty().addParseAction(lambda: "*")
expr = pp.infixNotation(base_operand,
[
("**", 2, pp.opAssoc.LEFT),
(implied_multiplication, 2, pp.opAssoc.LEFT),
(pp.oneOf("+ -"), 1, pp.opAssoc.RIGHT),
(pp.oneOf("* /"), 2, pp.opAssoc.LEFT),
(pp.oneOf("+ -"), 2, pp.opAssoc.LEFT),
])

这假定变量只是单个字符。还有一些对操作优先级的捏造,以使邻接、幂和前导符号起作用。添加到implied_multiplication表达式的解析操作用于显示乘法运算符的插入。

下面是一些测试输出:

tests = """
x-4
ax**2 + bx +c
ax**2-bx+c
mx+b
"""
expr.runTests(tests, fullDump=False)

指纹:

x-4
[['x', '-', 4]]
ax**2 + bx +c
[[['a', '*', ['x', '**', 2]], '+', ['b', '*', 'x'], '+', 'c']]
ax**2-bx+c
[[['a', '*', ['x', '**', 2]], '-', ['b', '*', 'x'], '+', 'c']]
mx+b
[[['m', '*', 'x'], '+', 'b']]

除非令牌具有固定长度,否则必须将相同类型的相邻标记与其他标记或空格分隔开。Gosu 编程语言包含邻接来实现"绑定表达式",这些表达式支持以下单元:

var length = 10m  // 10 meters
var work = 5kg * 9.8 m/s/s * 10m
print( work )  // prints 490 J
var investment = 5000 EUR + 10000 USD
var date = 1966-May-5 2:35:53:909 PM PST

相关内容

最新更新