❓问题1:grammar.js
不是用正则表达式 /.../
吗?为什么有 'print'
这样的字符串?
✅ 回答:
grammar.js
分成两类“终结符”表示法:
表达方式 | 含义 |
---|---|
'xxx' | 直接匹配该字符串字面量 |
/regex/ | 匹配符合正则的文本 |
💡 举例说明:
'print' // 匹配文本里正好是 "print" 这个词
/[a-zA-Z_]+/ // 匹配任何标识符(如变量名)
Tree-sitter 支持 literal 字符串 和 正则表达式,它们有不同的用途:
'='
、'let'
、'function'
等是关键词或符号,通常写成字符串字面量- 正则主要用来定义通用格式,比如数字、标识符、字符串、注释
❓问题2:token(...)
有什么用?为什么不用正则就行了?
✅ 核心回答:
你写 /.../
只是写了个规则,tree-sitter 默认会尝试进一步拆解规则内容。
而 token(...)
的作用是:
告诉 Tree-sitter:这个部分是词法单位,不要对它再做语法结构解析了!
📌 举个例子:
🔴 没有 token
的情况:
number: $ => /\d+\.\d+/
Tree-sitter 会试图把这个规则拆开,比如它会试着把 123.45
拆成:
123
.
45
这样它就可能匹配错,或者让你写更多优先级规则去处理冲突,非常麻烦。
✅ 使用 token
:
number: $ => token(/\d+\.\d+/)
Tree-sitter 就会说:
好的,我不拆了,我直接把
123.45
当成一个整体number
了!
✅ 总结:token(...)
用于匹配词法单位(不可再拆分的基本块),防止 tree-sitter 把内容继续“语法解析”。
❓那关键词比如 seq
、choice
等就不能省略吗?我直接 /regex/
一行行写正则不行吗?
✅ 回答:
你当然可以全写正则,但:
- 正则只能写“单个词法单元”,比如标识符、数字、注释等;
seq
、choice
等是用来构建**语法结构(句法规则)**的。
换句话说:
正则是词法(Lexical),而
seq/choice/...
是句法(Syntactic)
📌 比如:
// 正则可以写变量名
identifier: $ => /[a-zA-Z_][a-zA-Z0-9_]*/// 但你要写 let x = 123; 就必须用结构化方式:
variable_declaration: $ => seq('let',$.identifier,'=',$.number,';'
)
✅ 总结一波三问:
问题 | 回答 |
---|---|
grammar.js 只能写 /.../ 吗?为什么还有 '...' | '...' 是字符串字面量,比如关键字、符号等 |
为什么还要 token(...) ,不用正则不行吗? | token(...) 告诉 tree-sitter:这是一个不可再分的词法单元 |
能只写正则不写 seq/choice/optional 吗? | 正则只能写“单元”,构建结构必须用 seq 等关键词 |
你现在完全可以尝试自己写个 grammar.js
的小文件啦!想不想我们来练一下,比如:解析 let x = 1;
或者 print("hello")
?
只要你告诉我你想写哪种结构,我可以边写边讲,完全按你节奏来 😎