C#龙书(词法分析)如何处理文字

时间:2016-05-07 22:47:25

标签: c# dictionary compiler-construction lexer

这个项目是供教育使用的,我非常清楚优秀的编译器已经存在。

我目前正在通过着名的Dragon Book奋战,并开始实施我自己的Lexer。除文字外,它的效果令人惊讶。我不明白如何使用符号(查找)表处理文字,这本书似乎并没有很好地涵盖这一点:

在以下代码中,60是一个数字文字:

int myIdentifier = 60;

龙书说:

  

从技术上讲,对于lexeme 60,我们应该组成一个令牌   (数字,4),其中4指向内部的符号表   表示整数60 [...]

理解 - 我创建了以下令牌:

<enum TokenType, int lookupIndex> 
//TokenType could be 'number' and lookupIndex could be any int

将文字存储在这样的字典中:

Dictionary<int literal, int index> 
//literal could be '60' and index could be anything

由于文字本身是字典中的关键字,因此我可以快速检查是否已将未来文字添加到符号表中(或不)。

然后,Parser从Lexer接收令牌,并且应该能够识别符号表中的文字。

的问题:

  1. 为什么我的令牌包含查找索引而不是包含文字本身?
    不会告发&#39;更快......
  2. 当lookup-index是字典的值时,Parser如何能够快速找到符号表中的文字值?
    (我无法使lookup-index成为字典键,因为Lexer必须检查字典的值,因为它不是非常高效的字典) 多索引字典可以成为解决方案吗?我猜不是......
  3. 我必须为每种类型的文字创建一个符号表吗?
    F.e.:Dictionary<int literal, int index>
    Dictionary<double literal, int index>
    Dictionary<char literal, int index>等。
  4. 也许我完全错误地使用文字。随意发布任何更好的解决方案。

1 个答案:

答案 0 :(得分:2)

  1. 为什么我的令牌包含查找索引而不是包含文字本身?不会更快吗?

    当然,它可能会更快。但是,每个字面值都是不同的值。现在,大多数程序员都期望如果他们在同一个程序中使用"this longish string"两次,编译器就会非常聪明,只能在最终的可执行文件中发出该字符串的单个副本。如果你反编译代码时,你也会发现令人惊讶的,你发现常量1有273个不同的存储位置,因为每次编译器看到{{1} },它创建了一个新常量。

    确保常量文字只发出一次的最简单方法是将它们保存在以文字值索引的关联容器中。

    正如@ sepp2k在一个提交中指出的那样,大多数硬件允许使用小整数常量作为直接操作数,有时甚至是不那么小的常量。所以关于上面的常数1的陈述有点夸张。你可能能够以不同的方式处理整数,但这可能不值得。

  2. 当lookup-index是字典的值时,Parser如何能够快速找到符号表中的文字值?

    这在很大程度上取决于您用于文字表的精确数据结构(我不喜欢调用符号表,但不可否认这些概念是相关的。)在许多语言中,您会发现您的标准库容器并不是问题的完美匹配,因此您需要根据目的进行调整或编写替换。

    尽管如此,它并不是非常复杂。一种可能性是使用a += 1map<literalType, int>的组合。这里,地图将文字值与索引关联到矢量中。当您找到新的文字值时,将其输入到与矢量的当前大小相关联的地图中,然后将该值推送到矢量(这将使其索引对应于您刚刚插入到地图中的索引。)< / p>

    对于像字符串这样的大常量来说,这并不完全理想,因为在映射中的键和向量中的值之间,常量被存储两次。当你开始时,我建议你只是压抑你对这种重复的烦恼;之后,如果它被证明是一个问题,你可以找到解决方案。

    如果您使用的是C ++,则可以使用(无序)集而不是映射,并使用对新添加的元素的引用(指针)而不是索引。但我不认为这个功能可以用多种语言提供,而且与索引相比,指针有时也很尴尬。在某些语言中,您可以将所有值放入向量中,然后保留一个是向量中的索引的集合。这要求可以使用除键类型之外的其他内容来查找集合;出于某种原因,这个功能在很少的数据结构库中可用。

    而且,是的,如果您有其中一个方便的话,可以使用双重索引的数据结构。 (实际上,地图+矢量解决方案是双重索引的数据结构。)

  3. 我必须为每种类型的文字创建一个符号表吗?

    也许。你有多少文字?

    您可能最终会使用类型标记的枚举(&#34;有区别的联合&#34;),无论是变量还是常量。 (同样,并非所有语言都在其标准库中具有歧视性联盟,这确实令人难过;如果您的实现语言缺乏此基本功能,则您需要实现它。)对于有区别的联合实例,当然应该可以用作关联数据结构中的键,因此原则上没有什么能阻止你将所有文字保存在单个数据结构中。如果你有合适的类型,这绝对是我推荐的,至少在开始时。

    请注意,当您最终将文字作为目标代码发出时,您对其位表示和对齐的兴趣确实比它们的语义更感兴趣。如果两个完全不同类型的常量恰好具有相同的位表示,那么您可以为它们使用相同的存储位置。如果您有多个整数数据类型的宽度,那么您可能希望将所有这些数据类型保留在单个文字表中,以准确利用此优化。无需存储每个宽度的vector<literalType> :)。偶尔你会发现其他两种不同类型的文字具有相同表示的情况,但它可能不常见,不能用来处理它。 (但是,在IEEE硬件上,浮点和整数零具有相同的表示形式,并且通常与NULL指针的表示形式相同,因此您可能需要特殊的大小写零。)

    总而言之,这是一次判断。使用歧视联盟作为关键是多么复杂?通过使用具有特定密钥类型的关联容器可以节省多少存储空间,这是否重要?你想迭代所有相同类型的文字常量(答案:可能)以及你的数据结构有多容易?

    如果您使用精心设计的内部API,您将能够改变您对文字表的内部表示的看法。因此,我将此实验作为尝试良好API设计的机会。

  4. 还有别的吗?

    祝你的项目好运。学习和享受!