通用语法校验器tree-sitter——C++语法校验实践

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+++),但只识别到了最后一个。

cpp 复制代码
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,生成步骤如下:

  1. 安装依赖
css 复制代码
npm install --save-dev tree-sitter-cli tree-sitter-cpp

将node_modules中的web-tree-sitter.wasm文件复制到public目录下 2. 执行命令,在当前目录下生成tree-sitter-cpp.wasm

bash 复制代码
npx tree-sitter build --wasm node_modules/tree-sitter-cpp
  1. 将生成的tree-sitter-cpp.wasm放入public目录下

实践demo

代码如下:

js 复制代码
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();

错误节点捕获:

相关推荐
夜郎king4 分钟前
HTML5 SVG 实现日出日落动画与实时天气可视化
前端·html5·svg 日出日落
夏幻灵1 小时前
HTML5里最常用的十大标签
前端·html·html5
Mr Xu_1 小时前
Vue 3 中 watch 的使用详解:监听响应式数据变化的利器
前端·javascript·vue.js
未来龙皇小蓝1 小时前
RBAC前端架构-01:项目初始化
前端·架构
程序员agions1 小时前
2026年,微前端终于“死“了
前端·状态模式
万岳科技系统开发2 小时前
食堂采购系统源码库存扣减算法与并发控制实现详解
java·前端·数据库·算法
程序员猫哥_2 小时前
HTML 生成网页工具推荐:从手写代码到 AI 自动生成网页的进化路径
前端·人工智能·html
龙飞052 小时前
Systemd -systemctl - journalctl 速查表:服务管理 + 日志排障
linux·运维·前端·chrome·systemctl·journalctl
我爱加班、、2 小时前
Websocket能携带token过去后端吗
前端·后端·websocket
AAA阿giao2 小时前
从零拆解一个 React + TypeScript 的 TodoList:模块化、数据流与工程实践
前端·react.js·ui·typescript·前端框架