google 的go语言里呆了goyacc工具,使用方法基本类似于贝尔实验室的yacc工具. 所以熟悉了解yacc工具的人应该比较容易掌握。不过网上几乎没有找到详细的step by step。 虽然yacc方面的资料比较多, 但是具体细节上goyacc还是有些不同的。所幸在源代码里有一个sample。具体位置是~/go/src/cmd/goyacc/units.y. 算是比较详细。于是照猫画虎,经过代码裁剪,我自己写了个简单的表达式计算器goexpr。
一个.y文件中, 由^%{ 和 ^%}包括的部分, 是嵌入代码。 %%中间的部分则是要解析的LALR(1)文法,其定义是非常直接的。
于yacc不同的是, goyacc没有配套的lex/flex程序, 需要手工写一个方法 Lex()来得到所有的token. 还要写一个方法Error作为出错时的回调函数。除此之外都是顺水顺风的比较简单的东西。
使用方法
goyacc goexpr.y && 6g y.go && 6l -o goexpr y.6
如果是x86架构,则把6换成8即可
代码如下
%{
package main
import (
"fmt"
"os"
"io/ioutil"
"flag"
"bufio"
)
var fi *bufio.Reader
var peekrune int
var data []byte
var linep int = 0
var finalval float = 0
%}
%union
{
vvar string;
numval float;
}
%token NUMBER
%token OP
%%
expr:
expr1
| expr '+' expr1
{
$$.numval = $1.numval + $3.numval
finalval = $$.numval
}
| expr '-' expr1
{
$$.numval = $1.numval - $3.numval
finalval = $$.numval
}
expr1:
NUMBER
| expr1 '*' NUMBER
{
$$.numval = $1.numval * $3.numval
finalval = $$.numval
}
| expr1 '/' NUMBER
{
$$.numval = $1.numval /
$3.numval
finalval = $$.numval
}
%%
func getrune() int {
if linep >= len(data) {
return 0
}
c := data[linep]
return int(c)
}
func next() {
linep++
}
func getnumber(c int) int {
var n int = 0
for ;c>='0' && c <= '9'; {
n += (c - '0')
next()
c = getrune()
}
yylval.numval = float(n)
return NUMBER
}
func readblank() {
var c int
for c = getrune(); c == ' '; {
next()
c = getrune()
}
}
func Lex() int {
var c int
readblank()
c = getrune()
if c >= '0' && c <= '9' {
return getnumber(c)
}
switch c {
case '+', '-', '*', '/':
yylval.vvar = string(c)
next()
return c
}
return c
}
func Error(s string, v ...) {
fmt.Printf("ERROR:%s\n", s)
}
func main() {
if flag.NArg() == 0 {
fmt.Printf("Usage goexpr <expr file>\n")
os.Exit(1)
}
file := flag.Arg(0)
f, err := os.Open(file, os.O_RDONLY, 0)
if err != nil {
fmt.Printf("Error opening %v: %v", file, err)
os.Exit(2)
}
data, err = ioutil.ReadAll(f)
if err != nil {
fmt.Printf("Error reading file %v, %v\n", file, err)
os.Exit(3)
}
Parse() // Parse the data
fmt.Printf("result = %g\n", finalval)
}
这段代码中, 如果 $3.numval 写成和上面一行, 这样
$$.numval = $1.numval / $3.numval
就会报错。
引用
y.go:61: syntax error: unexpected $
, 从源代码中看到,似乎"/" 被当成了单行注释的开始, 疑似bug, 待会儿汇报。
分享到:
相关推荐
goyacc1:简单的goyacc示例(Go) goyacc2:简单的goyacc example2(Go) goyacc3:另一个带有解析器和表达式的goyacc示例(Go) 先决条件 Binson&Flex 走 去得到-u golang.org/x/tools/cmd/goyacc 怎么跑 make...
用Go编写PHP解析器该项目使用goyacc和golex库将PHP源解析为AST。 它可以用来编写静态分析,重构,指标,代码样式格式化工具。 在线尝试:用Go语言编写的dem PHP解析器该项目使用goyacc和ragel工具创建PHP解析器。 它...
为了使事情变得容易,我做了以下事情:复制所有的pkg / sql / parser,pkg / sql / lex并简化依赖关系简化Makefile以仅生成goyacc的东西在解析器和lex中添加goyacc生成的文件以进行获取轻松工作,请参阅.gitignore...
在解析器和lex中添加goyacc生成的文件,以使go get轻松工作,请参阅.gitignore文件 修剪etcd依赖项,请参阅go.mod 重命名除某些pkg/sql/parser测试外的所有测试文件 将所有必需的导入添加到供应商 取下panic满足...
expr.y 是一种玩具示例语言的 goyacc 实现,可以作为解析器未来实现的基础。 在这一点上,大多是废代码。 会话程序的有趣代码在escapes.go 中。 Escapes.go 包含对 Capecchi 等人的微积分限制版本的解释器。 2014 ...
[语法分析与goyacc用法] [解析整个语句的执行流程] [案例:为TiDB添加一个语句] [思考:如何支持json /协议缓冲区] 第四章MySQL协议支持 [协议概述] [如何用wireshark来辅助调试] [要求格式] [回应格式] [准备...
Go可以实现词法分析和语法解析,如使用解析库如`go-yacc`或`goyacc`。 17. **迭代器模式**:提供一种方法顺序访问聚合对象的元素,而又不暴露其底层表示。Go的range关键字可以实现迭代器模式。 18. **中介者模式**...
在 Go 语言中,可以使用自底向上的解析方法,如递归下降解析,或者使用第三方库如 GoGLexer 和 GoYacc 来辅助构建这些组件。 2. **类型系统**:Monkey 的类型系统可能包括基本类型(如整型、浮点型、布尔型和字符串...
Go语法树入门-开启自制编程语言和编译器之旅! Go语言QQ群:102319854,1055927514 ...凹语言(凹读音“ Wa”)(Wa编程语言): : 蚂蚁金服招聘云原生方向和编程语言方向职位(杭州/ P7):云原生方向要求熟悉k8s配套...