Upfront,我正在使用Python编写解释器,而不是编译为机器代码的实际编译器。我最近一直在浏览相当多的编译器构造指南,我理解为解析器生成令牌的基础知识,并构建一个语法树来评估算术表达式,但我不太明白如何解析带有函数调用的表达式,
之类的东西图。 (a)中
1 + pow(1, 1)
或者当用户定义像这样的函数时如何解析行
图。 (b)中
function newFunction( someArgs ){
... some magic ...
}
在图(a)中,我该如何标记这个表达式?在阅读保留字“pow”之后,我应该抓住所有内容到最后的括号并将其传递给解析器吗?或者我应该将“pow”,“(”,“1”,“1”和“)”作为单独的标记包含在内并将它们添加到我的解析树中?
在Fib。 (b)在编译函数定义时,我甚至不知道从哪里开始。任何让我走向正确方向的信息都将受到赞赏。
编辑:我正在使用Backus-Naur形式语法:
S :: =表达式
表达式:: = term |术语([+ - ]术语)+
term :: = factor | factor([* /] factor)+
factor :: = number | (表达)
number :: = [0-9] +
答案 0 :(得分: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
语法需要两个表达式。
答案 1 :(得分:2)
另一张海报建议在你的语法中添加函数名称。
适用于玩具语言,但不适用于可能存在大量库以及大量用户定义函数的实用语言。
您可以通过向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]+
现在你的解析器可以接收函数调用。 (左边的语法是定义函数的一种方式......但这只是我留给你添加的更多语法。)
这样做的代价是,在解析之后,某些东西决定了每个函数名称,它确切代表了它代表的代码位。您需要一个符号表来执行此操作。 这是另一个问题。