SwordScript - 使用C#开发脚本语言(四)抽象语法树

628 阅读2分钟

本章节对应仓库

2.抽象语法树 Github

什么是抽象语法树

抽象语法树是一种用于表示语法的树,简而言之,我们输入的各种运算符、操作符、表达式,经过解析后,形成一种树形结构,树上的每个节点都表示源代码中的一种结构。

例如,a = b * (3 + 5)在语法树里,表示为:

graph TD
= --> a
= --> *
* --> b
* --> +
+ --> 3
+ --> 5

通过将语法转换为树,对于语法解析会更为方便。

设计抽象语法树

classDiagram
ASTNode <|-- ASTLeaf
ASTNode <|-- ASTList
IEnumerable~ASTNode~ *.. ASTList
class ASTLeaf{
    
}
class ASTList{
    +IReadOnlyList~ASTNode~ Children
    +this(int i) ASTNode
}
class IEnumerable~ASTNode~{
    <<interface>>
}

语法树的基类为ASTNode,目前基类没有提供任何的抽象函数或方法

ASTLeaf代表叶节点,底下没有其他节点

ASTList代表枝干,底下还有其他节点

ASTNode.cs

namespace SwordScript;

public abstract class ASTNode
{
    
}

ASTLeaf.cs

namespace SwordScript;

public abstract class ASTLeaf : ASTNode
{
    
}

ASTList.cs

using System.Collections;
using System.Collections.Generic;

namespace SwordScript;

public abstract class ASTList : ASTNode, IEnumerable<ASTNode>
{
    public ASTList()
    {
        
    }
    
    public ASTList(IEnumerable<ASTNode> list)
    {
        _children.AddRange(list);
    }
    
    private List<ASTNode> _children = new List<ASTNode>();
    public IReadOnlyList<ASTNode> Children => _children;
    public ASTNode this[int i] => _children[i];
    public IEnumerator<ASTNode> GetEnumerator()
    {
        return _children.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

叶节点

叶节点通常代表着没有子物体的节点,在这里,叶节点可以分为两种:代表实体的标识符节点、代表基础类型的字面量节点

标识符节点

ASTIdentifier.cs

namespace SwordScript;

public class ASTIdentifier : ASTLeaf
{
    public ASTIdentifier(string name)
    {
        Name = name;
    }
    
    public string Name { get; }

    public override string ToString()
    {
        return Name;
    }
}

标识符节点相对比较简单,只需要能够获取标识符字符串即可。

字面量节点

根据上一章节的定义,可以得知目前有整形、浮点、字符串、布尔、空类型五种类型

对这五种类型分别创建对应的字面量节点

ASTLiteral.cs

using System.Globalization;

namespace SwordScript;

public abstract class ASTLiteral : ASTLeaf
{
    
}

public class ASTLongIntegerLiteral : ASTLiteral
{
    public ASTLongIntegerLiteral(long value)
    {
        Value = value;
    }
    
    public long Value { get; }

    public override string ToString()
    {
        return Value.ToString();
    }
}

public class ASTDoubleFloatLiteral : ASTLiteral
{
    public ASTDoubleFloatLiteral(double value)
    {
        Value = value;
    }
    
    public double Value { get; }

    public override string ToString()
    {
        return Value.ToString(CultureInfo.InvariantCulture);
    }
}

public class ASTStringLiteral : ASTLiteral
{
    public ASTStringLiteral(string value)
    {
        Value = value;
    }
    
    public string Value { get; }

    public override string ToString()
    {
        return """ + Value + """;
    }
}

public class ASTBooleanLiteral : ASTLiteral
{
    public ASTBooleanLiteral(bool value)
    {
        Value = value;
    }
    
    public bool Value { get; }

    public override string ToString()
    {
        return Value.ToString().ToLower();
    }
}

public class ASTNullLiteral : ASTLiteral
{
    public ASTNullLiteral()
    {
    }
    
    public override string ToString()
    {
        return "null";
    }
}

枝干节点

枝干节点代表着底下有其他节点的节点,例如二元表达式,就是此类。

1+2

graph TD
+ --> 1
+ --> 2

+号节点便是一个二元表达式。

二元表达式

先定义二元表达式节点基类,具体的表达式节点待下一章定义。

ASTBinaryExpr.cs

namespace SwordScript;

public abstract class ASTBinaryExpr : ASTList
{
    public ASTBinaryExpr(ASTNode left, ASTNode right) : base(new []{left, right})
    {

    }

    public ASTNode Left => this[0];
    
    public ASTNode Right => this[1];
}

结语

在本章中,我们定义了一些基本的抽象语法树节点,这些节点目前还没有实际意义。但是在下一章中,我们就会使用语法解析把语句机械成语法树。