前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >聊聊PegJS

聊聊PegJS

作者头像
QQ音乐前端团队
发布于 2021-04-19 01:40:13
发布于 2021-04-19 01:40:13
1.5K00
代码可运行
举报
运行总次数:0
代码可运行

在开发前端BFF框架的时候,需要将团队后台使用的JCE协议(类似ProtoBuff协议)转换成nodejs对应的语法,这里参考@tencent/jce2node-cli的实现,使用PEG.js解析生成AST,下面就来介绍一下PEG.js是如何进行解析的?

我们在对文本进行解析的时候,通常可以使用正则表达式从目标文本中提取所需信息。但是仅使用正则表达式来解析,会发现非常难以阅读,可维护性比较差,而PegJs 则是一种更加简便可维护的 parser 工具。

PEG.js是一个JavaScript的词法解析器,可以用来处理复杂的数据或计算机语言,并轻松构建转换器、解释器编译器和其他工具。它的语法对前端工程师很友好,只需要掌握基本的正则语法即可,并提供在线体验网址。下面是基于PegJS语法的一个官方示例,它的语法有这样两个特点:

  • PegJS的语法由一组规则组成,从上至下进行解析。起始规则是整个语法的『根』,后面的所有规则定义都应该是这个『根』的子节点,如果某个规则无法从『根』溯源下去,那么这个规则就是一条无效的规则。
  • 规则形似变量声明,由名称和解析表达式组成。解析表达式可以是正则表达式,也可以是其他规则定义。
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
// additive.pegjs
start
  = additive

additive
  = left:multiplicative "+" right:additive { return left + right; }
  / 
  multiplicative

multiplicative
  = left:primary "*" right:multiplicative { return left * right; }
  / 
  primary

primary
  = integer
  / 
  "(" additive:additive ")" { return additive; }

integer "integer"
  = digits:[0-9]+ { return parseInt(digits.join(""), 10); }

上面的语法定义了加法和乘法的混合运算规则,可以将文本中符合规则的字符,比如 (2+7)*8,进行解析和运算。我们首先从简单的表达式开始分析:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interger = [0-9] // 只匹配一个数字
// "1" -> "1"
// "12" -> Line 1, column 2: Expected end of input but "2" found.

integer = [0-9]+ // 至少匹配一个数字
// "12" -> ["1", "2"]
// "" -> Line 1, column 1: Expected [0-9] but end of input found.

integer = [0-9]* // 匹配0个或者多个数字
// "124" -> ["1", "2", "4"]
// "" -> []

符号+表示至少匹配1个,符号*表示匹配0个或者多个。默认情况下,使用了+* 匹配出的结果会返回一个数组,PegJS 提供在表达式中通过变量名和一个format函数来自定义返回值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
integer = digits:[0-9] { return digits.join() }
// "124" -> "124"

我们再来看上面的"integer"规则,显然,这条规则匹配多个数字并返回number类型的返回值。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
integer "integer"
  = digits:[0-9]+ { return parseInt(digits.join(""), 10); }
// "124" -> 124

这里来做一个小练习,匹配一个浮点数:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
float = number:interger "." decimal:interger {return parseFloat(number + '.' + decimal);}
interger = digits:[0-9]+ { return digits.join(''); }
// "124.35" -> 124.35

再看一下符号/,它表示or 的含义。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
number = float / integer

为避免歧义,如果定义规则start = a / b,当输入即可以匹配a也可以匹配b,那么PegJS则优先使用a来进行解析。

下面介绍一个重要的概念:递归,这在描述嵌套或者树状结构的时候非常有用。先来看一个简单的例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
commaSeparatedIntegerList
    = integer ',' commaSeparatedIntegerList
    / integer
integer = [0-9]

当解析输入"1,2"的时候,首先匹配了"1,",接下来"2"去递归匹配commaSeparatedIntegerList规则,发现符合integer表达式,最终的返回值是:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
[
   "1",
   ",",
   "2"
]

这里对匹配到的结果返回的value做一个说明:

  • 与文字字符串匹配的表达式会生成包含匹配文本的JavaScript字符串。
  • 与某个子表达式的重复出现匹配的表达式将生成具有所有匹配项的JavaScript数组。

最后我们再来看一下下面两条规则,它们的功能完全相同,第一条规则中定义了一个规则名称"数字",我们可以通过这种方式为规则起一个可读性高的名称:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
interger "数字" 
	= [0-9]

interger = [0-9]

我们还可以在规则定义的最开始用"{"和"}"在大括号内部定义一些JavaScript代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
{
  function makeInteger(digits) {
    return parseInt(digits.join(""), 10)
  }
}
interger = digits:[0-9]+ { return makeInteger(digits); }

以上就是 PegJS 语法的基本使用方法,我们可以用它来定义各种复杂的解析规则。

现在我们开始尝试去解析一个简单的JCE文件吧,它主要由两部分组成:structinterfacestruct定义了数据类型,interface 中则声明了服务的方法。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
module MTT {
    struct HelloReq {
        0   require int id;
    };

    struct HelloRsp {
        0   require int     iCode;
        1   require string  sMessage;
    };

