tree-sitter編寫parser,用external scanner實現eof規則

laditor發表於2024-12-03

介紹和分析

tree-sitter是一個parser生成工具,用於生成語法樹。tree-sitter parser官網英文教程一篇優秀tree-sitter的中文介紹

eof規則表示從當前位置開始,匹配空字元直到檔案結尾,中途任何的非空字元都會導致匹配失敗。

tree-sitter本身不提供eof規則,以下是使用external scanner功能實現的eof

程式碼實現

.代表專案根目錄
記得將程式碼中的your_language替換成你的語言名稱

./binding.gyp 檔案中

{
  "targets": [
    {
      ...
      "sources": [
        "bindings/node/binding.cc",
        "src/parser.c",
        "src/scanner.c", // 新增這一行
      ],
      ...
    }
  ]
}

scanner.c 檔案需要手動建立
external scanner的具體規則用法參考 tree-sitter external scanner

./src/scanner.c

#include "tree_sitter/alloc.h"
#include "tree_sitter/array.h"
#include "tree_sitter/parser.h"

static bool scan_eof(TSLexer *lexer);

enum TokenType { Eof };

void *tree_sitter_your_language_external_scanner_create(void) { return NULL; }

void tree_sitter_your_language_external_scanner_destroy(void *payload) {}

unsigned tree_sitter_your_language_external_scanner_serialize(void *payload,char *buffer) {
  return 0;
}

void tree_sitter_your_language_external_scanner_deserialize(void *payload,const char *buffer,unsigned length) {}

bool tree_sitter_your_language_external_scanner_scan(void *payload, TSLexer *lexer,const bool *valid_symbols) {
  if (valid_symbols[Eof]) {
    return scan_eof(lexer);
  }
  return false; // 匹配失敗
}

bool scan_eof(TSLexer *lexer) {
  // 標記匹配開始位置
  lexer->mark_end(lexer);

  while (!lexer->eof(lexer)) {
    // 檢查是否有非空字元,具體忽略哪些空字元可以在這裡指定
    if (lexer->lookahead != ' ' && lexer->lookahead != '\n' &&
        lexer->lookahead != '\r' && lexer->lookahead != '\t' &&
        lexer->lookahead != '\f' && lexer->lookahead != '\v') {
      return false; // 匹配失敗
    }
    lexer->advance(lexer, true); // 消耗字元
    lexer->mark_end(lexer);      // 動態更新結束位置
  }

  // 到達檔案末尾,匹配成功
  lexer->result_symbol = Eof;
  return true;
}

最後在external中宣告新增的規則,就可以使用這個規則了

./grammer.js

module.exports = grammar({
  name: "your_language",

  externals: $ => [
    $.eof,
  ],

  rules: {
    source_file: $ => seq(
      "world",
      $.eof
    ), // 一個簡單的示例
  }

執行

建立一個簡單的文字檔案example.txt,結尾可以加上任意數量的空字元

world

控制檯執行命令

tree-sitter generate
tree-sitter parse ./example.txt

得到以下輸出,說明eof規則執行成功

(source_file [0, 0] - [6, 0]
  (world [0, 0] - [0, 5])
  (eof [6, 0] - [6, 0]))

相關文章