Automatic conversion between two different programming languages through two grammar files that define the same grammar structure and grammar name
npm install subhutiOr 规则按顺序尝试,第一个成功即返回
Or 规则中优雅处理失败
@SubhutiRule 定义规则,代码简洁
.cache().debug().errorHandler())
getChild(), getChildren(), getToken() 等便捷方法
[lookahead ...] 约束
bash
npm install subhuti
或
yarn add subhuti
`
🚀 快速开始
$3
`typescript
import { SubhutiLexer, createKeywordToken, createRegToken, createValueRegToken } from 'subhuti'
// 定义 Token
const tokens = [
// 关键字
createKeywordToken('IfTok', 'if'),
createKeywordToken('ElseTok', 'else'),
createKeywordToken('ReturnTok', 'return'),
// 标识符和字面量
createRegToken('Identifier', /[a-zA-Z_][a-zA-Z0-9_]*/),
createRegToken('Number', /[0-9]+/),
// 符号
createKeywordToken('LParen', '('),
createKeywordToken('RParen', ')'),
createKeywordToken('Semicolon', ';'),
// 跳过空格和注释(skip: true)
createValueRegToken('WhiteSpace', /[ \t\r\n]+/, '', true),
createValueRegToken('Comment', /\/\/[^\n]*/, '', true),
]
// 创建 Lexer
const lexer = new SubhutiLexer(tokens)
// 分词
const sourceCode = 'if (x) return 42;'
const tokenStream = lexer.tokenize(sourceCode)
`
$3
`typescript
import { SubhutiTokenConsumer } from 'subhuti'
// 自定义 TokenConsumer,为每个 token 创建便捷方法
class MyTokenConsumer extends SubhutiTokenConsumer {
IfTok() { return this.consume(tokens.find(t => t.name === 'IfTok')!) }
ElseTok() { return this.consume(tokens.find(t => t.name === 'ElseTok')!) }
ReturnTok() { return this.consume(tokens.find(t => t.name === 'ReturnTok')!) }
Identifier() { return this.consume(tokens.find(t => t.name === 'Identifier')!) }
Number() { return this.consume(tokens.find(t => t.name === 'Number')!) }
LParen() { return this.consume(tokens.find(t => t.name === 'LParen')!) }
RParen() { return this.consume(tokens.find(t => t.name === 'RParen')!) }
Semicolon() { return this.consume(tokens.find(t => t.name === 'Semicolon')!) }
}
`
$3
`typescript
import { SubhutiParser, SubhutiRule, Subhuti } from 'subhuti'
@Subhuti
class MyParser extends SubhutiParser {
constructor(tokens) {
super(tokens, MyTokenConsumer) // 传入自定义 TokenConsumer
}
@SubhutiRule
Statement() {
this.Or([
{ alt: () => this.IfStatement() },
{ alt: () => this.ReturnStatement() },
{ alt: () => this.ExpressionStatement() }
])
}
@SubhutiRule
IfStatement() {
this.tokenConsumer.IfTok() // 使用 TokenConsumer 的便捷方法
this.tokenConsumer.LParen()
this.Expression()
this.tokenConsumer.RParen()
this.Statement()
// 可选的 else 分支
this.Option(() => {
this.tokenConsumer.ElseTok()
this.Statement()
})
}
@SubhutiRule
ReturnStatement() {
this.tokenConsumer.ReturnTok()
this.Expression()
this.tokenConsumer.Semicolon()
}
@SubhutiRule
Expression() {
// 简化示例
this.Or([
{ alt: () => this.tokenConsumer.Identifier() },
{ alt: () => this.tokenConsumer.Number() }
])
}
@SubhutiRule
ExpressionStatement() {
this.Expression()
this.tokenConsumer.Semicolon()
}
}
`
$3
`typescript
const parser = new MyParser(tokenStream)
.cache(true) // 启用 Packrat 缓存
.debug(false) // 生产环境关闭调试
.errorHandler(true) // 启用详细错误信息
// 解析
const cst = parser.Statement()
// 访问 CST
if (cst) {
console.log('规则名称:', cst.name)
console.log('子节点数量:', cst.childCount)
// 使用便捷方法访问
const condition = cst.getChild('Expression')
const returnValue = cst.getToken('Number')
// 访问位置信息(用于错误报告、源码映射)
console.log('位置:', cst.loc.start.line, cst.loc.start.column)
}
`
📖 核心能力
$3
#### Or - 顺序选择(规则顺序很重要!)
`typescript
this.Or([
{ alt: () => { / 长规则:优先尝试 / } },
{ alt: () => { / 短规则:作为回退 / } }
])
`
⚠️ 关键原则:长规则必须在短规则前面
#### Many - 0 次或多次
`typescript
this.Many(() => {
this.Statement()
})
`
#### AtLeastOne - 1 次或多次
`typescript
this.AtLeastOne(() => {
this.Parameter()
})
`
#### Option - 0 次或 1 次
`typescript
this.Option(() => {
this.ElseClause()
})
`
$3
`typescript
// 检查下一个 token 是否匹配
if (this.lookahead('LBrace', 1)) {
// 下一个是 {
}
// 检查下一个 token 是否不匹配
if (this.lookaheadNot('ElseTok', 1)) {
// 下一个不是 else
}
// 断言方法
this.assertLookaheadNotIn(['LBrace', 'FunctionTok', 'ClassTok'])
this.assertNoLineBreak()
`
$3
`typescript
// 检查语法是否正确
const result = parser.validateGrammar()
if (!result.success) {
console.error('发现语法冲突:', result.errors)
}
`
🎯 核心概念
$3
| 特性 | Subhuti (PEG) | 传统 LR/LALR |
|------|---------------|--------------|
| 匹配策略 | 第一个成功 | 最长匹配 |
| 规则顺序 | ⭐⭐⭐ 关键 | 不重要 |
| 回溯 | ✅ 支持 | ❌ 不支持 |
| 二义性处理 | 程序员控制 | 自动检测/报错 |
$3
在 Or 规则中:
- 前 N-1 分支:允许失败,失败时返回 undefined`