当有人需要查看进一步的声明以便能够在当前时刻做出正确的语义操作时,有人可以给我一些关于如何处理情况的建议/想法吗?例如,当某人编写某种不支持“前向声明”的编程语言的解释器/编译器时,这是众所周知的事件。我们举个例子:
foo(123);//<-- our parser targets here. we estimate we have a function
// invocation, but we have no idea about foo declaration/prototype,
// so we can't be sure that "foo" takes one integer argument.
void foo(int i){
//...
}
很明显,我们必须至少有两次传球。首先,我们解析所有函数声明并获取所有需要的信息,例如:函数采用的参数量,它们的类型然后我们能够处理函数调用并解决上面的困难。如果我们这样做,我们必须使用一些AST遍历机制/访问者来完成所有这些传递。在这种情况下,我们必须处理AST遍历/应用访问者,我们必须对直接集成在我们的解析器中的所有凤凰结构的美丽说“再见”。
你会怎么处理这个?
答案 0 :(得分:3)
[第二个答案,关于语义] 这个特殊的例子很简单。你可以做的是记录对未声明的函数进行的函数调用,以及实际的参数类型。当您稍后遇到函数声明时,检查是否存在与此新函数(更好)匹配的先前函数调用。显然,只有在解析结束时才会检测到错误,因为最后一行可能会引入缺失的函数。但是在该行之后,任何完全没有匹配的函数调用都是错误的。
现在,问题是这适用于简单的语义。如果你看一些更复杂的语言 - 例如使用类似C ++的函数模板 - 不再可能在简单的表中进行此类查找。您需要在结构上与您的语言结构匹配的专门tabes。 AST并不是那些最好的结构,更不用说解析过程中的部分AST了。
答案 1 :(得分:2)
如果你想进行两次传球,而不是在第一次传球结束时进行语义检查,你可以通过你的行动调用te函数知道他们所处的传球。所以如果你有一些动作
[functionCall(name, args)]
[functionDef(name, args, body)]
它们将被定义为类似的东西(不是正确的精神语法,但你明白了)
functionCall(string name, vector<string> args)
{
if (!first_pass) {
// check args for validity
// whatever else you need to do
}
}
functionDef(string name, vector<string> args, ... body)
{
if (first_pass)
// Add function decleration to symbol table
else
// define function
}
答案 2 :(得分:1)
我认为你做出了毫无根据的假设。例如,“很明显我们必须至少有两次通过”。不,不是。如果语法是foo(123)
只能解析为function-name "(" expression ")"
,那么一次传递就足够了。
因此,我建议您设计语法以进行明确的解析。避免使用无法单独解析的构造,例如避免依赖于elesewhere的声明。