0

我目前正在学习如何使用Irony创建简单的表达语言。我在找出定义函数签名的最佳方法以及确定验证这些函数输入的责任时遇到了一些麻烦。

到目前为止,我有一个简单的语法来定义我的语言的基本元素。这包括一些二元运算符、括号、数字、标识符和函数调用。我的语法的 BNF 看起来像这样:

<expression> ::= <number> | <parenexp> | <binexp> | <fncall> | <identifier>
<parenexp>   ::= ( <expression> )
<fncall>     ::= <identifier> ( <argumentlist> )
<binexp>     ::= <expression> <binop> <expression>
<binop>      ::= + - * / %
... the rest of the grammar definition

使用 Irony 解析器,我能够验证各种输入字符串的语法,以确保它们符合以下语法:

x + y / z * AVG(a + b, p)   -> Valid Syntax
x +/ AVG(x                  -> Invalid Syntax

这一切都很好,但现在我想更进一步,定义可用的函数,以及每个函数所需的参数数量。因此,例如,我想要一个FOO接受一个参数并BAR接受两个参数的函数:

FOO(a + b) * BAR(x + y, p + q)    -> Valid
FOO(a + b, 13)                    ->  Invalid

解析第二条语句时,我希望能够输出一条错误消息,该消息知道此函数的预期输入:

Too many arguments specified for function 'FOO'

我实际上不需要评估任何这些语句,只需验证语句的语法并确定它们是否是有效的表达式。

我到底应该怎么做?我知道从技术上讲,我可以像这样简单地将函数添加到语法中:

<foofncall> ::= FOO( <expression> )
<barfncall> ::= BAR( <expression>, <expression> )

但是这方面的某些事情感觉不太对劲。在我看来,语法似乎应该只定义一个通用函数调用,而不是该语言可用的每个函数。

  • 这通常在其他语言中是如何实现的?
  • 哪些组件应该负责分析语言语法的基本语法与函数定义等更具体的元素?这两个职责应该由同一个组件处理吗?
4

1 回答 1

2

虽然您可以直接在语法中进行类型检查,以便在解析器中强制执行,但这样做通常是个坏主意。相反,解析器应该只解析基本语法,并且应该使用单独的类型检查代码进行类型检查。

在编译器的正常情况下,解析器只生成抽象语法树或程序的某种等效表示。然后,在 AST 上运行类型检查传递,以确保所有类型正确匹配——确保函数具有正确数量的参数并且这些参数具有正确的类型,以及确保变量具有分配给的正确类型它们以及它们的使用方式。

除了通常更简单之外,这通常可以让您提供更好的错误消息——而不仅仅是“无效”,您可以说“FOO 的参数太多”或者你有什么。

于 2012-11-07T01:08:54.557 回答