【问题标题】:Producing squiggles using your abstract-syntax-tree使用您的抽象语法树生成曲线
【发布时间】:2011-05-06 08:24:42
【问题描述】:

情况:

我已经为一种很少使用的编程语言(我的一个爱好项目)制作了一个扫描器、解析器和各种 AST 类。解析器在扫描仪的帮助下构建了一个异构 AST,我对其进行了一些操作。在过去,我为一些 IDE 创建了一个插件/加载项,用于语法高亮和一些其他元素。

问题在于错误:解析器生成一些并可以访问构成语句的标记。然而,一些错误只会在以后出现,例如无法解析标识符。我想在此类标识符或其他错误标记下显示曲线。不仅如此,我喜欢在不丢失原始文档中所有 cmets、间距等的情况下操纵 AST 节点的能力。

在我的 AST 中创建新语句时,我可以轻松地将构成该语句的标记添加为子项。但是……

问题:

如果合理可行,我希望包括对显示曲线的支持。这需要一个语句知道组成语句的标记的位置。不幸的是,一个语句有变体,有时包含更多的标记,有时包含更少的标记。如果我正在制作只读 AST,这将不是问题。但是,我希望我的 AST 可以读写以进行重构!这意味着更改 AST 中的语句本质上意味着添加标记(语句的子级),因此 Statement 类应该能够自我解析。

这会污染 AST 解析代码并且不再保持关注点分离!

技术细节:

另一种方法是将 AssignmentStatement 变成一个工厂,获取一组标记,生成语句的一个实例,并不断让它知道自己的标记。

在我的情况下,用于分配的 AST 节点目前基本上是这样的:

分层样本 AST 可以是:

AssemblyDeclaration
    .. Statement ..
    .. Statement ..
    ClassDeclaration
       .. Token .. // one or more that make up the entire class statement
       .. Token .. // one or more that make up the entire class statement
       Statement(s)
         .. Token .. // Which have their own tokens that make up the statement .. and possibly have sub-nodes of their own such as Expressions which have -their- own Tokens that comprise it.

基本 ast 节点的概念性想法,语法树中所有内容的父节点,无论它是类声明、语句还是标记。

public abstract class BaseAstNode : IList<BaseAstNode>
{
    ... implementation of IList<BaseAstNode>
    ... implementation of Visitor Pattern
    ... implementation of Clonable
}    

public sealed class AssignmentStatement : BaseAstNode 
{
public Expression Expression { get; set; }; // Setting this will alter the Tokens (children!) of this node, possibly even ADD Tokens!
public TypeReference Target { get; set; }
}

public sealed class PrimitiveNumberExpression : Expression // is a BaseAstNode
{
public int Value { get; set; } // Setting this will alter the Tokens (children!) of this node!
}

public abstract class Token : BaseAstNode
{
    public Layers Layer { get; set; }
    public TokenType TokenType { get; set; }
    public int Column { get; set; }
    public int Line { get; set; }
    public int Position { get; set; }
    public abstract int Length { get; set; }
    public virtual string Value { get; set; }
    public override string ToString(){}
}

其他人如何解决这个问题?这是正确的方法吗?

【问题讨论】:

  • 你当前的代码是如何构造AssignmentStatement对象的?
  • var stmt = new AssignmentStatement { Expression = Expression.Null, Target = TypeReference.Null }; .. 只有解析器产生的表达式和引用。
  • 我想我的问题是如何为成员填充数据,为什么类也不能填充起始和长度值?
  • 语句是语法树的一部分,由解析器填充。每个语句都可以轻松获得开始和结束标记。但是我仍然不能在我的标识符下面加上波浪线,除非我开始跟踪语句组成的所有标记和 -their- 的位置。

标签: c# .net parsing abstract-syntax-tree


【解决方案1】:

如果您可以获得发生错误的语句的代码,您应该能够重新解析单个语句,这次跟踪令牌的位置并注意那些无效的(因为您没有令牌的位置第一次保存)。至少您不必解析整个文档,只需一行...也许可以重新使用您已经拥有的一些解析代码。

