goyacc初体験
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
x 86アーキテクチャの場合は6を8に変更すればよい
コードは次のとおりです.
このコードでは、$3の場合.numvalは上の行と書きます.
参照
y.go:61: syntax error: unexpected $
,ソースコードから,「/」は単行注釈の始まりとされているようで,バグと疑われ,後で報告する.
ひとつyファイルの^%{と^%}に含まれる部分は、埋め込みコードです.%%中間の部分は解析するLALR(1)文法であり,その定義は非常に直接的である.
yaccとは異なり、goyaccにはlex/flexプログラムがセットされておらず、Lex()を手で書くことですべてのtokenを得る必要がある.エラー時のコールバック関数としてErrorを書く方法もあります.それ以外は順風満帆の比較的簡単なものです.
使用方法
goyacc goexpr.y && 6g y.go && 6l -o goexpr y.6
x 86アーキテクチャの場合は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
", s)
}
func main() {
if flag.NArg() == 0 {
fmt.Printf("Usage goexpr <expr file>
")
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
", file, err)
os.Exit(3)
}
Parse() // Parse the data
fmt.Printf("result = %g
", finalval)
}
このコードでは、$3の場合.numvalは上の行と書きます.
$$.numval = $1.numval / $3.numval
はエラーを報告します.参照
y.go:61: syntax error: unexpected $
,ソースコードから,「/」は単行注釈の始まりとされているようで,バグと疑われ,後で報告する.