yacc 是用于生成语法解析器的程序,是编译器爱好者的工具。凹语言的 yacc 从 goyacc 移植而来,目前可以初步支持输出 凹语言 版本解析器代码。本文以以表达式解析为例展示下用法。

完整的例子可以参考(这里使用的是expr前缀):https://gitee.com/wa-lang/wa/blob/master/_examples/expr/expr.y。

如果对 yacc 不太了解可以先参考以下图书:

1. yacc 文件结构简介

yacc 文件一般以 *.y 格式命名,其格式如下:

// *.y 文件本身的注释%{
// 生成解析器代码的头部,一般是 import 等语句
%}// yacc 语法对应的词法类似、语法树节点等%%// BNF 语法定义%%// 生成解析器代码的尾部

简单来说,y 文件由两个 %% 行分隔为三个部分(类似文章的凤头、猪肚、豹尾):

  • 凤头:对应生成的解析器的头部,其中%{ ... %} 包含的部分为原样输出,其他部分是 yacc 语法定义的词法类型和语法树节点等

  • 猪肚:是 yacc 文件等核心,通过 BNF 语法定义了语法结构,这里主要是针对 LALR(1) 语法

  • 豹尾:如果是独立的程序,可以在这个部分引入词法解析器和 main 函数;如果是 package 则是可以省略的

2. 定义expr.y文件 - 凤头部分

创建表达式语法文件如下:

// 版权 @2023 凹语言 作者。保留所有权利。%{
// 这是 凹语言 yacc 的例子, 用于对表达式进行解析, 为了简化词法部分暂时通过手工录入.
%}%union {num :int
}%type  <num> expr expr1 expr2 expr3
%token '+' '-' '*' '/' '(' ')'
%token <num> NUM%%

其中%union定义了词法和语法解析器对接的结构体类型。%type语句定义了expr expr1 expr2 expr3几种语法节点,都是对应<num>类型值,而数字的值需要填充到%union定义的num属性部分。%token语句定义的运算符和NUM类型的数字。

3. 定义expr.y文件 - 猪肚部分

猪肚部分对应表达式的语法结构:

%%top:expr { println($1) }expr:expr1| '+' expr { $$ = $2 }| '-' expr { $$ = -$2 }expr1:expr2| expr1 '+' expr2 { $$ = $1 + $3 }| expr1 '-' expr2 { $$ = $1 - $3 }expr2:expr3| expr2 '*' expr3 { $$ = $1 * $3 }| expr2 '/' expr3 { $$ = $1 / $3 }expr3:NUM| '(' expr ')' { $$ = $2 }%%

当遇到expr语法规则是直接输出结果,expr1表示加减法、expr2表示乘除法、expr3表示数字或小括弧。在每个最终后面的{}中包含的是动作代码,它们根据不同的语法规则选择不同的计算方式得到结果,结果赋值给$$(也就是对应%type <num> expr expr1 expr2 expr3语句中的<num>部分类型,也对应%union定义的num成员)。

4. 定义expr.y文件 - 豹尾部分 - 01

有了凤头和猪肚部分,yacc就可以生成必要的解析器代码了。默认后生成以下格式的解析器函数yyParse

func yyParse(yylex: *yyLexer) => int {return yyNewParser().Parse(yylex)
}

yyLexer词法解析器则是用户需要自行实现的(词法解析实现相对简单),主要包含以下2个方法:

type yyLexer struct {}func yyLexer.Lex(yylval *yySymType) => int {// 返回 Token 类型, 并且将对应的值填充到 yylval 相应的属性中
}func yyLexer.Error(s string) {// 遇到错误
}

yyLexer.Lex 返回 Token 类型,并且将对应的值填充到 yylval 相应的属性中,遇到文件结尾时返回0表示文件结束。方法参数对应的yySymType类型由yacc工具生成,对应如下的代码:

type yySymType struct {yys :intnum :int
}

其中num对应对应%union定义的属性,也就是数字的值。

5. 定义expr.y文件 - 豹尾部分 - 02

为了简化演示代码,我们先手工构造词法序列,然后通过yyLexer.Lex 返回。

// Lex 结束标志
const eof = 0type yyToken struct {Kind  :intValue :int
}type yyLexer struct {tokens :[]yyTokenpos    :int
}func yyLexer.Lex(yylval *yySymType) => int {if this.pos >= len(this.tokens) {return eof}tok := this.tokens[this.pos]this.pos++yylval.num = tok.Valuereturn tok.Kind
}func yyLexer.Error(s string) {println("ERROR:", s)
}

首先定义yyToken,对应token的类型和值信息。然后yyLexer定义全部的token列表和当前的pos信息。yyLexer.Lex方法每次从this.tokens列表对应的this.pos位置返回一个token,如果是结束则返回eof

然后就可以构造main函数启动了:

func main {print("1+2*3 = ")yyParse(&yyLexer{tokens: []exprToken{{Kind: NUM, Value: 1},{Kind: '+'},{Kind: NUM, Value: 2},{Kind: '*'},{Kind: NUM, Value: 3},},})
}

6. 生成解析器代码

在生成解析器代码前再准备一个copyright.txt文件,比如“保留所有权利”或者“自由使用”之类的。然后通过以下命令生成解析器代码:

$ wa yacc -l -p=yy -c="copyright.txt" -o="y.wa" expr.y

