0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Posted at

前回までで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という仕組みを使う。
そのため、grammarファイルで演算の左のexpressionをleft,右のexpressionをrightとラベルを付ける。
そのために、grammarファイルに下記のようにleft=,right=を追記する。
またカッコについても大本のexpressionと区別するためinsideのラベルを付ける。

	| left=expression MULTI right=expression	# Multiplication

Visitorクラス作成

CalculatorBaseVisitorを継承して作成。

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.を付ける必要あるか。
付ける場合と付けない場合の違いはなにか。
後日調べる?

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?