return 文
引き続き、構文解析器(パーサー) を拡張していきます。今回は return文 をパースできるようにします。
パーサーの拡張拡張の方法は簡単です。
- AST を定義する。
- Ast を返す PrseXxx() を定義する。
- ParseXxx() を呼び出す。
return文をいくつか具体例を挙げると以下のような感じです。
return 1;
return x;
return add(1, 2);
リテラルや識別子、関数の呼び出し結果が考えられます。抽象的にreturn文の構文規則について考えると次のようになります。
return <expression>;
return の後ろに返却する値を生成する式が来るだけです。例によってまだ ParseExpression() は実装されていないので、セミコロンまで読み飛ばす仮実装で済ませます。式がパースできるようになれば、let文のパースと合わせて修正します。
実装
AST の定義
ではreturn文を表す抽象構文木を定義します。名前は ReturnStatement
です。
Ast/Expressions/Return
using Gorilla.Lexing;
namespace Gorilla.Ast.Statements
{
public class ReturnStatement: IStatement
{
public Token Token { get; set; }
public IExpression ReturnValue { get; set; }
public string TokenLiteral() => this.Token.Literal;
}
}
式なので IStatement
クラスを実装し、プロパティで return トークンと返す値を表す式を持ちます。
テストを書く
では実装前にテストを作成します。テストはlet文とあまり変わりません。式のパースがまだなのでテストできるのはちゃんと ReturnStatement が来るかどうかくらいです。
[TestMethod]
public void TestReturnStatement1()
{
var input = @"return 5;
return 10;
return = 993322;";
var lexer = new Lexer(input);
var parser = new Parser(lexer);
var root = parser.ParseProgram();
this._CheckParserErrors(parser);
Assert.AreEqual(
root.Statements.Count, 3,
"Root.Statementsの数が間違っています。"
);
foreach (var statement in root.Statements)
{
var returnStatement = statement as ReturnStatement;
if (returnStatement == null)
{
Assert.Fail("statement が ReturnStatement ではありません。");
}
Assert.AreEqual(
returnStatement.TokenLiteral(), "return",
$"return のリテラルが間違っています。"
);
}
}
このテストを通すために実装をしていきます。
ParseReturnStatement
ParseReturnStatement()
は、現在のトークンからreturn文をパースして ReturnStatement を返します。
Parsing/Parser.cs
public class Parser
{
// ..
public IStatement ParseStatement()
{
switch (this.CurrentToken.Type)
{
case TokenType.LET:
return this.ParseLetStatement();
case TokenType.RETURN:
return this.ParseReturnStatement();
default:
return null;
}
}
public ReturnStatement ParseReturnStatement()
{
var statement = new ReturnStatement();
statement.Token = this.CurrentToken;
this.ReadToken();
// TODO: 後で実装。
while (this.CurrentToken.Type != TokenType.SEMICOLON)
{
// セミコロンが見つかるまで
this.ReadToken();
}
return statement;
}
}
式のパースはセミコロンまで読み飛ばすことでひとまず無視して、あとで戻ってきましょう。あとはlet文と同じです。というより “return” トークンの確認のみなのでそれより簡単です。呼び出し元も ParseStatement()
に条件分岐を1つ追加するだけです。簡単ですね。
これで先ほどのテストが通ります。
まとめ
return文のパースを実装しました。実装はすでにあるパーサーを拡張する形で作りました。式のパースは相変わらず後まわしにしています。
次はようやく式のパースに移るのですが、なぜ先にreturn文の構文解析を先に実装したかというと、作成中のインタプリタ(Gorilla)の構文規則において、文はletとreturnしかないからです!! つまり、あとはすべて式となります。いまのところ..。
ifも式だし、関数定義も式です。例外が式文と呼ばれるもので、1つの式をラップした文のことです。
大抵のスクリプト言語だと、式だけからなる文というのを持つ。
1 + 1;
このコードは Javascript だと有効です。しかしこのような式だけからなる文を許さない言語もあります。C#がまさにそうです。
Gorilla は、式文を用いて1つの式を1つの文として扱えるようにします。
次回は式の構文解析に取り掛かります。以上
コメントを書く