環境
Visual Studio 2017
Windows 10
セットアップ
ツール > 拡張機能と更新プログラム > antlrで検索
antlr language supportをダウンロード
Hello abc
趣味プログラミングの部屋の(01)ANTLR4をC#で使ってみるを参考に、簡単なプログラムを作る。
Hello.g4とProgram.csはこのサイトからの引用。
- 新規プロジェクト作成
コンソールアプリ(.NET Framework) - antlr4パッケージインストール
nugetパッケージマネージャーコンソールで実行。
Install-Package antlr4
- Grammarファイル作成
ソリューションエクスプローラー > 追加 > ANTLR4 Combined Grammar
Hello.g4
// Define a grammar called Hello`
grammar Hello;
r : 'hello' ID ; // match keyword hello followed by an identifier
ID : [a-z]+ ; // match lower-case identifiers
WS : [ \t\r\n]+ -> skip ; // skip spaces, tabs, newlines
- Program.cs編集
- using追加
using Antlr4.Runtime;
を追加。 - mainを書き換える
using Antlr4.Runtime;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AntlrTest01
{
class Program
{
static void Main(string[] args)
{
string parsedString = "hello abc";
var inputStream = new AntlrInputStream(parsedString);
var lexer = new HelloLexer(inputStream);
var commonTokenStream = new CommonTokenStream(lexer);
var parser = new HelloParser(commonTokenStream);
HelloListener listener = new HelloListener();
parser.AddParseListener(listener);
var graphContext = parser.r();
Console.WriteLine(graphContext.ToStringTree());
}
}
}
- ビルドして実行
([] hello abc)
Listenerを使ってみる
Listenerで自動生成されるクラスとメソッド
グラマーファイルで定義したgrammarの名前 + 'BaseListener'という名前のクラスが自動生成されている。
この中ではグラマーファイルで定義したrの要素などのListenerが定義されている。
今回だと、下記が定義されている。
- public virtual void EnterR([NotNull] HelloParser.RContext context)
- public virtual void ExitR([NotNull] HelloParser.RContext context)
- public virtual void EnterEveryRule([NotNull] ParserRuleContext context)
- public virtual void ExitEveryRule([NotNull] ParserRuleContext context)
- public virtual void VisitTerminal([NotNull] ITerminalNode node)
- public virtual void VisitErrorNode([NotNull] IErrorNode node)
Listenerを使った簡単な実装をしてみる
この継承したクラスを定義し、Listenerをoverrideして作成しListenerを記述する。
HelloListener.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Antlr4.Runtime;
using Antlr4.Runtime.Misc;
using Antlr4.Runtime.Tree;
namespace AntlrTest01
{
public class HelloListener : HelloBaseListener
{
public override void EnterR([NotNull] HelloParser.RContext context)
{
base.EnterR(context);
Console.WriteLine("--EnterR--");
Console.WriteLine(" GetText():{0} ID:{1} ", context.GetText(),context.ID());
}
public override void EnterEveryRule([NotNull] ParserRuleContext context)
{
base.EnterEveryRule(context);
Console.WriteLine("--EnterEveryRule--");
Console.WriteLine(" GetText():{0}", context.GetText());
}
public override void ExitEveryRule([NotNull] ParserRuleContext context)
{
base.ExitEveryRule(context);
Console.WriteLine("--ExitEveryRule--");
Console.WriteLine(" GetText():{0}", context.GetText());
}
public override void ExitR([NotNull] HelloParser.RContext context)
{
base.ExitR(context);
Console.WriteLine("--ExitR--");
Console.WriteLine(" GetText():{0} ID:{1}", context.GetText(),context.ID());
for(var i= 0 ; i< context.children.Count;i++)
{
Console.WriteLine(" child({0}): {1}",i,context.children[i].GetText());
}
}
public override void VisitTerminal([NotNull] ITerminalNode node)
{
base.VisitTerminal(node);
Console.WriteLine("--VisitTerminal--");
Console.WriteLine(" GetText():{0}", node.GetText());
}
}
}
- mainでリスナーを設定。
static void Main(string[] args)
{
string parsedString = "hello abc";
var inputStream = new AntlrInputStream(parsedString);
var lexer = new HelloLexer(inputStream);
var commonTokenStream = new CommonTokenStream(lexer);
var parser = new HelloParser(commonTokenStream);
HelloListener listener = new HelloListener(); //追加
parser.AddParseListener(listener); //追加
var graphContext = parser.r();
Console.WriteLine(graphContext.ToStringTree());
}
実行結果
--EnterEveryRule--
GetText():
--EnterR--
GetText(): ID:
--VisitTerminal--
GetText():hello
--VisitTerminal--
GetText():abc
--ExitR--
GetText():helloabc ID:abc
child(0): hello
child(1): abc
--ExitEveryRule--
GetText():helloabc
([] hello abc)
Visitorを使ってみる
Visitorで自動生成されるクラスとメソッド
グラマーファイルで定義したgrammarの名前 + 'BaseVisitor'という名前のクラスが自動生成されている。
この中ではグラマーファイルで定義したrの要素のVisitorが定義されている。
Listenerだと、多くのメソッドが自動生成されていたが、Visitorでは下の1つだけ。
- public virtual Result VisitR([NotNull] HelloParser.RContext context)
これをoverrideして利用する。
Visitorを使った簡単な実装をしてみる
HelloVisitor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Antlr4.Runtime;
using Antlr4.Runtime.Misc;
namespace AntlrTest02
{
public class HelloVisitor : HelloBaseVisitor<object>
{
public override object VisitR([NotNull] HelloParser.RContext context)
{
Console.WriteLine(context.ID());
return base.VisitR(context);
}
}
}
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Antlr4.Runtime;
namespace AntlrTest02
{
class Program
{
static void Main(string[] args)
{
string parsedString = "hello abc";
if (args.Length > 0) parsedString = args[0];
var inputStream = new AntlrInputStream(parsedString);
var lexer = new HelloLexer(inputStream);
var commonTokenStream = new CommonTokenStream(lexer);
var parser = new HelloParser(commonTokenStream);
HelloParser.RContext context = parser.r();
HelloVisitor visitor = new HelloVisitor();
visitor.Visit(context);
}
}
}
- 実行結果
abc