这个项目是为了教育使用,我很清楚优秀的编译器已经存在。
我目前正在努力通过著名的Dragon Book并刚刚开始实现我自己的lexer。它工作得非常好,除了文字。我不知道如何使用符号(查找)表处理文字,这本书似乎没有很好地涵盖:
在下面的代码中60是一个数字文本:

int myIdentifier = 60;

龙卷上说:
从技术上讲,对于词素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

因为文字本身是字典中的键,所以我可以快速检查将来的文字是否已经添加到符号表中。
然后解析器从lexer接收令牌,并且应该能够识别符号表中的文本。
问题:
为什么我的令牌应该包含一个查找索引,而不是包含文本本身?
那不是更快吗…
当查找索引是字典的值时,解析器应该如何快速找到符号表中的文本值?
(我不能将查找索引作为字典键,因为这样lexer就必须检查字典的值,但是字典的性能也不是很好)
多索引词典可以解决这个问题吗?我想不是…
那么我必须为每种文字类型创建一个符号表吗?
F.E.:Dictionary<int literal, int index>
以及Dictionary<double literal, int index>
以及Dictionary<char literal, int index>等。
也许我完全走错了文字的道路。请随意发布任何更好的解决方案。

最佳答案

为什么我的令牌应该包含一个查找索引,而不是包含文本本身?那不是更快吗?
当然,可能会更快。但是每个文字都是不同的值。现在,大多数程序员都期望,如果他们在同一个程序中使用两次,例如"this longish string",编译器将足够聪明,只在最终可执行文件中发出该字符串的一个副本。如果你在反编译代码的时候,发现273个不同的存储位置,因为编译器每次看到1,都会创建一个新的常量,我们应该说,这也会很惊讶。
确保常量文本只发出一次的最简单方法是将它们保存在由文本值索引的关联容器中。
正如@sepp2k在命令中指出的那样,大多数硬件允许使用小整数常量作为直接操作数,有时甚至不允许使用这么小的常量。所以上面关于常数1的陈述有点恼火。您很可能能够以不同的方式处理整数,但这可能不值得麻烦。
当查找索引是字典的值时,解析器应该如何快速找到符号表中的文本值?
这在很大程度上取决于用于文字表的精确数据结构(我不喜欢称之为符号表,但必须承认概念是相关的)。在许多语言中,您会发现您的标准库容器与问题并不完全匹配,因此您要么需要使它们适应目的,要么编写替换项。
不过,这并不复杂。一种可能性是使用aa += 1和amap<literalType, int>的组合。在这里,映射将文字值与索引关联到向量中。找到新的文字值后,将其输入到与矢量当前大小相关联的映射中,然后将该值推到矢量上(这将使其索引与刚刚插入到映射中的索引相对应)。
对于像字符串这样的大型常量,这并不完全理想,因为在映射中的键和向量中的值之间,常量被存储两次。当你开始的时候,我建议你不要再为这种重复而烦恼了;以后,如果它被证明是个问题,你可以找到解决办法。
如果使用C++,则可以使用(无序的)集合而不是映射,并使用引用(指针)到新添加的元素而不是索引。但我不认为这个特性在很多语言中都可用,而且指针与索引相比有时也很笨拙。在某些语言中,您可以将所有值放入向量中,然后保留一个集合,该集合的键是向量中的索引。这要求可以使用键类型以外的其他类型来查找集合;由于某些原因,此功能在极少数的数据结构库中可用。
而且,是的,如果您手头有一个双索引的数据结构,那么就可以使用它。(实际上,map+vector解决方案是一种双索引数据结构。)
那么我必须为每种文字类型创建一个符号表吗?
也许吧。你有多少种文字?
您可能最终会使用类型标记的枚举(“区分联合”)来处理变量和常量。(同样,并不是所有语言的标准库中都有歧视联合,这真的很糟糕;如果您的实现语言缺少这一基本功能,则需要实现它。)在关联数据结构中使用歧视联合实例作为键肯定是可能的,因此没有什么可以阻止您,原则上,不要将所有文本都保存在一个数据结构中。如果你有合适的类型,那绝对是我推荐的,至少在开始的时候。
请注意,当您最终将文本作为对象代码发送时,您实际上对它们的位表示和对齐比语义更感兴趣。如果完全不同类型的两个常量碰巧具有相同的位表示,则可以对它们使用相同的存储位置。如果您有多个宽度的整数数据类型,那么您可能希望将它们全部保存在一个文本表中,以便准确地利用此优化。无需存储每个宽度的vector<literalType>:)。有时,您会发现其他情况下,两个不同类型的文字具有相同的表示,但它可能不够常见,不足以用您的方式来处理它。(但是,在ieee硬件上,浮点和整数零的表示方式相同,通常与空指针的表示方式相同,因此您可能需要特殊大小写零。)
总之,这是一个判断的电话。使用受歧视的联合作为密钥有多复杂?使用具有特定键类型的关联容器可以节省多少存储空间,这是否重要?您想在同一类型的所有文字常量上迭代吗(答案:可能是这样的),这对您的数据结构来说有多容易?
如果使用设计良好的内部api,您将能够改变对文字表的内部表示的看法。所以我会利用这个实验来尝试好的api设计。
还有别的吗?
祝你的项目顺利。学习和享受!

关于c# - C#Dragon Book(词法分析)如何处理文字,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37094477/

10-13 09:40