3

有人可以给我一些关于如何处理需要查看进一步声明以便能够在当前时刻做出正确语义动作的情况的建议/想法吗?例如,当有人编写某种不支持“前向声明”的编程语言的解释器/编译器时,这是一个众所周知的事件。让我们举个例子:

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 遍历/应用访问者,我们必须对直接集成在解析器中的所有美丽的凤凰结构说“再见”。

你会如何处理这个问题?

4

3 回答 3

3

[第二个答案,关于语义] 这个特定的例子恰好很简单。您可以做的是记录对尚未声明的函数的函数调用以及实际的参数类型。当您稍后确实遇到函数声明时,您会检查是否有前面的函数调用(更好地)与这个新函数匹配。您显然只会在解析结束时检测到错误,因为最后一行可能会引入缺失的函数。但是在那一行之后,任何根本不匹配的函数调用都是一个错误。

现在,问题是这适用于简单的语义。如果您查看更复杂的语言 - 例如使用类似 C++ 的函数模板- 将不再可能在简单的表中进行此类查找。您将需要在结构上与您的语言结构相匹配的专用标签。AST 并不是最好的结构,更不用说解析过程中的部分 AST。

于 2009-11-05T11:06:24.930 回答
2

如果你想做两遍,而不是在第一遍结束时进行语义检查,你可以让你的动作调用的函数知道它们在哪一遍。所以如果你有一些动作

[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
}
于 2009-11-05T12:41:16.350 回答
1

我认为你在做出毫无根据的假设。例如,“很明显我们必须至少有两次通过”。不,不是。如果语法foo(123)只能被解析为function-name "(" expression ")",那么一遍就足够了。

因此,我建议您设计语法以进行明确的解析。避免无法单独解析的构造,例如避免依赖于其他地方的声明。

于 2009-11-05T09:10:59.410 回答