言語理論とコンパイラ
第七回:
文脈自由言語と構文解析
2012 年 6月 1日
http://www.sw.it.aoyama.ac.jp/2012/Compiler/lecture7.html
Martin J. Dürst
© 2005-12 Martin
J. Dürst 青山学院大学
目次
- 前回のまとめと宿題
- 文法の記述方法の種類
- 文法の作り方
- 構文解析の要点
- 解析木と構文木
- 上向き構文解析と下向き構文解析
前回のまとめ
- 正規表現やそれと同等の表現力を持つツールには限界
(例: 括弧の入れ子構造など)
- 正規表現などは構文解析には使えないが、字句解析には有効
- コンパイラで字句解析と構文解析を分けると構造が鮮明、実行が高速
- (プログラム)
言語などの文法は文脈自由文法で記述可能
- 文脈自由文法はプッシュダウンオートマトンで受理可能
- プッシュダウンオートマトンでは決定性より非決定性のものが強力だが、実装は複雑で遅い
第一回の宿題
(授業終了後に返却)
- 点数配分は字句解析: 30、構文解析:
30、アセンブリ言語: 40
- 構文解析木では + と - の順番の間違いが目立つ
- アセンブリ言語では
;
は命令の終了のではなく、コメントの開始
- 代入される変数 (grade) を LOAD する意味がない
前々回からの宿題: Flex
正解例: c_tokens.l (発展問題: c_tokens_advanced.l)
注意点: 浮動小数の正規表現、コメントの正規表現
コメントの正規表現
読みやすさのため、コメントを /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
プログラム言語など知っている言語やデータ形式の文法を調べなさい
(提出不要):
C プログラム言語の文法の例 (yacc
形式; typedef
に要注意)
Java プログラム言語の文法
(BNF 形式)
Ruby プログラム言語の文法
(図)
文法の記述の種類
単純な文法
- 例: C
の文法
- メタ記号は
→
/:
と |
だけ
- 理論で使う文法規則と同じ
(A|E)BNF
- 例: Java
の文法
- メタ記号は
→
/:
(Java
の文法では改行)、
|
、{}
、[]
など (Java
の文法では斜体と正体の差に注目)
- 書き換え規則の右側に正規表現みたいなもの
文法規則と BNF
文法規則には色々な書き方がある:
- 一番単純な書き方: 書き換え規則の羅列
- 同じ左辺の書き換え規則の右辺を
|
で列挙
⇒ 根本的に 1. と変わらない (syntactic sugar/糖衣構文)
- 正規表現の
?
に相当するもの (あり/無し)
の追加 (よく [
...]
で書く)
⇒ 二つの構文規則に分けることが可能
- 正規表現の
*
に相当するもの (よく
{
...}
で書く)
⇒ 書き換えが可能
上記の拡張を含む文法規則は BNF (Backus-Naur Form), EBNF
(Extended...), ABNF (Augmented...) などという
EBNF の書き換え
M → a {N} b
⇒
M → a b | a NList b
NList → N | NList N
文法作成の例
- 目的:
5 + 3 * (7 + 2)
のような式のための文法
文法の作成方法
- 言語の簡単な実例を記述
- 実例をトークンの種類に変更 (字句解析の結果)
- 実例の現象を命名 (例: ...式、...文など)
- 書き換え規則の案
- もうちょっと複雑な例で使えるかどうかを検討、修正
構文解析の目的
- 入力の受理・非受理の判定
- 入力の処理:
- 分かりやすいエラーメッセージ
構文解析の難しさ
- 決定性と非決定性のプッシュダウンオートマトンの受理能力が違う
- 一般の文脈自由文法を受理できるアルゴリズムは
O(n3)
- O(n)
のアルゴリズムが好ましいが、そのために文法の制限が必要
- 構文解析の研究では長年、様々な解析方法が研究されてきた:
- 人間に分かりやすい文法
- 制限が少ない文法
- 手作業で解析機が実装できる文法
- 自動で解析機が実装できる文法
一般的な構文解析の方法
(Cocke–Younger–Kasami アルゴリズム)
- 文法を (Chomsky) 標準形に変換:
A → BC、A →
a、S → ε のみ可能
- 入力の長さ 1, 2, ... の部分列を検討
- (長さ 3 以上の部分列の場合)
部分列の様々な分割を検討
(動的計画法、DA
の行列計算と同様)
- 計算量は O(n3 · g) (g
は文法の大きさ)
構文解析の結果: 解析木と構文木
- 解析木 (parse tree, concrete syntax tree):
- 葉は終端記号と (分析の途中の場合)
被終端記号
- 節は非終端記号
- 文法や解析方法の研究に使われる
- 構文木 (抽象構文木、abstract syntax tree):
- 葉は終端記号の一部 (識別子、定数など)
- 節は被終端記号の一部に相当するが、終端記号の一部
(演算子、予約語) などをラベルに使う
- 一部の終端記号は無視 (括弧類など)
解析木と構文木の例
文法:
E → T '+' T
(Expression, 式)
T → F '*' F
(Term, 項)
F → '(' E ')'
(Factor, 因子)
F → integer
入力の例:
5 + 3 * (7 + 2)
解析の実装: 下向き解析と上向き解析
- 下向き構文解析 (top-down parsing):
- 解析木を上から (初期記号から) 作る
- 上向き構文解析 (bottom-up parsing):
- 解析木を下から (終端記号から) 作る
- 途中に複数の (小さな) 解析木がある
来週への宿題
ノートパソコンを持参