tree-sitter 的 grammar.js 编写方法
- 一、`grammar.js` 的作用是什么?
- 二、基本结构
- 三、关键词解释
- 四、编写小技巧
- 1. 起点是 `source_file`
- 2. 所有规则名(如 `identifier`, `number`)都是 `$ => ...`
- 3. 正则表达式用于定义词法规则(终结符),例如:
- 五、🍀 `$`的简单解释:`$` 是规则的“引用工具”
- ❓ 那 `$` 到底能干嘛?
- 🧠 举个例子来说明
- 🔧 类比一下:
要掌握
tree-sitter
的
grammar.js
编写方法,首先我们要理解 tree-sitter 的核心概念,它是一个
基于GLR算法的增量解析器生成工具,常用于语法高亮、代码分析等任务。
一、grammar.js
的作用是什么?
这是 tree-sitter 语法的定义文件,描述了一个语言的 词法规则(Lexing) 和 语法规则(Parsing)。
二、基本结构
grammar.js
是一个 CommonJS 模块,通常长这样:
module.exports = grammar({
name: 'my_language', // 语言名称
// 词法规则
extras: $ => [/\s/, $.comment], // 忽略的内容,如空格、注释
// 定义词法(token)
rules: {
source_file: $ => repeat($.statement),
statement: $ => choice(
$.variable_declaration,
$.function_call
),
variable_declaration: $ => seq(
'let',
$.identifier,
'=',
$.expression,
';'
),
function_call: $ => seq(
$.identifier,
'(',
optional($.expression),
')',
';'
),
expression: $ => choice(
$.number,
$.string,
$.identifier
),
identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/,
number: $ => /\d+/,
string: $ => /"[^"]*"/,
comment: $ => /\/\/.*/
}
});
三、关键词解释
seq(...)
: 顺序匹配(sequence),表示一段连续的符号。choice(...)
: 匹配多个可能之一(like OR)。repeat(...)
: 重复0次或多次。repeat1(...)
: 重复至少1次。optional(...)
: 可选项。token(...)
: 表示这是一个词法单元(token),不会再细分。prec(n, rule)
: 设置优先级(n
越大,优先级越高)。
四、编写小技巧
1. 起点是 source_file
tree-sitter 会从你定义的 rules.source_file
开始解析整个文件。
2. 所有规则名(如 identifier
, number
)都是 $ => ...
这个 $
是构造语法树时的“工具”,让你可以引用别的规则。
3. 正则表达式用于定义词法规则(终结符),例如:
identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/,
number: $ => /\d+/,
五、🍀 $
的简单解释:$
是规则的“引用工具”
在 grammar.js
中,你写的每一个语法规则都是一个函数,比如这样:
identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/
这里的 $ =>
就是给 tree-sitter 传递一个函数,这个函数的参数 $
是个“语法引用器”。
❓ 那 $
到底能干嘛?
它能让你“引用其他规则”。
🧠 举个例子来说明
比如你想定义这样一个语句:
let x = 42;
你在写规则时会写成:
variable_declaration: $ => seq(
'let',
$.identifier,
'=',
$.expression,
';'
)
这里的 $.identifier
和 $.expression
就是**“引用”之前定义的两个规则**。注意:
$.identifier
-> 指向你之前写的identifier: $ => ...
$.expression
-> 指向你之前写的expression: $ => ...
这样 tree-sitter 就能在 variable_declaration
这个规则里,使用别的规则了。
🔧 类比一下:
你可以把 rules 想象成一个对象:
rules = {
identifier: ...,
expression: ...,
variable_declaration: ...
}
那 $.identifier
就相当于 rules.identifier
。
所以这个 $
就是代表你整个 rules 对象本身。