AST(抽象语法树)

使用此库,语法的抽象语法树将成为一个自定义对象,您可以以任何您喜欢的方式遍历它。

声明规则

假设,这些是我们的 AST 节点类型

struct Expression : ASTNode {
    let char : Character
}

struct CommaSeparatedexpressions : ASTNode {
    var exprs : [Expression]
}

我们的目标是识别一个逗号分隔的表达式列表,为了一个简单的例子,表达式只是字符。

让我们声明必要的规则

// List -> <List> ',' <Expr>

struct RecNextIsList : Constructor {
    
    @NonTerminal
    var recognized : CommaSeparatedexpressions
    
    @Terminal var separator = ","
    
    @NonTerminal
    var next : Expression
    
    func onRecognize(context: Context) throws -> CommaSeparatedexpressions {
        recognized.exprs.append(next)
        return recognized
    }
    
}

// List -> ''

struct EmptyIsList : Constructor {
    
    func onRecognize(context: Context) throws -> CommaSeparatedexpressions {
        CommaSeparatedexpressions(exprs: [])
    }
    
}

// List -> <Expr>
// we need this rule because we used left recursion and allow empty lists
// without this rule our lists would have to look like ",a,b,a"


struct ExprIsList : Constructor {
    
    @NonTerminal
    var expr : Expression
    
    func onRecognize(context: Context) throws -> CommaSeparatedexpressions {
        CommaSeparatedexpressions(exprs: [expr])
    }
    
}

// Expr -> <any characters in our alphabet>


struct CharIsExpr : Constructor {
    
    var ruleName: String {
        "Char \(char)" // we need to distinguish the rules for each character
    }
    
    @Terminal var char : Character
    
    func onRecognize(context: Context) throws -> Expression {
        Expression(char: char)
    }
    
}

我们现在可以将这些规则总结成一个语法

struct ListGrammar : Grammar {
    var constructors: [any Constructors<Context>] {
        [ExprRules(), ListRules()]
    }
    struct ExprRules : Constructors {
        typealias Output = Expression
        typealias Ctx = Context
        @Case var a = CharIsExpr(char: "a")
        @Case var b = CharIsExpr(char: "b")
        @Case var c = CharIsExpr(char: "c")
    }
    struct ListRules : Constructors {
        typealias Output = CommaSeparatedexpressions
        typealias Ctx = Context
        @Case var recursion = RecNextIsList()
        @Case var empty = EmptyIsList()
        @Case var expr = ExprIsList()
    }
}

最后,让我们运行一个测试!

    func testList() throws {
                
        let parser = try Parser.CLR1(rules: ListGrammar(), goal: CommaSeparatedexpressions.self)
        
        try XCTAssertEqual(parser.parse("a,b,a").get().exprs.map(\.char), ["a", "b", "a"]) // works!
        
        
    }