    interface Hello {
        int hello (HelloReq req, out HelloRsp rsp);
    };
};

首先定义struct的规则,它里面包含多个成员,每个成员由序号、关键字、类型、变量名和分号组成:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
StructDefinition "struct"
  = "struct" _+ id: Identifier _* "{" _* members: MemberDeclaration+ _* "}" _* ";" _* {
    return {
      id,
      type: "struct",
      members
    }
  }
    
MemberDeclaration "member"
  = i: IntegerLiteral _+ 
    key: ("require" / "optional") _+ 
    type: TypeSpecifier _+ 
    id: Identifier _* ";" _*  {
      return {
        index: i,
        isRequired: key === "required",
        id,
        type,
        id
      };
    }
    
IntegerLiteral
  = digits: [0] { return parseInt(digits); }
  / head: [1-9] tail:[0-9]* { return parseInt([head, ...tail].join('')); }
  
Identifier
  = head: [_a-zA-Z] tail: [_a-zA-Z0-9]* {
    return [head, ...tail].join('');
  }

// 这里TypeSpecifier的定义仅为示例,没有罗列出所有的类型定义
TypeSpecifier
  = "void" / "bool" / "string" / "int" / "short" / type: "unsigned" blank "int" { return type.join("") } 
    
blank
  = [ ]+ {
    return "";
  }

_ "whitespace"
  = ([ \t\n\r]) {
    return "";
  }

通过上面的规则,我们就可以得到结构化的struct了。

struct.png

接下来用类似的思路来定义interface的规则:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
InterfaceDefinition "interface"
	= "interface" _+ id: Identifier _* "{" _* methods: MethodDeclaration+ _* "}" _* ";" _* {
    return {
    	id,
      type: "interface",
      methods
  	}
  }
  
MethodDeclaration "method"
	= returnType: TypeSpecifier _+ id: Identifier _* "(" _* params: ParameterDefinition _* ")" _* ";" _* {
    return {
    	id,
    	type: "method",
      returnType,
    	params
  	}
  }
  
ParameterDefinition
	= first: SingleParameterDefinition _* "," _* left: ParameterDefinition {
    return [first, ...left]
  } / 
  param: SingleParameterDefinition { return [param]; }
  
  
SingleParameterDefinition
	= "out" _+ type: (Identifier / TypeSpecifier)  _+ id: Identifier {
    return {
      id,
      io: "out",
      type
    }
  } 
  / 
  _* type: (Identifier / TypeSpecifier) _+ id: Identifier {
    return {
      id,
      io: "",
      type
    }
  }

那么就得到了解析后的接口方法定义:

interface.png

最后,将struct规则和interface规则进行整合后,就可以得到一个简单的Pegjs语法的JCE解析器了。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
jce
	= module: ModuleDefinition { return module; }

ModuleDefinition
	= _* "module" _+ id: Identifier _* "{" _* value: ValueDefinition+ _* "}" _* ";" _* {
    return {
    	type: "module",
      id,
    	value,
  	}
  }
  
ValueDefinition = StructDefinition / InterfaceDefinition

最终整个JCE文件的解析结果如下:

jce.png

参考文献:

Intro to Peg.js