或者,do 存储每个标记的位置(文档中的开始和长度),以便您在需要参考其来源时可以轻松访问该标记的位置。为什么不这样做?

如果您可以从令牌对象重新生成令牌文本,一些可能的优化将是不存储令牌长度,或者只是在行内搜索该文本,假设您想对每个在行(或所有行)上出现该标记。

编辑: 现在您已将标记链接到解析它们的原始源代码,您需要将语句链接到解析它们的标记。有两种方法可以做到这一点:

  1. 将 List 成员添加到 BaseAstNode。或
  2. 为每个派生语句类添加特定的 Token 成员,这样您就可以准确地知道哪个令牌链接到语句的哪个部分。

现在您有了一条从语句到源代码的链,因此您可以识别与每个语句的每个部分相关联的源代码。

编辑 2: 如果您试图弄清楚实际解析如何将令牌处理为 BaseAstNode 派生对象,我还没有考虑过这个问题(不清楚这是问题的一部分)。由于我以前没有这样做过,所以我的建议在第一次尝试时可能不是最好的。但希望它可以根据需要进行改进。我的第一个想法将我引向 System.Xml 命名空间,它也实现了一个解析器。也许您的某些设计可以仿照 XML 解析器和相关类。 XmlNode 可能类似于您的 BaesAstNode 类。 XmlElement 可能类似于您的 Token 类。 XmlDocument 是 XmlNode 的一种特殊化,它在一个 LoadXml 函数中处理所有解析,该函数使用子对象填充对象。

因此,按照 Xml 命名空间的示例,您可以使 ParseCode 成为 ClassDeclaration 的成员,该成员使用所有适当的子对象填充对象。它可以接受字符串或令牌的有序集合作为输入,并将 BaseAstNode 的派生实例添加到存储在类中的 BaseAstNode 对象的有序集合中。但并非所有解析都需要在一个函数中。当您解析文本或标记时,我假设您将从 ParseCode 函数开始,将解析的标记推送到堆栈或队列中,直到您解析了足够的标记以唯一地识别您正在解析的语句类型,然后传递那些排队的标记和解析器的当前状态到语句类之一上的适当派生解析器。当派生类完成解析定义语句的标记时,标记将被添加到语句中,然后调用代码会将解析的语句添加到语句的父集合中。

【讨论】:

  • 我已经重写了我的问题,以包括您要求的详细信息以及为清楚起见进行的一些额外编辑。如果我所描述的是您的建议,我还有一些关于谁负责将令牌解析和跟踪到 ast-nodes 的问题。看看新信息!
  • 这看起来非常接近我的想法。我可能不会独立存储位置和列;如果我什至需要两者,我可能会从另一个和原始文本中计算一个。老实说,我以前没有这样做过,但我通常可以解决新问题:)。那么还有什么问题......之前谁负责解析令牌,为什么必须改变?
  • 实际上更仔细地观察它,我有点想你想要跟踪所有东西 (BaseAstNode) 的位置,而不仅仅是标记。但如果你认为代币是你唯一关心的位置,你可以按照你的建议去做。
  • 一点也不——我确实想跟踪所有内容。空白、注释、语言元素。所有这些都是令牌。这样的语句将由空格、注释、行尾和语言标记组成。令牌(ast)节点是具有 Length 的节点,因此确定行和列。至于解析——如果一个语句节点应该能够理解它自己的元素,它必须将这堆组成自己的标记解析成一个有效的语句,记住可能存在或不存在的可选关键字。
  • 我想我很难理解像 AssignmentStatement 和 Token 这样的对象之间的关系。 AssignmentStatement 是不是从 token 又提升了一个级别,这样您首先将所有内容解析为 token,然后将它们解析为更高级别的类,但它们都是从 BaseAstNode 派生的?而且等级之间没有关系?您是否考虑过在更高级别之间添加关系,就像您刚刚在 Tokens 和原始来源之间添加一样?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多