在简单语言语法中定义函数签名

时间:2012-11-07 00:48:22

标签: parsing compiler-construction syntax bnf irony

我目前正在学习如何使用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> )

但是这件事并不合适。对我来说,似乎语法应该只定义一个泛型函数调用,而不是语言可用的每个函数。

  • 这通常如何用其他语言完成?
  • 有哪些组件应该处理分析语言语法的基本语法与更具体的元素(如函数定义)的职责?两个职责是否应由同一个组成部分处理?

1 个答案:

答案 0 :(得分:2)

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

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

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