使用此库,语法的抽象语法树将成为一个自定义对象,您可以以任何您喜欢的方式遍历它。
假设,这些是我们的 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!
}