Roslyn语法节点是否重用?

时间:2012-05-02 15:44:25

标签: c# expression-trees roslyn

我一直在关注Roslyn CTP,虽然它解决了与Expression tree API类似的问题,但两者都是不可改变的,但Roslyn以完全不同的方式这样做:

  • Expression个节点没有引用父节点,使用ExpressionVisitor进行修改,这就是为什么可以重用大部件的原因。

  • 另一方面,Roslyn的SyntaxNode有一个对其父级的引用,因此所有节点都有效地成为一个无法重用的块。提供诸如UpdateReplaceNode等方法以进行修改。

这到底在哪里? DocumentProjectISolution? API促进了树的逐步更改(而不是按钮),但是每个步骤都是完整的副本吗?

他们为什么做出这样的选择?我有什么有趣的伎俩吗?

1 个答案:

答案 0 :(得分:170)

更新:这个问题是the subject of my blog on June 8th, 2012。谢谢你提出的好问题!


好问题。我们讨论了你长期以来提出的问题。

我们希望拥有一个具有以下特征的数据结构:

  • 不可变。
  • 树的形式。
  • 从子节点廉价访问父节点。
  • 可以从树中的节点映射到文本中的字符偏移量。
  • 持久性即可。

通过持久性我的意思是当对文本缓冲区进行编辑时,重用树中的大多数现有节点的能力。由于节点是不可变的,因此重用它们没有任何障碍。我们需要这个来表现;每次按键时我们都无法重新解析文件的大量文件。我们需要重新解析并重新解析受编辑影响的树的部分。

现在,当您尝试将所有这些内容放入一个数据结构时,您会立即遇到问题:

  • 您如何首先构建节点?父母和孩子都互相引用,并且是不可变的,所以首先建立哪一个?
  • 假设您设法解决了这个问题:如何让它持久化?您不能在另一个父级中重用子节点,因为这会告诉孩子它有一个新父级。但孩子是不变的。
  • 假设您设法解决该问题:当您将新字符插入编辑缓冲区时,映射到该点之后的位置的每个节点的绝对位置都会发生变化。这使得构建持久数据结构变得非常困难,因为任何编辑都可以改变大多数节点的跨度!

但在Roslyn团队中,我们经常做不可能的事情。我们实际上通过保留两个解析树来完成不可能的事情。 “绿色”树是不可变的,持久的,没有父引用,是“自下而上”构建的,每个节点都跟踪其宽度而不是绝对位置。当编辑发生时,我们只重建受编辑影响的绿树部分,通常是树中总解析节点的O(log n)。

“红色”树是一个围绕绿树建立的不可变 facade ;它是按需“自上而下”构建的并在每次编辑时丢弃。当您从顶部下降树时,它通过按需制造它来计算父引用。它通过从宽度计算它们来制造绝对位置,再次,当你下降时。

你,用户,只看到红树;绿树是一个实现细节。如果您查看解析节点的内部状态,您实际上会看到在另一个类型中存在对另一个解析节点的引用;这是绿树节点。

顺便提一下,这些被称为“红/绿树”,因为它们是我们在设计会议中用于绘制数据结构的白板标记颜色。颜色没有其他意义。

这种策略的好处是我们可以获得所有这些伟大的东西:不变性,持久性,父引用等等。成本是这个系统很复杂,如果“红色”外墙变大,可能会占用大量内存。我们目前正在做实验,看看我们是否可以在不损失收益的情况下降低部分成本。