言語理論とコンパイラ
第九回:
上向き構文解析の原理
2013 年 6 月 6 日
http://www.sw.it.aoyama.ac.jp/2014/Compiler/lecture9.html
Martin J. Dürst
© 2005-14 Martin
J. Dürst 青山学院大学
今日の予定
前回のまとめ
- 下向き構文解析とバックトラック
- 再帰的下向き構文解析
- 概要: 非終端記号ごとに関数
- 左再起への対応
- 下向き構文解析の限界: 左再起、先読み
前回の文法の発展
- 出発点 (parser.c):
- E → T '+' T | T
T → number
- 三つ以上の被演算子への対応 (parserA.c):
- E → T '+' E | T
T → number
- 引き算の追加 (parserB.c):
- E → T '+' E | T '-' E | T
T → number
- 右結合から左結合への変更 (parserC.c):
- E → E '+' T | E '-' T | T
T → number
- 左再帰への対応 (parserD.c):
- E → T ME
ME → '+' T ME | '-' T ME | ε
T → number
更なる文法の発展
- EBNF の利用:
書き換え規則の中の繰り返しを関数内の繰り返しと置き換える
- 掛算への対応
- 優先度への対応
下向き構文解析と上向き構文解析
- 下向き構文解析 (top-down parsing):
- 解析木を上から (初期記号から) 作る
- 上向き構文解析 (bottom-up parsing):
- 解析木を下から (終端記号から) 作る
- 途中に複数の (小さな) 解析木がある
下向き構文解析と上向き構文解析
|
下向き構文解析 |
上向き構文解析 |
一般的な方法 |
バックトラック |
動的プログラミング (CYK アルゴリズム) |
広く使われている方法 |
再帰的下向き構文解析 |
LR 法 |
導出の順番: 最左導出と最右導出
最左導出の場合に、いつも最も左の非終端記号を置き換える
最右導出の場合に、いつも最も右の非終端記号を置き換える
簡単な文法の例:
E → E '+' T
T → integer
入力例: 5 + 7 + 3
解析方法の種類
- LL: 左から入力を読んで、最左導出
- LR: 左から入力を読んで、最右導出 (逆順)
- LL(1): LL で、先読みはトークン一つに限定
- LR(1): LR で、先読みはトークン一つに限定
- LALR: LR (1) の一種で、yacc、bison など幅広く使用
ラベルは文法にも使用: 「この文法は
LL(1)」(で解析可能)
LALR 構文解析の原理
スタックを使って読んだトーケンや途中の非終端記号を蓄積
オートマトンを使ってできるだけ簡単な操作で次のステップを決定
(LA)LR 構文解析の三つのオペレーション
- shift:
トークンを一個読んで、そのトークンをスタックにステートと一緒に詰める
- reduce:
スタックの上部にあるトークンや非終端記号を文法規則を使って一つの非終端記号に変換
reduce 後には go to を使って状態を移動することがある
- accept: 入力を受理、作業を終了
Shift と Reduce の具体例
上向き構文解析の利点と問題点
bison の概要
- yacc: yet another compiler compiler
- Unix とともに普及
- compiler compiler: コンパイラを作るコンパイラ
- yet another: もう一つ
(当時compiler compiler が流行、名前に困った)
(以後、YA.. は広く使われるようになった)
- bison: yacc の gnu 版
演習例: 簡単な電卓
スタートのためのファイル: makefile,
calc.y, calc.lex
bison のマニュアル