前回の記事はこちら
今回は前回に続いて構文解析器を作っていきます。
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());
}
}
次回は、ターコイズ言語のインタープリタを作り、動かしていきます。
次回の記事はこちら