tree-sitter介绍
以下内容来自于官方文档:tree-sitter.github.io/tree-sitter…
Tree-sitter 是一个解析器生成工具和增量解析库,用于为源代码文件构建具体的语法树,并在源文件编辑时高效更新语法树。它旨在提供一个通用、快速且鲁棒的解决方案,用于解析编程语言,即使在存在语法错误的情况下也能正常工作。
主要特点:
- 通用性:能够解析任何编程语言。
- 高效性:足够快,可以在文本编辑器中每按一个键就进行解析。
- 鲁棒性:即使有语法错误,也能提供有用的结果。
- 无依赖:运行时库使用纯 C11 编写,可以嵌入到任何应用程序中。
工作原理:
Tree-sitter 生成解析器并维护一个增量解析库,随着源文件的编辑实时更新语法树,从而支持如文本编辑器中的实时解析。
支持的语言:
- 语言绑定:支持 C# (.NET)、C++、Crystal、D、Delphi、ELisp、Go、Guile、Janet、Java (JDK 8+ 和 11+)、Julia、Lua、OCaml、Odin、Perl、Pharo、PHP、R 和 Ruby 等语言的绑定(部分绑定可能不完整或过时)。
tree-sitter的缺点
Tree-sitter 不是利用编程语言(如 C++、JavaScript 等)的现有或官方解析器来进行解析的。它是一个独立的解析器生成工具,使用自己的框架和语法定义来为各种语言生成专属的解析器。这些解析器基于 GLR(广义 LR)算法构建,并通过 Tree-sitter 的工具包预先编译或运行时生成,而不是依赖语言的内置运行时环境(如 V8 对于 JavaScript)。这种设计允许 Tree-sitter 在编辑器中实现高效的增量解析,但也可能导致与官方解析器在某些边缘情况下的不一致。
因此,如果需要高质量的语法解析,请不要用tree-sitter。 虽然tree-sitter提供了api让开发者编写更精细的解析,但不如考虑其他wasm方案或Language Server Protocol (LSP)
测例
以下c++代码中有4处错误,包括使用了关键字/错误的声明/使用了未定义变量/错误语法(a+++),但只识别到了最后一个。
int main()
{
int int = 1;
inb a = 1;
int a = 0, b = 1, c = 2, d = 3, e = 4;
if (x > 1)
{
}
a++ + ;
if (a || (b < c && e >= d))
{ /* ... */
}
return 0;
}
playground
tree-sitter.github.io/tree-sitter…
在浏览器环境中使用tree-sitter
tree-sitter支持在浏览器环境中使用,方法也很简单。
安装依赖
npm install web-tree-sitter
将
生成wasm
语法校验需要对应语言的wasm,生成步骤如下:
- 安装依赖
npm install --save-dev tree-sitter-cli tree-sitter-cpp
将node_modules中的web-tree-sitter.wasm文件复制到public目录下 2. 执行命令,在当前目录下生成tree-sitter-cpp.wasm
npx tree-sitter build --wasm node_modules/tree-sitter-cpp
3. 将生成的tree-sitter-cpp.wasm放入public目录下
实践demo
代码如下:
import { code } from "./code";
import { Parser, Language, Query } from "web-tree-sitter";
async function main() {
await Parser.init({
locateFile(scriptName: string, scriptDirectory: string) {
return scriptName;
},
});
const cpp = await Language.load("tree-sitter-cpp.wasm");
const parser = new Parser();
parser.setLanguage(cpp);
const tree = parser.parse(code);
console.log(tree);
console.log(tree?.rootNode);
if (!tree!.rootNode.hasError) {
console.log("没有错误");
return;
} else {
console.log("存在错误");
}
// 异常查询
const queryString = "(ERROR) @error-node (MISSING) @missing-node";
const language = parser.language;
const query = new Query(language!, queryString);
const root = tree!.rootNode;
// Execute query and get matches
const matches = query.matches(root!);
const errorNodes = [];
for (const match of matches) {
for (const capture of match.captures) {
errorNodes.push(capture.node);
}
}
console.log(errorNodes);
if (errorNodes.length) {
const { row, column } = errorNodes[0]!.startPosition;
console.log(`${row + 1}行,${column + 1}列存在错误`);
}
}
main();
错误节点捕获: