Table of Contents

ひと目で見るパイプライン

クイックスタートでは5行で構文解析を動かしてみましたね。
その5行の中では、実はいくつもの段階が 順番に起こっています。
この章では、その 全体の流れ(パイプライン) を大きな絵として見ます。
細部は それぞれの章で扱うので、ここでは「地図」だけ頭に入れてください。

テキストが結果になるまで

私たちの入力 a + a * a がどんな道をたどるのか、追いかけてみましょう。

   "a + a * a"            ← ① ただの文字の並び (テキスト)
        │
        ▼  字句解析 (Lexing)
   [a] [+] [a] [*] [a]    ← ② 意味のあるかけら = トークン(token)たち
        │
        ▼  構文解析 (Parsing)
        Expr              ← ③ 文法構造 = 構文木(parse tree)
       /  |  \
     Expr '+' Term
      |       / | \
     Term  Term '*' Factor
      ...
        │
        ▼  (その次の段階たち — 意味解析、コード生成 …)
     実行 / 翻訳 / コンパイル

それぞれの矢印が一つの 変換段階 です。

① テキスト → ② トークン : 「字句解析(Lexing)」

コンピュータにとって a + a * a は、最初は空白を含めて9文字でしかありません。
最初の段階は、これを 意味のある 最小単位(トークン) に切り分けることです。

  • a → 「名前(識別子)」トークン
  • + → 「足し算演算子」トークン
  • * → 「掛け算演算子」トークン
  • 空白 → 捨てる

この仕事をするのが 字句解析器(lexer) または トークナイザー(tokenizer) です。

② トークン → ③ 木 : 「構文解析(Parsing)」

ここでトークンたちが 文法に合うように並んでいるか を確認し、その構造を木にします。
たとえば a + a * a で掛け算が足し算より先にまとまるべきだということ(優先順位)も、この木の形で あらわれます。

a + a * a   →   a + (a * a)      ← '*'がより深く(先に)まとまる

この仕事をするのが パーサー(parser) です。
そして Janglim が力を注いでいるのが、まさにここです。

③ 木 → その次

構文木ができると、その後は目的に応じて分かれます。
電卓なら木をたどって値を計算し、 コンパイラなら意味を解析して機械語/LLVM IR に翻訳します。
Janglim が dogfooding(自己検証) 用に作った AJ言語 は、ここまで進みます。

LRパーサーの隠れた0段階: 「テーブルを前もって作る」

上の絵には、実は 隠れた段階 がもう一つあります。
パーサーはトークンを受け取る 前に、文法を分析して 構文解析表(parse table) という巨大な表を前もって計算します。

この表は「今こういう状態で、次のトークンがこれなら → こう行動せよ」をすべて書きとめた 行動指針書 です。
構文解析のとき、パーサーは考えずに、この表だけを見て機械的に動きます。
だから LR 構文解析は速くて正確です。

💡 だから LR パーサーを「決定的(deterministic)パーサー」と呼びます。 表の各マスには行動が きっちり一つ に定まっています。だからパーサーは毎瞬間、分かれ道で迷ったり後戻り(backtrack) したりせず、一本の道だけを進みます。このように 次の行動が常に一意に定まる パーサーを決定的パーサーと呼び、 LR がその代表です。(文法に 衝突 があったり、複数の分岐を探索する GLR/LGLR 方式を使うと決定的では なくなりますが — それは後ろの 衝突 の章で扱います。)

文法  ──(前もって分析)──▶  構文解析表  ──(構文解析のとき参照)──▶  決定(shift? reduce?)

この表を作るには FIRST/FOLLOWLR項目正準集合 といった概念が必要です。
この表を作る旅 こそがマニュアルの本論です。
私たちは FIRST/FOLLOWから 始めます。

コードの層(layer) — ほんの少しだけ

Janglim の内部は、いくつものモジュールが 層を重ねて 積み上がっています。
下に行くほど、より基礎的な部品です。

  ┌─────────────────────────────────────────────┐
  │  AJ言語 → 意味解析 → LLVM IR (コンパイラ)       │  ← 上段 (応用)
  ├─────────────────────────────────────────────┤
  │  パーサーエンジン: 正準集合 · 構文解析表 · ドライバ │
  ├─────────────────────────────────────────────┤
  │  文法(Grammar) コンテナ                         │
  ├─────────────────────────────────────────────┤
  │  シンボル · レキサー · FIRST/FOLLOW · 正規化 · 構文木 │  ← 核心部品の集まり
  ├─────────────────────────────────────────────┤
  │  トークン型 · 共通ヘルパー                       │  ← 下段 (基礎)
  └─────────────────────────────────────────────┘

おもしろい点: FIRST/FOLLOW が 下段の近く にありますね? コード構造の上では、とても基礎的な部品です。 でも 概念を学ぶ順番では、構文解析表の直前に学ぶのが一番よく理解できます(なぜ 必要なのかが、そのときに腑に落ちるからです)。だからこのマニュアルは コードの層の順番ではなく、理解の順番 で 進みます。各ページごとに「この概念はどの層/ファイルか」は、別に表示しておきます。

モジュール層の全体地図は、あとで 付録 で別にまとめます。

次の章

これで地図を手に入れたので、LR 構文解析の最初の核心概念に入っていきましょう。

👉 FIRST / FOLLOW