式を括弧でグループ化する 式を括弧でくくって演算の優先度を調整することができます。一般的な計算式と同じ考えです。 例えば 1 + 2 * 3 この式は (1 + (2 * 3)) となります。 括弧を付けた式 (1 + 2) * 3 は ((1 + 2) * 2) とならないといけません。括弧内の式はより高い優先度で計算されます。 この違いを式の構文解析処理に実装します。優先度の実装は思っているほ […]
構文解析器の拡張 ここまでで、式の構文解析を完成させることができました。return文、let文についても実装済です。 今回は真偽値リテラルの構文解析処理の実装を行います。 テスト用ヘルパ関数の作成 具体的な実装に移る前に、テスト用のヘルパ関数を先に作成しましょう。 整数リテラルの実装時に _TestIntegerLiteral() というヘルパ関数を作成しました。同様に _TestIdentif […]
Pratt 構文解析 Pratt構文解析 という手法で式の構文解析を実装してきました。ここまでの実装で演算子の優先度を考慮したうえで、正しいASTノードを構築できるようになりました。 しかし実装がうまくいったのはテストを作成することで確認できましたが、詳しい説明は端折っていました。詳しい理論はぶっちゃけわからないのですが、どのように動作するかを見ていこうと思います。 VSにのデバッグ機能は優秀です […]
前置演算子 ここでは前置演算子がくっついた式の解析を実装します。難しいことはありません。識別子も数値リテラルも、前置演算子の解析関数として関連付けて処理しました。それと同様の手順で進めます。 実装する前置演算子は、否定 ! と マイナス - です。それぞれ以下のような式を解析します。 -5; !foo; 1 + -2; 中置演算子の直後に前置演算子が来るパターンも有効です。 次のような構造をしてい […]
式の構文解析に取り掛かる これまでに文の解析、let文とreturn文についての構文解析を実装しました。これで Gorilla の実装における文は、基本的にこの2つのみです。ということで、ようやく式の解析に取り掛かります。 式の構文解析は、構文解析における本丸です。ここが一番厄介であり、面白い部分でもあります。 式の解析は一筋縄ではいかない 式の1つである四則演算による数式を考えます。 1 + 2 […]
式の構文解析の前に 式の構文解析に移りたいのですが、先にちょっとした確認用の機能を作っておきます。これが済んだら式の解析です。 ASTからソースコードを復元する機能 今回は、デバッグ用に使える機能を追加していきます。ASTからソースコードを作成する機能です。これがあると、後の式の解析機能を実装するのが楽になります。 ToCode() INode に ToCode() を追加します。すべてのASTは […]
return 文 引き続き、構文解析器(パーサー) を拡張していきます。今回は return文 をパースできるようにします。 パーサーの拡張拡張の方法は簡単です。 AST を定義する。 Ast を返す PrseXxx() を定義する。 ParseXxx() を呼び出す。 return文をいくつか具体例を挙げると以下のような感じです。 return 1; return x; return add(1 […]
構文解析器にエラー出力機能を実装する 構文エラーが無視されてしまう 前回はlet文の構文解析の一部を実装しました。その過程で ExpectPeek() で先読み結果を判定する処理を実装し、必要なトークンが存在するかどうかを見て処理を分岐しました。具体的には "=" があるかどうかを以下のコードで判定しています。 Parsing/Parser.cs public class Pa […]
構文解析器(パーサー) 構文解析器(パーサー)の役割は、入力データから何らかのデータ構造を構築することです。データ構造はたいてい構文木や抽象構文木などの階層的な構造で定義され、入力に対して構造化された表現を与えます。また、その過程で入力が正しい構文かどうかをチェックします。 多くの場合構文解析器は字句解析器の後におかれ、字句解析器から得られたトークン列を入力として受け取ります。 今回作成する構文解 […]
REPLを作成し字句解析を行う 前回までで字句解析器の実装はひとまず完了となりました。ただ、ここまではエントリポイントとなる main() の実装を行っていません。 字句解析器を作成し、その動作をテストコードで確認しただけでした。なのでデフォルトのメイン関数の実装が残ったままで、おそらく実行すると “Hello World” が出力されるだけです。 今回は REPL を作成 […]