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();
        }
    }
}
```