LoginSignup
2
2

More than 3 years have passed since last update.

ANTLR4をC#から使ってみる #01

Last updated at Posted at 2020-06-06

環境

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

2
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
2