8. 2009 年 10 月 30 日 言語処理系入門 2 9
導出
開始記号から出発して,構文規則を右向きに適用してながら非
終端記号を書き換えていくと,最終的にトークン列が得られる
.
いわゆる文法エラーとなるプログラムは,文法規則から導出で
きないトークン列のことである
導出の例(最左導出):
expr ⇒ expr + expr
⇒ NUM + expr
⇒ NUM + expr - expr
⇒ NUM + expr * expr - expr
⇒ NUM + NUM * expr - expr
⇒ NUM + NUM * NUM - expr
⇒ NUM + NUM * NUM - ( expr )
⇒ NUM + NUM * NUM - ( expr + expr )
⇒ NUM + NUM * NUM - ( NUM + expr )
⇒ NUM + NUM * NUM - ( NUM + NUM )
最左導出は,最も左側
に位置する非終端記号
を置き換えていく
9. 2009 年 10 月 30 日 言語処理系入門 2 10
解析木( Parse Tree )
置換えの順序を取り除
いた導出をグラフ
(木)で表現したもの
木の内部節点(ノード)
非終端記号
木の葉
終端記号(トークン)
葉を左から右にたどると
トークン列が得られる
異なる導出戦略でも同
じ解析木が得られる
E
E + E
NUM E - E
*E E
NUM NUM
E( )
E + E
NUMNUM
10. 2009 年 10 月 30 日 言語処理系入門 2 11
あいまいな文法
ある文法から導出されるトークン列に対応する解析
木が複数あり得る場合,その文法はあいまいな文あいまいな文
法法であるE
E + E
NUM E - E
*E E
NUM NUM
E( )
E + E
NUMNUM
E
E E
NUM
E - E
*
E E
NUM NUM E( )
E + E
NUMNUM
+
(具象)構文を定義す
る場合,同じ入力
(トークン列)に対し
て得られる解析木の形
が異なるので困る
11. 2009 年 10 月 30 日 言語処理系入門 2 12
あいまいさの除去
文法を書き換えることで,曖昧さを除去できる場合がある
同じ言語を異なる文法で書きなおす
例:あいまいでない式言語の文法
E → T | E + T | E – T
T → F | T * F | T / F
F → NUM | ( E )
演算子に優先順位を付けるという手もある.
本質的にあいまいな言語の場合,除去できない
そんなときには言語設計を見直す必要あり
C 言語の例:
t * y;
t が typedef された型名の場合はポインタ変数 y の宣言となるが, t
が変数名の場合,乗算の式となる.
12. 例:ぶら下がり else
次の if-then 文, if-then-else 文の文法を考える
stmt → if expr then stmt
| if expr then stmt else stmt
| other
以下の文に対応する解析木は 2 通りある(左側の方が自然)
if E1 then if E2 then S1 else S2
演習問題:上の文法を変形して曖昧さを取り除く
2009 年 10 月 30 日 言語処理系入門 2 13
stmt
if expr then stmt
if expr then stmt else stmt
E1
E2 S1
S2
stmt
if expr then stmt
if expr then stmt
else stmt
E1
E2 S1
S2
20. 2009 年 10 月 30 日 言語処理系入門 2 22
木構造データを Ocaml で定義する
ヴァリアント型を使う
type tree = Leaf | Node of tree * tree
値の例:
Leaf
Node(Leaf,Node(Leaf,Leaf))
・・・
木の巡回は再帰関数とパターンマッチで
let rec traverse_tree tree =
match tree with
| Leaf -> ()
| Node(t1,t2) -> traverse_tree t1;
traverse_tree t2
Leaf
Leaf
Leaf Leaf
Node
Node
21. 2009 年 10 月 30 日 言語処理系入門 2 23
抽象構文を Ocaml で定義する
式言語の抽象構文
E ::= V
| E bop E
| uop E
V ::= n
n Z∈ , bop { +, -, *, / }∈ , uop { - }∈
Ocaml での実装
type bop = BopAdd | BopSub | BopMul | BopDiv
type uop = UopMin
type expr = ValExpr of value
| BopExpr of bop * expr * expr
| UopExpr of uop * expr
and value = int
22. 評価器の実装
(* 補助関数:演算子に対応する関数を返す *)
let fun_of_bop bop =
match bop with
BopAdd -> ( + )
| BopSub -> ( - )
| BopMul -> ( * )
| BopDiv -> ( / )
(* 評価器本体 : expr -> value *)
let rec eval e =
match e with
| ValExpr v -> v
| BopExpr(bop, e1, e2)
-> (fun_of_bop bop) (eval e1) (eval e2)
| UopExpr(UopMin, e1)
-> -(eval e1)
2009 年 10 月 30 日 言語処理系入門 2 24