documentation#grammar-syntax-and-semantics

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2021-04-16,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 QQ音乐前端团队 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Power BI颜色编码方式选择
Power BI可以使用DAX自定义颜色,比如字体颜色、背景色、填充色等。主要使用的颜色编码方式为英文颜色名称、RGB、十六进制。
wujunmin
2025/02/10
1450
Power BI颜色编码方式选择
Power BI 条件格式三剑合璧
Power BI 表格矩阵可以设置五种条件格式类型,绝大多数情况下你可能对某列只使用一种条件格式,本文介绍一个三种条件格式(背景色、字体颜色和图标)叠加使用的业务情景。
wujunmin
2023/11/23
3590
Power BI 条件格式三剑合璧
Power BI 长文本条件格式总结
Power BI内置表格矩阵最常用的条件格式有字体颜色、背景色和图标。长文本能不能实现同样的条件格式?当然可以。原理是DAX+HTML/SVG。
wujunmin
2025/02/10
1260
Power BI 长文本条件格式总结
利润表分析怎么做才能更出彩,原来还能这么用Power BI(文末超大福利放送!!!)
很多人都认为Power BI 仅仅是一个可视化界面展示的工具,还不清楚Power BI 的每个模块是如何相互影响和关联的,或如何将每一模块结合起来运用到工作实践中去,最终通过数据“原材料”的高效加工为企业决策者做出一道“美味佳肴”?
公众号PowerBI大师
2022/11/11
2.5K1
利润表分析怎么做才能更出彩,原来还能这么用Power BI(文末超大福利放送!!!)
Power BI+DeepSeek+SVG实现卡片图自由
昨天分享了DeepSeek辅助Power BI自定义条件格式图标,今天继续分享AI辅助Power BI可视化,使用DeepSeek生成SVG度量值,制作任意构造的卡片图,整个过程无需了解SVG代码知识。
wujunmin
2025/02/19
3600
Power BI+DeepSeek+SVG实现卡片图自由
DeepSeek辅助Power BI设计背景图
有人说DeepSeek目前不支持图片处理,这种说法是片面的。确切地说,目前不支持像素图片处理,但是支持SVG图片处理。
wujunmin
2025/03/06
1190
DeepSeek辅助Power BI设计背景图
Power BI: 在总计行实现条件格式
文章背景:矩阵是Power BI中经常用到的一个视觉对象,如何针对矩阵的值和总计行分别设置不同的条件格式?本文通过创建度量值的方式来实现。
Exploring
2023/12/19
5790
Power BI: 在总计行实现条件格式
Go每日一库之88:color
Golang下的命令行色彩使用库, 拥有丰富的色彩渲染输出,通用的API方法,兼容Windows系统
luckpunk
2023/09/30
4930
Go每日一库之88:color
Power BI卡片图添加地图
Power BI 2023年6月推出的卡片图视觉对象是一个良好的地图载体。在卡片图添加地图,本质上就是添加图标,以下卡片图中,地图的添加方式和销售业绩、业绩达成率的图标没什么不同。
wujunmin
2023/09/05
4120
Power BI卡片图添加地图
Power BI SVG批量颜色处理工具
SVG素材可以大大增强Power BI的可视化效果,比如SVG地图、SVG图标、SVG背景。我前期分享了两个工具来降低Power BI用户的SVG使用门槛,如《阿里云SVG地图批量适配Power BI》和《Power BI 无代码SVG工具七大应用》,今天分享第三个:SVG批量颜色处理工具
wujunmin
2025/02/10
930
Power BI SVG批量颜色处理工具
3.10 PowerBI报告可视化-条件格式:使用颜色度量值,实现多条件格式
加入 PowerBI自己学 知识星球 可以:下载源文件,边学边练;遇到问题,提问交流,有问必答。
PowerBI自己学_轻松
2025/02/25
1690
3.10 PowerBI报告可视化-条件格式:使用颜色度量值,实现多条件格式
Power BI做一个日历图表
日历可以放在报表一角,以便阅读者知晓当前日期在当月的位置。下图是一个示例,有星期,有日期,周末为灰色,如果是当天,则有红色背景色并且字体显示为白色。如何在Power BI中实现呢?
wujunmin
2021/11/26
2.3K0
Power BI 长文本局部高亮条件格式
长文本在Power BI通常用作摘要,描述一种或一组业务状况。在长文本植入度量值可以达到动态切换数据的目的。以下是很普通的一种长文本,展示了单个门店的三个指标。
wujunmin
2025/02/10
680
Power BI 长文本局部高亮条件格式
C语言教你怎么改变字体颜色
发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/151557.html原文链接:https://javaforall.cn
全栈程序员站长
2022/09/06
3.9K0
C语言教你怎么改变字体颜色
Power BI制作动态颜色调试工具
在Power BI设置画布背景或者图表背景时,可以手动输入颜色代码,输入的方式有两种,HEX(十六进制)或者RGB(红绿蓝)。
wujunmin
2022/12/13
1.8K0
Power BI制作动态颜色调试工具
Power BI 卡片图动态分组与排序
Power BI 新卡片图视觉对象可以一次存放多个指标。以下使用字段参数功能同时显示了六个指标。
wujunmin
2025/02/10
1440
Power BI 卡片图动态分组与排序
Power BI 条件格式图标总结-2023版
Power BI的条件格式有五种模式,背景色、字体颜色、数据条、图标和Web URL。在这五种模式中,只有图标可以有无限的扩展性,其它四种功能比较单一。条件格式图标可以怎么玩?下面以五重境界进行描述。
wujunmin
2024/01/10
3900
Power BI 条件格式图标总结-2023版
Python批量修改Excel文件格式:加粗、颜色交替、渐变背景色填充
功能描述:首先生成几个测试用的Excel文件,然后批量修改这些文件的格式,把表头加粗并设置为黑体,其他行字体为宋体,设置奇偶行颜色不同,并设置偶数行为从红到蓝的渐变背景色填充。 from random import sample import openpyxl from openpyxl.styles import Font, colors def generateXlsx(num): for i in range(num): wb = openpyxl.Workbook()
Python小屋屋主
2018/04/16
2.6K0
Python批量修改Excel文件格式:加粗、颜色交替、渐变背景色填充
Processing文字气泡抖动创作思路解析
今天小菜的#processing源码分析系列给大家带来的是一个文字气泡抖动的效果实现原理解析。
ChildhoodAndy
2021/11/16
1.3K0
Processing文字气泡抖动创作思路解析
Power BI 柱形组合图标签位置优化
以上柱形图反映了当前值和同期对比的增长下降趋势。增长值的标签此处进行了优化,除了红绿颜色标识,箭头位置也对应做了区分,对比更鲜明。使用Power BI内置的堆积柱形图结合新数据标签,我们可以轻松实现以上效果。
wujunmin
2025/02/10
980
Power BI 柱形组合图标签位置优化
相关推荐
Power BI颜色编码方式选择
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验