著者:平西 宏彰
プログラミング言語で記述されたプログラムを解釈して、実行可能な形式に変換するのがコンパイラの役割です。コンパイラは、簡単なものであれば200行程度のコードで作成できます。今回は、整数の四則演算用の数式を処理できるコンパイラを作成します。
シェルスクリプトマガジン Vol.75は以下のリンク先でご購入できます。
図4 「main.go.y」ファイルに最初に記述するコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 |
%{ package main import ( "fmt" "log" "strconv" "github.com/llir/llvm/ir" "github.com/llir/llvm/ir/constant" "github.com/llir/llvm/ir/types" "github.com/llir/llvm/ir/value" ) %} %% %% type Node struct { // 構文木 ID string // 構文の種類 Value string // 構文が持つ値 Left *Node Right *Node } type Lexer struct { // 字句解析器 src string // ソースコード index int // 調査中のインデックス node *Node // 構文木 } func (l *Lexer) Error(err string) { // エラー処理 log.Fatal(err) } func main() { var code string fmt.Scan(&code) // コード入力 lexer := &Lexer{src: code, index: 0} // 字句解析 yyParse(lexer) // 構文解析 generator(lexer.node) // コード生成 } |
図5 「main.go.y」ファイルの宣言部に追加するコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
%union { num int ope string node *Node } %type<node> program expr %token<num> NUMBER %token<ope> ADD SUB MUL DIV %left ADD, SUB %left MUL, DIV |
図6 「main.go.y」ファイルのプログラム部に追加するコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
func (l *Lexer) Lex(lval *yySymType) int { if len(l.src) <= l.index { return -1 } c := l.src[l.index] l.index++ if c == '+' { return ADD } if c == '-' { return SUB } if c == '*' { return MUL } if c == '/' { return DIV } if '0' <= c && c <= '9' { la := l.index for la < len(l.src) && '0' <= l.src[la] && l.src[la] <= '9' { la++ } num, _ := strconv.Atoi(l.src[l.index-1:la]) lval.num = num l.index = la return NUMBER } return -1 } |
図7 「main.go.y」ファイルの規則部に記述するコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
program // 構文解析を開始するための構文 : expr { $$ = $1 yylex.(*Lexer).node = &Node{} // nodeの初期化 // 式の構文解析で生成された構文木をprogram構文に yylex.(*Lexer).node = $$ } expr // 式の構文 : NUMBER { // 生成規則が数値のときの動作を記述 $$ = &Node{ ID: "NUMBER", Value: strconv.Itoa($1), } } | expr ADD expr { $$ = &Node{ ID: "expr", Value: "+", Left: $1, // 左の式 Right: $3, // 右の式 } } | expr SUB expr { $$ = &Node{ ID: "expr", Value: "-", Left: $1, Right: $3, } } | expr MUL expr { $$ = &Node{ ID: "expr", Value: "*", Left: $1, Right: $3, } } | expr DIV expr { $$ = &Node{ ID: "expr", Value: "/", Left: $1, Right: $3, } } |
図8 「main.go.y」ファイルのプログラム部に追加するコード
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 |
func generator(node *Node) { m := ir.NewModule() main := m.NewFunc("main", types.I32) block := main.NewBlock("") block.NewRet(calc(block, node)) fmt.Println(m.String()) } func calc(block *ir.Block, node *Node) value.Value { if node.ID == "expr" { return expr(block, node) } if node.ID == "NUMBER" { return number(node) } log.Fatal("error: generator") return nil } func number(node *Node) value.Value { val, ok := strconv.Atoi(node.Value) if ok != nil { log.Fatal("error: generator") return nil } return constant.NewInt(types.I32, int64(val)) } func expr(block *ir.Block, node *Node) value.Value { if node.Left == nil || node.Right == nil { log.Fatal("error: generator") return nil } left := calc(block, node.Left) right := calc(block, node.Right) if node.Value == "+" { return block.NewAdd(left, right) } if node.Value == "-" { return block.NewSub(left, right) } if node.Value == "*" { return block.NewMul(left, right) } if node.Value == "/" { return block.NewUDiv(left, right) } log.Fatal("error: generator") return nil } |