npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

subhuti

v0.2.86

Published

Automatic conversion between two different programming languages through two grammar files that define the same grammar structure and grammar name

Readme

Subhuti

npm version License: MIT

Subhuti (सुभूति) - 轻量级、高性能的 PEG Parser Generator 框架,用 TypeScript 构建,专为快速开发编程语言解析器而设计。

名称由来: Subhuti(菩提祖师)是孙悟空的师父,寓意让编程语言转换如七十二变般灵活。

✨ 核心特性

🚀 高性能 Packrat Parsing

  • 线性时间复杂度 O(n):通过 LRU 缓存避免重复解析
  • 智能缓存管理:自动清理过期缓存,内存占用可控
  • 可选开关:根据需求灵活启用/禁用缓存

🎯 PEG 风格语法(Parsing Expression Grammar)

  • 顺序选择Or 规则按顺序尝试,第一个成功即返回
  • 自动回溯:失败时自动恢复状态,支持复杂语法
  • 清晰语义:程序员完全控制规则顺序,无二义性

🛡️ 智能错误管理(allowError 机制)

  • 前 N-1 分支允许失败:在 Or 规则中优雅处理失败
  • 最后分支抛详细错误:精确定位语法错误,附带完整上下文
  • RAII 模式管理:自动恢复错误状态,避免手动管理

🎨 优雅的 TypeScript API

  • 装饰器模式:使用 @SubhutiRule 定义规则,代码简洁
  • 强类型支持:完整的 TypeScript 类型定义
  • 链式调用:流畅的 API 设计(.cache().debug().errorHandler()

🔧 开发友好

  • 调试支持:内置 Trace Debugger,可视化规则匹配过程
  • 错误处理:详细的错误信息(位置、期望、实际、规则栈)
  • 问题检测系统:运行时检测左递归、无限循环等常见错误
  • 语法验证:自动检测 Or 规则冲突(前缀遮蔽、空路径)
  • CST 辅助方法getChild(), getChildren(), getToken() 等便捷方法
  • Token 前瞻:完整支持 ECMAScript 规范的所有 [lookahead ...] 约束

📦 安装

npm install subhuti
# 或
yarn add subhuti

🚀 快速开始

1. 定义 Lexer(词法分析器)

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)

2. 定义 TokenConsumer(可选,简化 token 消费)

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. 定义 Parser(语法分析器)

import { SubhutiParser, SubhutiRule, Subhuti } from 'subhuti'

@Subhuti
class MyParser extends SubhutiParser<MyTokenConsumer> {
  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()
  }
}

4. 解析代码

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)
}

📖 核心能力

Parser 组合器

Or - 顺序选择(规则顺序很重要!

this.Or([
  { alt: () => { /* 长规则:优先尝试 */ } },
  { alt: () => { /* 短规则:作为回退 */ } }
])

⚠️ 关键原则长规则必须在短规则前面

Many - 0 次或多次

this.Many(() => {
  this.Statement()
})

AtLeastOne - 1 次或多次

this.AtLeastOne(() => {
  this.Parameter()
})

Option - 0 次或 1 次

this.Option(() => {
  this.ElseClause()
})

Token 前瞻(Lookahead)

// 检查下一个 token 是否匹配
if (this.lookahead('LBrace', 1)) {
  // 下一个是 {
}

// 检查下一个 token 是否不匹配
if (this.lookaheadNot('ElseTok', 1)) {
  // 下一个不是 else
}

// 断言方法
this.assertLookaheadNotIn(['LBrace', 'FunctionTok', 'ClassTok'])
this.assertNoLineBreak()

语法验证

// 检查语法是否正确
const result = parser.validateGrammar()

if (!result.success) {
  console.error('发现语法冲突:', result.errors)
}

🎯 核心概念

PEG 顺序选择 vs 传统最长匹配

| 特性 | Subhuti (PEG) | 传统 LR/LALR | |------|---------------|--------------| | 匹配策略 | 第一个成功 | 最长匹配 | | 规则顺序 | ⭐⭐⭐ 关键 | 不重要 | | 回溯 | ✅ 支持 | ❌ 不支持 | | 二义性处理 | 程序员控制 | 自动检测/报错 |

allowError 机制

Or 规则中:

  • 前 N-1 分支:允许失败,失败时返回 undefined
  • 最后分支:失败时抛出详细错误

📊 与其他工具对比

| 工具 | Subhuti | ANTLR | PEG.js | Chevrotain | |------|---------|-------|--------|------------| | 语言 | TypeScript | Java/多语言 | JavaScript | TypeScript | | 风格 | PEG | LL(*) | PEG | LL(k) | | 定义方式 | 装饰器 | 独立语法文件 | 独立语法文件 | TypeScript API | | 回溯 | ✅ | ❌ | ✅ | ❌ | | 性能 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ |

🎯 实际应用

Slime 项目

使用 Subhuti 构建完整的 JavaScript ES2025 解析器:

  • ✅ 支持最新 ECMAScript 2025 规范的所有语法特性
  • ✅ CST → AST 转换
  • ✅ 代码生成和 Source Map 支持

📄 License

MIT © alamhubb


English