制作玩具语言解释器,AST变量和范围

时间:2018-09-20 01:12:29

标签: parsing abstract-syntax-tree interpreter

就像标题中一样,我最近开始与一位朋友一起使用c ++进行解析器/解释器的工作,该解析器/解释器随后将集成到一个更大的项目中(如果可行);我们决定开始制作用于抽象语法树的类,稍后我们将在适当的解析器端进行工作。 我们从范围和变量的概念开始。进行搜索后,我们发现了一个示例,其中每个范围都有一个符号表和一个指向先前范围的符号的链接,因此,如果在当前范围中未找到变量,它将在上方查找,依此类推。 我必须从该示例中指出的第一件事是,尝试访问一个在堆栈中具有很多作用域甚至不存在的变量的代价是很高的(最坏的情况是堆栈深度很高)。我当时想……不,我们可以做得更好。

我们的思考结果如下: 程序根目录中的单个符号表,其中包含字符串到变量堆栈的映射

map<string, stack<variable>> table;

然后每个范围将保存一组字符串,这将是在该范围中分配的字符串

set<string> allocated;

分配了变量“ a”后,其名称的字符串将添加到该范围本地集中,然后将新变量推入e表(table [“ a”]。push())。 要访问该变量以进行编辑或读取,将读取相同位置的顶部(table [“ a”]。top()) 最后,作用域析构函数将遍历已分配的所有元素,并从映射的堆栈中弹出。

for(variable_name in allocated)
    {
    table[variable_name].pop();
    }

在任何情况下,这种方式都会进行分配,读取和写入O(1)。 这是我的两个问题:

1)必须保存表和范围中每个变量的字符串,并且必须在范围末尾遍历所有变量,这与多表系统相比效率低下只需删除一个数组?

2)我发现的示例在初学者教程中不是有效率,还是我想念的东西比我和我的朋友提出来的想法更有价值?

1 个答案:

答案 0 :(得分:1)

特纳在这里没有绝对的答案;它取决于语言范围的确切性质以及编程风格。我的建议是首先使其运行,然后查看是否需要改进。无论选择使用什么作为符号表实现,请确保将实现详细信息隐藏在ADT原型后,该原型将定义符号表的行为。然后,您可以根据需要轻松地交换其他实现。

无论如何,这里有一些数据点:

  1. 作用域嵌套通常不是很深。实际上,对于大多数语言而言,嵌套范围很广的样式被认为是较差的样式。

  2. 无论如何,您的建议涉及为每个范围创建一个哈希表。这不是真的必要;您可以使用用于所有查找的单个哈希表和标记范围边界的堆栈来完成整个操作。符号表是unordered_map<name, definition>,作用域堆栈是stack<pair<name, definition>>。 (我假设使用C ++。在这里,name可能只是std::string的别名,但请参阅下文。definition包含您需要为每个符号存储的元数据。不必保留它们像这样分开;您可以使用单一类型,然后使用set而不是map。)作用域堆栈中的definition是来自外部作用域的定义或指示在外部范围内变量未定义。还需要有一个标记值(用于名称或定义),以指示作用域的开始。

    输入示波器时,将前哨压入示波器堆栈。然后,每次定义变量时,它的先前定义都被推入作用域堆栈,新定义存储在符号表中。离开合并范围时,将合并范围堆栈弹出到最后一个标记,然后将每个变量替换为其先前的定义。

  3. 典型语言中有很多不同类型的作用域。下面是一些示例:

    • 关闭范围。如果允许在函数内部定义函数,则某些外部作用域实际上是闭包。尽管符号表的处理除了正确地跟踪元数据外并没有什么不同,但这些处理与同一函数中外部块的作用域需要不同的处理。

    • 全局范围和/或模块范围。

    • 复合对象(“类”)成员名称范围。这些嵌套方式与块作用域的嵌套方式不同,但是取决于您语言的名称查找算法,它们可能仍属于链接名称搜索的一部分。

  4. 创建名称std::string对象显然更简单,但是最终将创建大量重复的字符串,与其他字符串相比,这些字符串必须是字符串。现代计算机的速度足够快,因此无关紧要,但是您可能仍要考虑对其进行优化。我更喜欢通过将字符串放入std::set<std::string>(或等效项)中,然后使用元素指针而不是字符串本身来“实习”字符串。这有两个好处:

    • 每个字符串仅存储和分配一次,从而节省了分配开销。现代的分配库非常快,但保留成千上万个相同字符串的副本仍然毫无意义,对于程序中名称的每次使用都是如此。将名称保留在内部表中可能会增加它们的生存期,但是实际上这并不是什么大问题,特别是因为许多程序员在不同的范围内回收名称。

    • 可以使用指针比较而不是逐字符比较来比较名称。因为它不需要循环,所以速度更快。同样,现代硬件使此操作变得不必要,但这仍然是一个优点。如果然后使用指针而不是字符串作为符号表键,则可以节省每次查找时计算键哈希值的开销。这是可衡量的,尽管不是革命性的进步。

相关问题