情境:
我已经为一个很少用的编程语言制作了一个扫描器、解析器和各种AST类,这是我的一个业余项目。解析器在扫描器的帮助下构建了一个异构AST,我对其进行一些操作。过去,我曾为一些IDE创建了语法高亮插件/附加组件以及其他一些元素。
问题在于错误:解析器会生成一些错误,并且可以访问构成语句的标记。然而有些错误只会在后来出现,比如无法解析标识符。我想在这样的标识符或其他有问题的标记下显示波浪线。不仅如此,我还希望能够在不失去原始文档中所有注释、空格等内容的情况下操纵我的AST节点。
在我的AST中创建一个新的Statement时,我可以轻松地将组成该Statement的标记添加为子项。但是……
问题:
如果合理可行,我希望包括支持显示波浪线。这需要语句知道构成该语句的标记的位置。不幸的是,语句有一些变化,有时包含更多的标记,有时少些标记。如果我正在创建一个只读的AST,那么这将不是问题。但是,出于重构目的,我希望我的AST可以进行读写操作!这意味着在AST中更改语句基本上意味着添加标记(也就是Statement的子项),因此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(){}
}
别人是怎么解决这个问题的?这是正确的方式吗?