编译器:如何解析函数调用和函数定义

Posted

技术标签:

【中文标题】编译器:如何解析函数调用和函数定义【英文标题】:Compilers: How to parse function calls and function definitions 【发布时间】:2013-09-20 03:59:50 【问题描述】:

首先,我正在用 Python 制作解释器,而不是编译为机器代码的实际编译器。我最近浏览了很多编译器构建指南,我了解为解析器生成标记和构建语法树以评估算术表达式的基础知识,但我不太了解如何解析带有函数调用的表达式,像

图。 (一)

1 + pow(1, 1)

或者当用户定义这样的函数时如何解析行

图。 (b)

function newFunction( someArgs )
   ... some magic ...

在图(a)中,我应该如何标记这个表达式?在阅读了保留字“pow”之后,我是否应该将所有内容都抓取到右括号并将其传递给解析器?还是应该将“pow”、“(”、“1”、“1”和“)”分别作为单独的标记添加到我的解析树中?

在斐波那契。 (b) 在编译函数定义时,我什至不知道从哪里开始。任何能让我朝着正确方向前进的信息将不胜感激。

编辑:我使用的是巴科斯-瑙尔形式语法:

S ::= 表达式

表达式 ::= 术语 |项([+-]项)+

术语 ::= 因子 |因子([*/] 因子)+

因子 ::= 数字 | (表达式)

数字 ::= [0-9]+

【问题讨论】:

【参考方案1】:

另一张海报建议在语法中添加函数名称。

这适用于玩具语言,但不适用于可能有大量库和大量用户定义函数的实用语言。

您可以通过向 BNF 添加函数调用来处理后者, 以一种将函数名排除在语法之外的方式:

S ::= expression

expression ::= term | term ([+-] term)+

term ::= factor | factor ([*/] factor)+

factor ::= number | ( expression ) | identifier |  functioncall

functioncall ::= identifier [(]  arguments [)]

arguments ::= empty | arguments 

arguments ::= expression |  arguments [,] expression

number ::= [0-9]+

identifier ::= [a-z]+

现在您的解析器可以接收函数调用。 (语法中省略了一种定义函数的方法......但这只是更多的语法,我留给你添加)。

这样做的代价是,在解析之后,某些东西已经决定了每个函数名称,确切地说它代表了哪位代码。您将需要一个符号表来执行此操作。 那是另一个问题。

【讨论】:

list_of_funcs[0]()等函数调用呢? 如果你允许一个特殊的表达式来计算要调用的函数,那么你相应地修改语法。要首先处理您的具体示例,您需要将 factor 的定义扩展为 factor = ... |因子 '[' 表达式 ']' ;然后你改变函数调用:functioncall = factor '[' arguments ']'。 BNF 很有表现力。【参考方案2】:

希望这个问题会有一些好的答案,但我建议你做的第一件事是帮助提高问题的质量:

    限制您的语言范围并 定义您的语法。

查看Backus–Naur Form,了解如何定义您的语言将支持的语法。

首先,考虑为少数数学函数编写一个简单的解析器,以了解它。

<digit>   ::= "0"|"1"|...|"9"
<integer> ::= <digit>*
<expr>    ::= <integer> | <add> | <pow>
<add>     ::= "add(" <expr> "," <expr> ")"
<sub>     ::= "sub(" <expr> "," <expr> ")"
<pow>     ::= "pow(" <expr> "," <expr> ")"

从这样的正式语法中,您可以确定什么是有效表达式,什么不是有效表达式。

例如:add(1,2)pow(2,add(2,1)) 都是有效的,而 add(1) 不是 add 语法需要两个表达式。

【讨论】:

哇...我不敢相信我从来没有想过将保留字添加到我的语法中。假设我对程序中的所有预定义函数都这样做了,我的意思是将它们添加到我的语法表中,以便可以递归地定义它们。我将如何添加使用我的语言的程序员创建的函数定义?我应该以某种方式动态地将它们添加到我的语法中还是将它们存储在其他地方? 在大多数语言中,函数本质上是指向语句/表达式的顺序列表的指针。如何管理这取决于您。最简单的可能是将函数的名称作为指向该列表的指针存储在符号表中,当在源代码中找到具有该 id 的标记时,您的解释器知道执行指令列表。

以上是关于编译器:如何解析函数调用和函数定义的主要内容,如果未能解决你的问题,请参考以下文章

c语言中函数定义和声明有啥区别

C语言基础:函数(定义函数声明函数调用函数(传值调用 / 引用调用)函数的参数(形参 / 实参))

函数声明与函数定义的区别

函数定义函数声明函数调用以及extern跨文件的变量引用

C++基础入门丨6. 函数——定义调用和声明

C++基础入门丨6. 函数——定义调用和声明