在编译的原理的世界中,lex and yacc 的 example 很经典。在 python 中,有第三方工具包ply实现了类似于 lex and yacc的工具。跟传统的lex 和yacc 一样,lex 提供 token 供 yacc 进行语法分析(移进–规约),在go语言的世界中,可使用golang 自带的 text/scanner 近似的替代lex(它提供golang语言的词法分析),使用goyacc完成语法分析的过程。下面就将给出一个使用上述环境的example。
// filename : main.y
// usage: goyacc -o "main.go" main.y && go run main.go
%{
package main
import (
"bufio"
"os"
"strings"
"text/scanner"
"strconv"
"log"
"io"
"strings"
"fmt"
)
%}
%union {
num int
}
%type <num> expr term
%token '+' '-' '*' '/'
%token <num> NUM
%%
top: expr
{ fmt.Println($1) }
expr: term
{ $$ = $1 }
| expr '+' term
{ $$ = $1 + $3 }
| expr '-' term
{ $$ = $1 - $3}
;
term: NUM
{ $$ = $1 }
| term '*' NUM
{ $$ = $1 * $3 }
| term '/' NUM
{ $$ = $1 / $3 }
;
%%
const eof=0
type MainLex struct {
scanner.Scanner
}
func (l *MainLex) Lex(lval *yySymType) int {
token, lit := l.Scan(), l.TokenText()
tok := int(token)
if tok == scanner.Int {
lval.num, _ = strconv.Atoi(lit)
return NUM
}
return tok
}
func (x *MainLex) Error(s string) {
log.Printf("parse error: %s", s)
}
func main() {
in := bufio.NewReader(os.Stdin)
for {
if _, err := os.Stdout.WriteString("> "); err != nil {
log.Fatalf("WriteString: %s", err)
}
line, err := in.ReadString('\n')
if err == io.EOF {
return
}
if err != nil {
log.Fatalf("ReadBytes: %s", err)
}
s := new(MainLex)
s.Init(strings.NewReader(line))
yyParse(s)
}
}