Dev Study
← Rust で小さなブラウザを作る コース

25. 構文解析と AST — 生成規則 1 つが関数 1 つ(再帰下降)

は、の列を(AST)にします。書き方はで、 の 1 つを関数 1 つにします。parse_statement は Statement、parse_variable_declaration は VariableStatement、parse_function_declaration は FunctionDeclaration、parse_assignment_expression は AssignmentExpression、parse_additive_expression は AdditiveExpression、parse_left_hand_side_expression は MemberExpression と CallExpression、parse_primary_expression は PrimaryExpression です。

優先順位は関数の呼び出しの深さで表します。AssignmentExpression が AdditiveExpression を呼び、それが LeftHandSideExpression を呼び、それが PrimaryExpression を呼ぶ。深い方が先に結びつくので、a = 1 + f(2) は「代入の右辺が足し算で、その右の項が呼び出し」という木になります。本物の仕様には AdditiveExpression と LeftHandSideExpression の間に MultiplicativeExpression(* / %)などの段がありますが、この実装は + と - だけなので飛ばしています。

結合の向きは 2 通りの書き方で出します。AdditiveExpression は(1 - 2 - 3 は (1 - 2) - 3)なので、仕様の左再帰(AdditiveExpression : AdditiveExpression + ...)をループに書き換えて、左から順に BinaryExpression を積みます。AssignmentExpression は右結合(a = b = 1 は a = (b = 1))なので、右辺で自分自身を再帰呼び出しします。MemberExpression と CallExpression は、PrimaryExpression のあとに . 名前 と (引数) をループで何回でも続けられる形にしてあります(document.getElementById('t').textContent のように)。

失敗は ParseError(期待したものと、実際に来たトークン、または入力の終わり)で返します。下のコードを実行すると、5 つのプログラムの AST が字下げで出て、最後の 2 つ(var = 1 と f( )が SyntaxError になります。

本物のブラウザでは: の src/parsing/parser.cc は再帰下降で AST(src/ast/ast.h)を作り、すぐに BytecodeGenerator が のに落とします。まだ呼ばれない関数は preparser.cc が構文だけ確かめて本解析を後回しにします()。 も Parser.cpp → BytecodeEmitter.cpp の順で、「lazy parsing」と GC を介さない Stencil 形式を使います。どちらも AST を直接歩いて実行はせず、一度バイトコードにします。この回の AST を歩く器は、その手前で止めた形です。

開発のどこに活かすか: 再帰下降は、手で書ける構文解析の中で一番読みやすく、業務で出会う「式を評価したい」要求(検索フィルタ、料金計算式、権限条件、設定のテンプレート)にちょうど合います。「優先順位は呼び出しの深さ、左結合はループ、右結合は再帰」の 3 点を押さえれば、電卓から簡単な言語まで同じ形で書けます。

点線の付いた語はタップすると説明が出ます。用語集を開く →

① 1 + 2 - 3 と a = b = 1 の木

左結合はループで左に積む、右結合は再帰で右に伸ばす
BinaryExpression -左: (1 + 2)、右: 3
AssignmentExpressiontarget: a、value: (b = 1)
BinaryExpression +左: 1、右: 2
AssignmentExpressiontarget: b、value: 1
  • ・評価は葉から根へ。(1 + 2) を先に 3 にしてから 3 - 3
  • ・a = (b = 1) は、先に b に 1 が入り、その値 1 が a にも入る

木の形が決まれば評価の順番は決まります。構文解析の仕事は「どう結びつくか」を木の形で確定することです。

▶ サンプルコードを実行してみよう(自由に書き換えてOK)

Rust

手元で動かす(配布物)

図やコードに出てくる用語(タップで説明)

サンプルコードで身につける(3本) →

学んだ概念を、素朴な例から実務寄りの例まで3つの具体例で確認します。

公式ドキュメントで詳しく ↗