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

[前回](https://qiita.com/Hidenori/items/95949cb7e5ef102a7a40)まででVisitorの使い方がわかったので、計算機を完成させる。

# 実装
## 文法ファイル

```Calculator.g4
grammar Calculator;

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

expression 
	: NUMBER						# Number
	| '(' inside=expression')'			# Parentheses
	| left=expression MULTI right=expression	# Multiplication
	| left=expression DIV right=expression		# Division
	| left=expression PLUS right=expression	# Addition
	| left=expression MINUS right=expression	# Subtraction
;

```

演算(+ - / *)の左辺 右辺を区別するために、[Rule Element Labels](https://github.com/antlr/antlr4/blob/master/doc/parser-rules.md#rule-element-labels)という仕組みを使う。
そのため、grammarファイルで演算の左のexpressionをleft,右のexpressionをrightとラベルを付ける。
そのために、grammarファイルに下記のようにleft=,right=を追記する。
またカッコについても大本のexpressionと区別するためinsideのラベルを付ける。

```
	| left=expression MULTI right=expression	# Multiplication
```

## Visitorクラス作成
CalculatorBaseVisitor<float>を継承して作成。

```CalculatorVisitor.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Antlr4.Runtime.Misc;

namespace Calculator02_2
{
    class CalculatorVisitor: CalculatorBaseVisitor<float>
    {
        public override float VisitAddition([NotNull] CalculatorParser.AdditionContext context)
        {
            return base.Visit(context.left) + base.Visit(context.right);
        }
        public override float VisitDivision([NotNull] CalculatorParser.DivisionContext context)
        {
            return base.Visit(context.left) / base.Visit(context.right);
        }
        public override float VisitMultiplication([NotNull] CalculatorParser.MultiplicationContext context)
        {
            return base.Visit(context.left) * base.Visit(context.right);
        }
        public override float VisitSubtraction([NotNull] CalculatorParser.SubtractionContext context)
        {
            return base.Visit(context.left) - base.Visit(context.right);
        }
        public override float VisitParentheses([NotNull] CalculatorParser.ParenthesesContext context)
        {
            return base.Visit(context.inside);
        }
        public override float VisitNumber([NotNull] CalculatorParser.NumberContext context)
        {
            return float.Parse(context.NUMBER().GetText());
        }
    }
}
```

各演算から、左辺/右辺をvisit。数値は数値をそのまま返す。

## Program.cs
visitがfloatを返すようになったので、それを表示するように変更。

```Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Antlr4.Runtime;

namespace Calculator02_2
{
    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);
            var graphContext = parser.expression();
            CalculatorVisitor visitor = new CalculatorVisitor();
            Console.WriteLine(visitor.Visit(graphContext));
        }
    }
}
```

# 実行結果
```
>Calculator02_2.exe 3-1
2

>Calculator02_2.exe 3-1*5
-2

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

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

>Calculator02_2.exe 1*2+3/4
2.75
```

# メモ
Visitorから別のVisitorをvisitする際に、base.を付ける必要あるか。
付ける場合と付けない場合の違いはなにか。
後日調べる？