LoginSignup
0
1

Unityでのゲーム作り日記#5 ~自作言語の構文解析器を作る 後半~

Last updated at Posted at 2024-01-17

前回の記事はこちら

今回は前回に続いて構文解析器を作っていきます。

BNF (バッカスナウア記法を決める

前回、書き忘れてしまっていたのですが、結構大事なのでターコイズ言語のBNFを書いておきます。
BNFを知らない人のためのwikiへのリンク

<factor> ::= 数字 | 識別子 | 識別子 "(" argment ")" | "(" <assignmentOrDefineExp> ")"
<signExp> ::= "-" <factor> | "+" <factor> | <factor>
<expoExp> ::= <signExp> "^" <expoExp> | <signExp>
<mulDivModExp> ::= <expoExp> "*" <expoExp> | <expoExp> "/" <expoExp> | <expoExp> "%" <expoExp>
<addSubExp> ::= <mulDivModExp> "+" <mulDivModExp> | <mulDivModExp> "-" <mulDivModExp>
<compareExp> ::= <addSubExp> "<" <addSubExp> | <addSubExp> ">" <addSubExp> | <addSubExp> "<=" <addSubExp> | <addSubExp> ">=" <addSubExp> | <addSubExp> "==" <addSubExp> | <addSubExp> "!=" <addSubExp>
<assignmentOrDefineExp> ::= "var" 識別子 "=" <compareExp> | "func" 識別子 "(" <parameter> ")" "{" <suite> "}" | <compareExp>
<statement> ::= "if" "(" <assignmentOrDefineExp> ")" ( "{" <suite> "}" | <assignmentOrDefineExp> ) [ "else" ( "{" <suite> "}" | <assignmentOrDefineExp> ) ] | "while" "(" <assignmentOrDefineExp> ")" ( "{" <suite> "}" | <assignmentOrDefineExp> )
<program> ::= <suite>
<parameter> ::= <parameter> 識別子 | 識別子
<argment> ::= <argment> <compareExp> | <compareExp>
<suite> ::= <suite> <assignmentOrDefineExp> | <assignmentOrDefineExp>

このBNF通りに作っていきます。
ただし、ここではfor文のことを書いていませんが、for文も作るかもしれません

実際に作っていく

実はまだ表示用のprint関数などのネイティブ関数?を実装していないので、できたら更新します。

TurquoiseParser.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Tokens;
using System;
using TurquoiseAstNode;

public class TurquoiseParser
{
   private Lexer lex;

   public TurquoiseParser(Lexer lex)
   {
       this.lex = lex;
   }

   private void token(string op)
   {
       Token t = lex.read();
       if (t.getValue().ToString() != op)
           throw new TurquoiseException($"Invalid token \'{t.getValue()}\' in line {t.getLineno()}.");
   }

   private bool istoken(string op, int peekNum = 0)
   {
       return lex.peek(peekNum).getValue().ToString() == op;
   }

   private BlockExpression blockOrOneStatement() //{...} か ... を返す
   {
       List<IExpression> body = new List<IExpression>();
       if (istoken("{"))
       {
           token("{");

           while (!istoken("}"))
           {
               body.Add(statement());
           }

           token("}");
       }
       else
       {
           body.Add(statement());
       }

       BlockExpression bodyExpression = new BlockExpression(body.ToArray());

       return bodyExpression;
   }

   private IExpression factor() //数値、識別子(変数、関数)、(...)
   {
       if (istoken("("))
       { //if (...)
           token("(");
           IExpression e = assignmentOrDefineExp(); //expression
           token(")");

           return e;
       }

       Token t = lex.read();
       switch (t.GetType())
       {
           case Type @_ when @_ == typeof(Number):
               return new NumberExpression((double)t.getValue());
           case Type @_ when @_ == typeof(Identifier):
               string identifier = t.getValue().ToString();

               if (istoken("("))
               {
                   token("(");

                   List<IExpression> argment = new List<IExpression>();
                   while (!istoken(")"))
                   {
                       argment.Add(compareExp());
                   }

                   token(")");

                   return new FunctionExpression(identifier, new ParameterExpression(argment.ToArray()));
               }

               return new VariableExpression(t.getValue().ToString()); //変数の場合
           default:
               throw new TurquoiseException($"Bad token '{t.getValue().ToString()}'.");
       }
   }

   private IExpression signExp() //-...
   {
       if (istoken("-"))
       {
           token("-");
           IExpression right = factor();
           return new UnuOpExpression(right, "-");
       }
       else if (istoken("+"))
       {
           token("+");
           IExpression right = factor();
           return right;
       }
       return factor();
   }

   private IExpression expoExp() // a ^ b (aのb乗)
   {
       IExpression left = signExp();

       if (istoken("^"))
       {
           token("^");
           IExpression right = expoExp();
           return new BinOpExpression(left, right, "^");
       }
       return left;
   }

   private IExpression mulDivModExp() //掛け算 割り算 余り
   {
       IExpression left = expoExp();

       while (istoken("*") || istoken("/") || istoken("%"))
       {
           if (istoken("*"))
           {
               token("*");
               IExpression right = expoExp();
               left = new BinOpExpression(left, right, "*");
           }
           else if (istoken("/"))
           {
               token("/");
               IExpression right = expoExp();
               left = new BinOpExpression(left, right, "/");
           }
           else if (istoken("%"))
           {
               token("%");
               IExpression right = expoExp();
               left = new BinOpExpression(left, right, "%");
           }
       }
       return left;
   }

   private IExpression addSubExp() //足し算 引き算
   {
       IExpression left = mulDivModExp();

       while (istoken("+") || istoken("-"))
       {
           if (istoken("+"))
           {
               token("+");
               IExpression right = mulDivModExp();
               left = new BinOpExpression(left, right, "+");
           }
           else if (istoken("-"))
           {
               token("-");
               IExpression right = mulDivModExp();
               left = new BinOpExpression(left, right, "-");
           }
       }
       return left;
   }

   private IExpression compareExp() //比較演算子
   {
       IExpression left = addSubExp();

       while (istoken("<") || istoken(">") || istoken("<=") || istoken(">=") || istoken("==") || istoken("!="))
       {
           if (istoken("<"))
           {
               token("<");
               IExpression right = addSubExp();
               left = new BinOpExpression(left, right, "<");
           }
           else if (istoken(">"))
           {
               token(">");
               IExpression right = addSubExp();
               left = new BinOpExpression(left, right, ">");
           }
           else if (istoken("<="))
           {
               token("<=");
               IExpression right = addSubExp();
               left = new BinOpExpression(left, right, "<=");
           }
           else if (istoken(">="))
           {
               token(">=");
               IExpression right = addSubExp();
               left = new BinOpExpression(left, right, ">=");
           }
           else if (istoken("=="))
           {
               token("==");
               IExpression right = addSubExp();
               left = new BinOpExpression(left, right, "==");
           }
           else if (istoken("!="))
           {
               token("!=");
               IExpression right = addSubExp();
               left = new BinOpExpression(left, right, "!=");
           }
       }
       return left;
   }

   private IExpression assignmentOrDefineExp() //代入系 一応式扱い
   {
       if (istoken("var"))
       {
           token("var");

           string name = lex.read().getValue().ToString();
           token("=");
           IExpression value = compareExp();

           return new AssignmentVarExpression(name, value);
       }
       else if (istoken("func"))
       {
           token("func");

           string name = lex.read().getValue().ToString();

           token("(");

           List<IExpression> parameter = new List<IExpression>();
           while (!istoken(")"))
           {
               parameter.Add(factor());
           }
           ParameterExpression parameterExpression = new ParameterExpression(parameter.ToArray());

           token(")");

           token("{");

           List<IExpression> expressions = new List<IExpression>();
           while (!istoken("}"))
           {
               expressions.Add(statement());
           }
           BlockExpression Process = new BlockExpression(expressions.ToArray());

           token("}");

           return new DefineFuncExpression(name, new FunctionsNameAndValue(new FunctionExpression(name, parameterExpression), Process));
       }

       return compareExp();
   }

   private IExpression statement() //文
   {
       if (istoken("while"))
       {
           token("while");

           token("(");

           IExpression expression = assignmentOrDefineExp();

           token(")");

           BlockExpression bodyExpression = blockOrOneStatement();

           return new Statement(new string[] { "while" }, new IExpression[] {expression, bodyExpression });
       }
       else if (istoken("if"))
       {
           token("if");

           token("(");

           IExpression expression = assignmentOrDefineExp();

           token(")");

           BlockExpression ifBlock = blockOrOneStatement();

           Statement ifStatement = new Statement(new string[] { "if" }, new IExpression[] { expression, ifBlock });

           if (istoken("else"))
           {
               token("else");

               BlockExpression elseBlock = blockOrOneStatement();

               ifStatement = new Statement(new string[] { "if" }, new IExpression[] { expression, ifBlock, elseBlock });
           }

           return ifStatement;
       }

       return assignmentOrDefineExp();
   }

   public IExpression program() //プログラム全体
   {
       List<IExpression> expressions = new List<IExpression>();

       while (!istoken("EOF"))
       {
           IExpression result = statement();
           expressions.Add(result);

           Debug.Log($"result:{result.Evaluate().getDouble()}"); //後で消す
       }
       return new BlockExpression(expressions.ToArray());
   }
}

次回は、ターコイズ言語のインタープリタを作り、動かしていきます。

次回の記事はこちら

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