Swift Tree Sitter (Swift 树状结构分析器)

此模块为 tree-sitter 解析库提供 Swift 绑定。

安装

使用 Swift 包管理器

将其作为依赖项添加到 Package.swift 文件中。

.package(url: "https://github.com/viktorstrate/swift-tree-sitter", from: "1.0.0")

或者从 Xcode 导航到 File -> Swift Packages -> Add Package Dependency...,然后输入此 URL

https://github.com/viktorstrate/swift-tree-sitter

直接导入到 Xcode

如果你想在运行时从 .bundles 动态加载语言,你必须直接将其导入到 Xcode,因为 Swift 包管理器不支持 Mac bundle。

要做到这一点,请下载项目并将包含 SwiftTreeSitter.xcodeproj 文件的文件夹拖到 Xcode 项目的侧边栏中。

用法

首先,你需要设置 Parser 并指定要使用的语言。

let javascript = try STSLanguage(fromPreBundle: .javascript)
let parser = STSParser(language: javascript)

然后你可以解析一些源代码。

let sourceCode = "let x = 1; console.log(x);";
let tree = parser.parse(string: sourceCode, oldTree: nil)!
print(tree.rootNode.sExpressionString!)

// (program
//   (lexical_declaration
//     (variable_declarator name: (identifier) value: (number)))
//   (expression_statement
//     (call_expression function:
//       (member_expression object: (identifier)
//         property: (property_identifier))
//         arguments: (arguments (identifier)))))

检查语法树。

let callExpression = tree.rootNode.child(at: 1).firstChild(forOffset: 0)
print("type:\t\(callExpression.type)")
print("start point:\t\(callExpression.startPoint)")
print("end point:\t\(callExpression.endPoint)")
print("start byte:\t\(callExpression.startByte)")
print("end byte:\t\(callExpression.endByte)")

// type:        call_expression
// start point: STSPoint(row: 0, column: 11)
// end point:   STSPoint(row: 0, column: 25)
// start byte:  11
// end byte:    25

如果你的源代码发生更改,你可以更新语法树。这将比从头重新计算树花费更少的时间。

// replace let with const
let newSourceCode = "const x = 1; console.log(x);";

tree.edit(
  STSInputEdit(
    startByte: 0,
    oldEndByte: 3,
    newEndByte: 5,
    startPoint: STSPoint(row: 0, column: 0),
    oldEndPoint: STSPoint(row: 0, column: 3),
    newEndPoint: STSPoint(row: 0, column: 5)
))

let newTree = parser.parse(string: newSourceCode, oldTree: tree)

从自定义数据源解析文本

如果你的文本存储在自定义数据源中,你可以通过将回调函数传递给 .parse() 而不是 String 来解析它。

let sourceLines = [
  "let x = 1;\n",
  "console.log(x);\n"
]

let tree = parser.parse(callback: { (byte, point) -> [Int8] in
  if (point.row >= sourceLines.count) {
    return []
  }

  let line = sourceLines[Int(point.row)]

  let index = line.index(line.startIndex, offsetBy: Int(point.column))
  let slice = line[index...]
  let array = Array(slice.utf8).map { Int8($0) }

  return array
}, oldTree: nil)