其中-l表示生成的代码禁止映射到*.y文件行列号(用生成代码的位置),-p=yy表示生成的解析器函数和类型等用yy前缀(这也是默认值),-c="copyright.txt"为生成代码指定版权信息,-o="y.wa"指定输出文件,最后的expr.y对熟人的yacc规则文件。

生成代码成功之后可以执行:

$ wa y.wa
1+2*3 = 7

7. 下一步

目前的凹语言版 yacc 工具还是Go语言实现的,只是输出的解析器是凹语言代码。我们希望下一步可以将 yacc 工具本身移植到凹语言实现,最终可以通过 wasm 模块执行。

凹语言版本 yacc 简介 - 以表达式解析为例相关推荐

  1. 数学表达式解析器简介

    2019独角兽企业重金招聘Python工程师标准>>> 在实际项目开发中如果需要解析数学公式,无须再运用解释器模式进行设计,可以直接使用一些第三方解析工具包,它们可以统称为数学表达式 ...

  2. C语言 科学计算器 后缀表达式 解析字符串 仿JS的eval函数

    C语言 利用后缀表达式解析字符串 最近用98标准的C语言写了个解析字符串,类似于JavaScript中的eval函数,感觉挺实用(移植到了计算器上,可以画F(X,Y)==0这种图像了),特此分享一下, ...

  3. 【包】R语言rvest包简介

    以下翻译自:rvest包github项目主页 R语言rvest包简介 rvest包可以帮助我们从网页上抓取信息,它通常与magrittr包配合使用,便于进行常见的网络抓取任务.revest包的开发主要 ...

  4. 京东“百亿补贴”提前20小时上线,电商价格战开打; iPhone 15 Pro玻璃面板泄露;凹语言 0.5.0发布|极客头条

    「极客头条」-- 技术人员的新闻圈! CSDN 的读者朋友们早上好哇,「极客头条」来啦,快来看今天都有哪些值得我们技术人关注的重要新闻吧. 整理 | 梦依丹 出品 | CSDN(ID:CSDNnews ...

  5. python中set()函数的用法,python中set()函数简介及实例解析

    python中set()函数简介及实例解析 set函数也是python内置函数的其中一个,属于比较基础的函数.其具体介绍和使用方法,下面进行介绍. set() 函数创建一个无序不重复元素集,可进行关系 ...

  6. 表达式解析器(MVEL)

    Jeval 在运行时解析计算静态和动态表达式:支持数学,布尔,字符串,函数表达式:支持大部分的数学和布尔运算符:支持自定义函数:支持嵌套函数:支持解析:支持自定义变量解析器: 官网:http://je ...

  7. c++ _int64转字符串_C语言 仿JS的eval函数 解析字符串

    C语言 利用后缀表达式解析字符串(符合c98标准,很容易移植到计算器上) 最近用98标准的C语言写了个解析字符串,类似于JavaScript中的eval函数,感觉挺实用(移植到了计算器上,可以画F(X ...

  8. c语言计算机编程例题详解,计算机C语言编写程序题及答案解析精选.doc

    计算机C语言编写程序题及答案解析精选 2011年计算机二级C语言编写程序题及答案解析精选 [4.1]已知银行整存整取存款不同期限的月息利率分别为:0.315% 期限一年0.330% 期限二年月息利率 ...

  9. Crontab中文表达式解析

    简介 最近工作中在使用调度框架,经常和定时表达式打交道,并且有查看表达式中文解释的需求,于是在网上搜集资料和自己进行一定的修改,写了一个Crontab表达式解析的工具类 详解 这个没啥好解释,看资料, ...

最新文章

  1. 【百度地图API】如何区分地址解析和智能搜索?
  2. PyTorch 《动手学深度学习》学习笔记(Dive-into-DL-Pytorch)
  3. express中get和post的区别
  4. 配置msf连接postgresql数据库
  5. 基于JAVA+SpringMVC+Mybatis+MYSQL的物流管理系统
  6. linux软件包管理rpm
  7. BZOJ 1067 降雨量(RMQ-ST+有毒的分类讨论)
  8. mysql 附近3公里的,mysql 查询附近N公里内数据
  9. 早教机器人刷固件_E-puck2机器人系列教程-固件修复升级
  10. 学习阮一峰老师-互联网协议笔记(IP/UDP/TCP/HTTP)
  11. rubyinstaller下载安装 redis集群
  12. android pc摄像头驱动,Android 驱动USB摄像头
  13. 利用canvas的rotate创建一个简易的风车动画
  14. c语言 ZZ转字符串,C语言 字符串中的转义字符与字符串的长度 zz
  15. Linux – cp: omitting directory 复制文件失败
  16. 如何成为一个高情商的员工(附全书思维导图)
  17. 蚂蚁金服 g6 用法
  18. APP测试面试题汇总
  19. 卸载已有navicat for mysql,安装破解版。
  20. Java中使用json时java.lang.NoClassDefFoundError: net/sf/ezmorph/Morpher问题解决

热门文章

  1. # 计算机通信网络实验(一) 二层分组交换实验
  2. 用Excel,爬取网站数据
  3. 如何把VHD转换成VHDX
  4. 北京邮电大学计算机学院csp,北京邮电大学
  5. 汽车名词解释-发动机参数(2)
  6. python形态选股_用Python选一个自己的股票池!堪比资深的炒股选手
  7. 圣诞氛围拉满,《小鬼当家》原版取景地首次对外开放入住
  8. 美食网站主页java代码,中华美食网 Javaweb静态界面
  9. 基于Win10安装双系统Ubuntu不能正常进入的问题
  10. 河南省各地市经纬度及编码数据