2018-10-09

ANTLR4でパーサをgolangで作る

ANTLR4のgolangでvisitorパターンで実装するサンプルが無かったので試しに作ってみました。

以下、golangで実装する場合の備忘録

 

これに従ってantlr4をインストール

goのruntimeをインストール

$ go get github.com/antlr/antlr4/runtime/Go/antlr

grammerファイルからパーサを生成(今回はformulaというpackage名で作ります)

$ antlr4 -Dlanguage=Go -visitor formula.g4

コマンドを実行するとこんな感じなファイルが生成される↓

formula.tokens
formulaLexer.interp
formula_lexer.go
formulaLexer.tokens
formula_listener.go
formula.g4
formula_base_listener.go
formula_parser.go
formula.interp
formula_base_visitor.go
formula_visitor.go

visitorを使うのでformula_base_visitor.goに定義されているBaseformulaVisitorを埋め込む形で構造体を作っていく。

// Code generated from formula.g4 by ANTLR 4.7.1. DO NOT EDIT.

package parser // formula

import "github.com/antlr/antlr4/runtime/Go/antlr"

type AstBuilder struct {
	*BaseformulaVisitor
}

func (v *AstBuilder) VisitCompilationUnit(ctx *CompilationUnitContext) interface{} {
	return ctx.Expression().Accept(v)
}

func (v *AstBuilder) VisitPrimaryExpression(ctx *PrimaryExpressionContext) interface{} {
	return ctx.Primary().Accept(v)
}

func (v *AstBuilder) VisitBinaryExpression(ctx *BinaryExpressionContext) interface{} {
	left := ctx.Expression(0).Accept(v)
	right := ctx.Expression(1).Accept(v)
	op := ctx.GetOp().GetText()
	return &BinaryNode{
		Left:  left,
		Right: right,
		Op:    op,
	}
}

書き方は他のターゲットと同じくctxに対してルールのメソッドを呼び出してAcceptしていきます。配列の場合は引数にインデックスを入れるか、Allのprefixがついたメソッドを呼ぶと配列が返ってくるのでそれを使います。終端ノードはGetText()でトークンの文字列を取得できます。

あとは定義した構造体をこういう感じで使っていけばOK。

package main

import (
	"os"

	"github.com/tzmfreedom/go-salesforce-formula-parser"
)

func main() {
	input, err := antlr.NewFileStream(os.Argv[1]) // ファイル名を指定する場合
	// input := antlr.NewInputStream("1+2*3") // 文字列を指定する場合
	lexer := NewformulaLexer(input)
	stream := antlr.NewCommonTokenStream(lexer, 0)
	p := NewformulaParser(stream)
	p.AddErrorListener(antlr.NewDiagnosticErrorListener(true))
	p.BuildParseTrees = true
	tree := p.CompilationUnit() // ルートのルール名のメソッドを指定
	tree.Accept(&AstBuilder{}) // 定義した構造体はここで使う
}
このエントリーをはてなブックマークに追加