言語理論とコンパイラ
第七回:
下向き構文解析の原理
2014 年 5月 23日
http://www.sw.it.aoyama.ac.jp/2014/Compiler/lecture7.html
Martin J. Dürst
© 2005-14 Martin
J. Dürst 青山学院大学
目次
- 前回のまとめと宿題
- 文法の記述方法の種類
- 文法の作り方
- 文法の作成
- 構文解析の目的
- 解析木と構文木
- 下向き構文解析
- 構文解析の要点
前回のまとめ
- 正規表現やそれと同等の表現力を持つツールには限界
(例: 括弧の入れ子構造など)
- 正規表現などは構文解析には使えないが、字句解析には有効
- コンパイラで字句解析と構文解析を分けると構造が鮮明、実行が高速
- (プログラム)
言語などの文法は文脈自由文法で記述可能
- 文脈自由文法はプッシュダウンオートマトンで受理可能
- プッシュダウンオートマトンでは決定性より非決定性のものが強力だが、実装は複雑で遅い
前々回からの宿題
解答例:[都合により削除]
追加: C のコメントの正規表現
読みやすさのため、コメントを /x x/
にし、スペースを使用
第一案: /x .* x/
問題: /xx/ /xx/
をまとめて認識
第二案: /x [^x]* x/
問題: /xxx/
を認識しない
第三案: /x ([^x]|x[^/])* x/
問題: /x xx/ /x
x/
をもとめて認識
第四案: /x ([^x]|x+[^/])* x/
問題: 以前と同様
第五案: /x ([^x]|x+[^/x])* x/
問題: /x xx/
を認識しない
第六案: /x ([^x]|x+[^/x])* x+/
完成!
参考: Mastering Regular Expressions, Jeffrey E.F. Friedl, pp. 168,...
前回の宿題
C プログラム言語、Java、Ruby、XML
など知っている言語やデータ形式の文法を調べなさい。
[都合により削除]
文法の作成手順
- 言語の簡単な実例を記述
- 実例をトークンの種類に変更 (字句解析の結果)
- 実例の現象を命名 (例: ...式、...文など)
- 書き換え規則の案
- もうちょっと複雑な例で使えるかどうかを検討、修正
文法作成の例 1:
- 目的:
5 + 3
のような式のための文法
- 作成手順 2 の効用
文法作成の例 2
- 目的:
5 + 3 * 7
のような式のための文法
文法作成の例 3
- 目的:
5 + 3 * (7 + 2)
のような式のための文法
構文解析の例 1
- 目的:解析のアルゴリズムを検討
- 例 3 の
5 + 3 * (7 + 2)
を記述する文法で実験
構文解析の目的
- 入力の受理・非受理の判定
- 入力の処理:
- 分かりやすいエラーメッセージ
構文解析の結果: 解析木と構文木
- 解析木 (parse tree, concrete syntax tree):
- 葉は終端記号と (分析の途中の場合)
非終端記号
- 節は非終端記号
- 用途: 文法や解析方法の研究・調査
- 構文木 (抽象構文木、abstract syntax tree):
- 葉は終端記号の一部 (識別子、定数など)
- 節は非終端記号の一部に相当するが、終端記号の一部
(演算子、予約語) などをラベルに使用
- 一部の終端記号は無視 (括弧類など)
解析木と構文木の例
文法作成の例 3 の文法
入力の例:
5 + 3 * (7 + 2)
解析の実装: 下向き解析と上向き解析
- 下向き構文解析 (top-down parsing):
- 解析木を上から (初期記号から) 作る
- 上向き構文解析 (bottom-up parsing):
- 解析木を下から (終端記号から) 作る
- 途中に複数の (小さな) 解析木がある
下向き解析の一般概要
- 文法を初期記号から解析木を作成
- 深さ優先で左から解析木を作成
- 選択肢があれば順番に試してみる
- 終端記号まで解析木を展開したら、入力のトークンと比較
- 一致の場合、続く
- 一致しない場合、戻って
(バックトラック、backtracking)、違う選択を試す
バックトラックの要点
バックトラックの遅さへの対策:
- 文法の書き方に注意:
バックトラックが減るように文法を修正
- 途中の結果を記憶 (packrat parser)
次のトークンしか見なくてよい文法に限定した方が望ましい
文法の記述の種類
単純な文法
- 例: C
の文法
- メタ記号は
→
/:
と |
だけ
- 理論で使う文法規則と同じ
BNF
- 例: Java
の文法
- メタ記号は
→
/:
(Java
の文法では改行)、
|
、{}
、[]
、()
など
(Java の文法で書体の差に注目)
- 書き換え規則の右側に正規表現みたいなもの
文法規則と BNF
文法規則には色々な書き方がある:
- 一番単純な書き方: 書き換え規則の羅列
- 同じ左辺の書き換え規則の右辺を
|
で列挙
⇒ 根本的に 1. と変わらない (syntactic sugar/糖衣構文)
- 正規表現の
?
に相当するもの (あり/無し)
の追加 (よく [
...]
で書く)
⇒ 二つの構文規則に分けることが可能
- 正規表現の
*
に相当するもの (よく
{
...}
で書く)
⇒ 書き換えが可能
上記の拡張を含む文法規則は BNF (Backus-Naur Form), EBNF
(Extended...), ABNF (Augmented...) などという
BNF の書き換え
M → a {N} b
⇒
M → a b | a NList b
NList → N | NList N
構文解析の難しさ
- 決定性と非決定性のプッシュダウンオートマトンの受理能力が違う
- 一般の文脈自由文法を受理できるアルゴリズムは
O(n3)
- O(n)
のアルゴリズムが好ましいが、そのために文法の制限が必要
- 構文解析の研究では長年、様々な解析方法が研究されてきた:
- 人間に分かりやすい文法
- 制限が少ない文法
- 手作業で解析機が実装できる文法
- 自動で解析機が実装できる文法
一般的な構文解析の方法
(Cocke–Younger–Kasami アルゴリズム)
- 文法を (Chomsky) 標準形に変換:
A → BC、A →
a、S → ε のみ可能
- 入力の長さ 1, 2, ... の部分列を検討
- (長さ 3 以上の部分列の場合)
部分列の様々な分割を検討
(動的計画法、DA
の行列計算と同様)
- 可能な書き換え規則を全てチェック、記憶
- 計算量は O(n3 · g) (g
は文法の大きさ)
来週への宿題
ノートパソコンの持参