You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Nearley中结合自定义语法与JS语法(无需定义完整JS语法)

实现自定义语法与JS原生语法的混合解析(基于Nearley+Moo)

核心思路借鉴JSX的实现逻辑:通过词法分析器的状态切换区分自定义语法和JS代码,将JS内容作为黑盒字符串传递,无需在Nearley中定义完整JS语法,具体步骤如下:

1. 用Moo实现多状态词法分析

利用Moo的状态切换功能,定义两个核心状态:js(处理原生JS代码)和custom(处理自定义<tag>语法),在两种状态间自动切换:

const moo = require('moo');

const lexer = moo.states({
  // JS状态:匹配所有非<的内容,遇到<则切换到自定义语法状态
  js: {
    customTagOpen: { match: '<', push: 'custom' },
    jsContent: { match: /[^<]+/, lineBreaks: true },
  },
  // 自定义语法状态:处理标签结构,遇到>切回JS状态
  custom: {
    tagName: /[a-zA-Z_][a-zA-Z0-9_]*/,
    customTagClose: { match: '>', pop: 1 },
    whitespace: /\s+/,
  },
});

2. 在Nearley中定义混合语法规则

无需编写JS语法规则,只需将JS内容作为整体块处理,与自定义标签规则并列:

# 顶层入口:允许多个JS块或自定义标签的混合
main -> topLevelElement* {% (d) => d[0] %}

# 顶层元素:要么是JS代码块,要么是自定义标签
topLevelElement -> jsBlock | customTag

# JS代码块:拼接连续的JS内容,保留原始字符串
jsBlock -> jsContent:+ {% (d) => ({ type: 'js', code: d[0].join('') }) %}

# 自定义标签:解析<tag>结构,生成对应AST节点
customTag -> customTagOpen whitespace? tagName whitespace? customTagClose {%
  (d) => ({ type: 'customTag', name: d[2].value })
%}

3. 扩展:支持自定义标签内嵌入JS表达式

如果需要类似JSX的{表达式}嵌入逻辑,只需在Moo的状态规则中添加切换逻辑,遇到{切回JS状态,处理完}再返回:

// 更新后的custom状态
custom: {
  tagName: /[a-zA-Z_][a-zA-Z0-9_]*/,
  jsExprOpen: { match: '{', push: 'js' },
  jsExprClose: { match: '}', pop: 1 },
  customTagClose: { match: '>', pop: 1 },
  whitespace: /\s+/,
}

// 同时更新JS状态,识别}并切回custom
js: {
  customTagOpen: { match: '<', push: 'custom' },
  jsExprClose: { match: '}', pop: 1 },
  jsContent: { match: /[^<}]+/, lineBreaks: true },
}

对应的Nearley规则也可以扩展,允许自定义标签中插入JS表达式节点。

4. 后续AST处理

解析完成后,你会得到包含两种类型节点的混合AST:

  • 对于js类型的节点,直接将其code字段交给专业JS解析工具(如@babel/parser)生成标准JS AST;
  • 对于自定义标签节点,按照你的业务逻辑处理;
  • 最后将两种AST按顺序合并或转译为目标代码即可。

这种方式既复用了成熟的JS解析工具,又能快速集成自定义语法,完全符合需求。

内容的提问来源于stack exchange,提问作者Tim

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 09:25:39