ANTLR4をC#から使ってみる #04 Listenerでの計算機の完成


前回の記事までで、下記がわかった。

  • C#でANTLR4を利用するためのVisual Studioの設定方法と、プロジェクトを作った後の設定方法
  • ListenerのではExit***の挙動
  • Exit***リスナーとスタックを使うとListenerで計算機を作れること

実装

Listenerクラスへのスタックの作り込み

Stack<float> stack = new Stack<float>();

演算子(+-*/)の実装

        public override void ExitAddition([NotNull] CalculatorParser.AdditionContext context)
        {
            base.ExitAddition(context);
            stack.Push(stack.Pop() + stack.Pop());
        }
        public override void ExitDivision([NotNull] CalculatorParser.DivisionContext context)
        {
            base.ExitDivision(context);
            var right = stack.Pop();
            var left = stack.Pop();
            stack.Push(left / right);
        }
    ・・・

スタックからの計算結果の取り出し用のメソッド作成

        public float getAnswer()
        {
            return stack.Pop();
        }

mainメソッドからの計算結果の取り出しと出力

            Console.WriteLine(listener.getAnswer());

実行結果

>Calculator01_3.exe 3-1
2

>Calculator01_3.exe 3-1*5
-2

>Calculator01_3.exe 3-1*5/2
0.5

>Calculator01_3.exe (3-1)*5/2
5

>Calculator01_3.exe 1*2+3/4
2.75

最終的なソース

Calculator.g4
grammar Calculator;

PLUS : '+';
MINUS: '-';
MULTI: '*';
DIV  : '/';
NUMBER : [0-9]+;
WHITESPACE : [ \r\n\t]+ -> skip;

expression 
    : NUMBER                        # Number
    | '(' expression ')'            # Parentheses
    | expression MULTI expression   # Multiplication
    | expression DIV expression     # Division
    | expression PLUS expression    # Addition
    | expression MINUS expression   # Subtraction
;
Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Antlr4.Runtime;

namespace Calculator01_3
{
    class Program
    {
        static void Main(string[] args)
        {
            string parsedString;
            if (args.Length == 0)
            {
                Console.WriteLine("引数に数式を指定してください");
                return;
            }
            else
            {
                parsedString = args[0];

            }
            var inputStream = new AntlrInputStream(parsedString);
            var lexer = new CalculatorLexer(inputStream);
            var commonTokenStream = new CommonTokenStream(lexer);
            var parser = new CalculatorParser(commonTokenStream);

            CalculatorListener listener = new CalculatorListener();
            parser.AddParseListener(listener);
            var graphContext = parser.expression();
            Console.WriteLine(listener.getAnswer());
        }
    }
}
CalculatorListener.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Antlr4.Runtime.Misc;

namespace Calculator01_3
{
    class CalculatorListener : CalculatorBaseListener
    {
        Stack<float> stack = new Stack<float>();
        public override void ExitNumber([NotNull] CalculatorParser.NumberContext context)
        {
            base.ExitNumber(context);
            stack.Push(float.Parse(context.NUMBER().GetText()));
        }
        public override void ExitAddition([NotNull] CalculatorParser.AdditionContext context)
        {
            base.ExitAddition(context);
            stack.Push(stack.Pop() + stack.Pop());
        }
        public override void ExitDivision([NotNull] CalculatorParser.DivisionContext context)
        {
            base.ExitDivision(context);
            var right = stack.Pop();
            var left = stack.Pop();
            stack.Push(left / right);
        }
        public override void ExitMultiplication([NotNull] CalculatorParser.MultiplicationContext context)
        {
            base.ExitMultiplication(context);
            stack.Push(stack.Pop() * stack.Pop());
        }
        public override void ExitSubtraction([NotNull] CalculatorParser.SubtractionContext context)
        {
            base.ExitSubtraction(context);
            var right = stack.Pop();
            var left = stack.Pop();
            stack.Push(left - right);
        }
        public float getAnswer()
        {
            return stack.Pop();
        }
    }
}