Microsoft PowerPoint - 05LLprint.ppt [互換モード]

Similar documents
1 (2 * 3) 1 2 * 3 Preorder In order Post order 1 * 1 * Breadth-first Depth-first * * 3 Preorder: 1 * 2 3 In order: 1 2 * 3 Post orde

Microsoft PowerPoint - 3.ppt [互換モード]

Microsoft PowerPoint - Compiler06.pptx

文法と言語 ー文脈自由文法とLR構文解析2ー

Microsoft PowerPoint - Compiler06note.pptx

Microsoft PowerPoint - Compiler05.pptx

Microsoft PowerPoint - 04SyntaxAnalysis.ppt [互換モード]

Microsoft PowerPoint - 03BNFScanner.ppt [互換モード]

Microsoft PowerPoint - 08LR-conflicts.ppt [互換モード]

Microsoft Word - Javacc.docx

Microsoft PowerPoint - Compiler05note.pptx

PowerPoint プレゼンテーション

構文解析表の作成講義でも少し触れましたが 各選言で先頭に出現する可能性がある終端記号の集合 のことを DIRECTOR 集合とよびます DIRECTOR は direction( 方向 ) を決定するという意味を持っており LL(k) 構文解析器が非終端記号を解析する際に そのうちどの選言を利用する

Microsoft PowerPoint - 03BNFScanner-print.ppt

nlp1-04a.key

情報工学実験 C コンパイラ第 2 回説明資料 (2017 年度 ) 担当 : 笹倉 佐藤

言語プロセッサ2005 -No.6-

Microsoft PowerPoint ppt

プログラミング言語処理系論 (4) Design and Implementation of Programming Language Processors

Microsoft PowerPoint L03-Syntex and Semantics-1-students ( )

18/12/06 情報工学実験 C コンパイラ (2018 年度 ) 担当 : 笹倉 佐藤 その 3 yacc の構造 定義部 %% 定義部の終了 規則部 %% 規則部の終了 ユーザ定義サブルーチン部 :C のプログラムを書く 形は lex と同じ 1

Microsoft Word - problem3.doc

Microsoft Word - problem5.doc

Microsoft PowerPoint - kougi10.ppt

C8

第12回 モナドパーサ

Microsoft PowerPoint - 05.pptx

C プログラミング演習 1( 再 ) 2 講義では C プログラミングの基本を学び 演習では やや実践的なプログラミングを通して学ぶ

構造化プログラミングと データ抽象

構造化プログラミングと データ抽象

Microsoft PowerPoint - Compiler03note.pptx

情報工学実験 C コンパイラ第 2 回説明資料 (2018 年度 ) 担当 : 笹倉 佐藤

Microsoft PowerPoint - 11Syntax.ppt

関東/関西/九州同時開催 女性エンジニア大集合!新春LT 座談会 スクリプト インタプリタを 作ってみた 1 スクリプトインタプリタを作ってみた

Microsoft PowerPoint - 13Kadai.pptx

Functional Programming

オートマトンと言語

memo

Microsoft PowerPoint ppt

Microsoft PowerPoint - 02LanguageTheory.ppt [互換モード]

情報処理Ⅰ

Taro-リストⅠ(公開版).jtd

<4D F736F F D20438CBE8CEA8D758DC F0939A82C282AB2E646F63>

Microsoft PowerPoint - kougi4.ppt

Microsoft PowerPoint - アルデIII 02回目10月15日

Microsoft PowerPoint - Compiler03.pptx

コンピュータ工学講義プリント (7 月 17 日 ) 今回の講義では フローチャートについて学ぶ フローチャートとはフローチャートは コンピュータプログラムの処理の流れを視覚的に表し 処理の全体像を把握しやすくするために書く図である 日本語では流れ図という 図 1 は ユーザーに 0 以上の整数 n

Microsoft PowerPoint - C_Programming(3).pptx

alg2015-6r3.ppt

第10回 モジュール

数理言語

4 分岐処理と繰返し処理 ( 教科書 P.32) プログラムの基本的処理は三つある. (1) 順次処理 : 上から下に順番に処理する ぶんきそろ (2) 分岐処理 : 条件が揃えば, 処理する はんぷく (3) 反復処理 : 条件が揃うまで処理を繰り返す 全てのプログラムは (1) から (3) の

4-4 while 文 for 文と同様 ある処理を繰り返し実行するためのものだが for 文と違うのは while 文で指定するのは 継続条件のみであるということ for 文で書かれた左のプログラムを while 文で書き換えると右のようになる /* 読込んだ正の整数値までカウントアップ (for

Microsoft PowerPoint - CproNt02.ppt [互換モード]

Microsoft PowerPoint - IntroAlgDs-05-4.ppt

PowerPoint プレゼンテーション

compiler-text.dvi

コンパイラとは プログラミング言語 ( 高級言語 ) で書かれたプログラムを入力し, コンピュータが実行できる言語 ( 機械語など ) に変換するプログラムのこと例 : gcc コンパイラは対応する言語によって複雑である場合もあるし単純である場合もある 本実験では簡単な言語のコンパイラを作成する

Microsoft PowerPoint - アルデIII 02回目10月14日

ohp07.dvi

Microsoft PowerPoint - 4.pptx

メソッドのまとめ

バイオプログラミング第 1 榊原康文 佐藤健吾 慶應義塾大学理工学部生命情報学科

Microsoft PowerPoint - ca ppt [互換モード]

Microsoft Word - 13

( ) ( ) lex LL(1) LL(1)

オートマトン 形式言語及び演習 1. 有限オートマトンとは 酒井正彦 形式言語 言語とは : 文字列の集合例 : 偶数個の 1 の後に 0 を持つ列からなる集合 {0, 110, 11110,

Microsoft Word - VBA基礎(3).docx

PowerPoint プレゼンテーション

オートマトン 形式言語及び演習 3. 正規表現 酒井正彦 正規表現とは 正規表現 ( 正則表現, Regular Expression) オートマトン : 言語を定義する機械正規表現 : 言語

Microsoft PowerPoint ppt

: gettoken(1) module P = Printf exception End_of_system (* *) let _ISTREAM = ref stdin let ch = ref ( ) let read () = (let c =!ch in ch := inp

memo

テキスト処理第 12 回 ( ) 田中哲産業技術総合研究所情報技術研究部門 u.ac.jp /

自然言語は曖昧性だらけ! I saw a girl with a telescope 構文解析 ( パージング ) は構造的な曖昧性を解消 2

Taro-スタック(公開版).jtd

Microsoft PowerPoint - ruby_instruction.ppt

Microsoft PowerPoint - algo ppt [互換モード]

オートマトンと言語

Microsoft PowerPoint - 11.pptx

:30 12:00 I. I VI II. III. IV. a d V. VI

PowerPoint プレゼンテーション

プログラミング実習I

Microsoft PowerPoint - ProD0107.ppt

始めに, 最下位共通先祖を求めるための関数 LcaDFS( int v ) の処理を記述する. この関数は値を返さない再帰的な void 関数で, 点 v を根とする木 T の部分木を深さ優先探索する. 整数の引数 v は, 木 T の点を示す点番号で, 配列 NodeSpace[ ] へのカーソル

2-1 / 語問題 項書換え系 4.0. 準備 (3.1. 項 代入 等価性 ) 定義 3.1.1: - シグネチャ (signature): 関数記号の集合 (Σ と書く ) - それぞれの関数記号は アリティ (arity) と呼ばれる自然数が定められている - Σ (n) : アリ

Taro-2分探索木Ⅱ(公開版).jtd

Microsoft Word - CompA-Ex doc

untitled

プログラミングA

Microsoft PowerPoint - 09re.ppt [互換モード]

r02.dvi

ohp02.dvi

PowerPoint Presentation

Taro-2分探索木Ⅰ(公開版).jtd

プログラミングA

Java プログラミング Ⅰ 3 回目変数 変数 変 数 一時的に値を記憶させておく機能型 ( データ型 ) と識別子をもつ 2 型 ( データ型 ) 変数の種類型に応じて記憶できる値の種類や範囲が決まる 型 値の種類 値の範囲 boolean 真偽値 true / false char 2バイト文

Microsoft PowerPoint - 10.ppt [互換モード]

memo

Microsoft PowerPoint - C1(演算と変数).ppt

Transcription:

このスライドの内容 コンパイラ理論 6 LL 構文解析 下降型構文解析を詳しく 再帰下降型 (recursive descent) LL(1) 櫻井彰人 <sentence> ::= <noun-phrase><verb-phrase> <noun-phrase> ::= <cmplx-noun> <cmplx-noun><prep-phrase> <verb-phrase> ::= <cmplx-verb> <cmplx-verb><prep-phrase> <prep-phrase> ::= <prep><cmplx-noun> <cmplx-noun> ::= <article><noun> <cmplx-verb> ::= <verb> <verb><noun-phrase> <article> ::= a the <noun> ::= boy girl flower <verb> ::= touches likes sees <prep> ::= with 生成の例 ::= ::= * <factor> <factor> <factor> ::= '(' <expr ')' <num> <num> ::= 0 1 2 3 4 5 6 7 8 9 構文木の例 <sentence> ::= <noun-phrase><verb-phrase> ::= <cmplx-noun><verb-phrase> ::= <article><noun><verb-phrase> ::= a <noun><verb-phrase> ::= a boy <cmplx-verb> ::= a boy <verb> ::= a boy sees <factor> <num> 1 * <factor> <factor> <num> 2 <num> 3 抽象構文木 構文木 1 (2 * 3) <factor> 1 * <factor> * <factor> <factor> <num> <num> <num> 2 3 1 2 3 1 ( 2 * 3 ) 1

抽象構文木 1 (2 * 3) 探索 Preorder In order Post order 1 * 1 * Breadth-first Depth-first 2 3 2 3 1 * 2 3 探索 Preorder: 1 * 2 3 In order: Post order: 1 2 3 * 表現するもの 構文 意味 構文木 式であれば 演算順序が表現されていないといけない すなわち 文法に組み込まれていないといけない 補足 : 抽象構文木では 構文のうち 木構造から推定できるものは捨象している 曖昧な文法 ::= * '(' ')' 0 1 2 3 4 5 6 7 8 9 ::= * '(' ')' 0 1 2 3 4 5 6 7 8 9 問題なし? 7 2

::= * '(' ')' 0 1 2 3 4 5 6 7 8 9 ::= * '(' ')' 0 1 2 3 4 5 6 7 8 9 * * 問題あり! 1 2 * 3 1 2 * 3 9 7 曖昧 (ambiguous)! 9 パーサ動作例で用いる規則 <C-PROG> MAIN OPENPAR <PARAMS> CLOSEPAR <MAIN-BODY> <PARAMS> NULL <PARAMS> VAR <VAR-LIST> <VARLIST>, <VAR> <VARLIST> <VARLIST> NULL <MAIN-BODY> CURLYOPEN <DECL-STMT> <ASSIGN-STMT> CURLYCLOSE <DECL-STMT> <TYPE><VAR><VAR-LIST>; <ASSIGN-STMT> <VAR> = <EXPR>; <EXPR> <VAR> <EXPR> <VAR><OP><EXPR> <OP> <OP> - <TYPE> INT <TYPE> FLOAT main() { int a,b; a = b; パーサ動作例 Scanner Parser 次の token の要求 " main() { int a,b; a = b; パーサ動作例 Scanner Token: ';' Parser 他になすべきこと? 意味動作 semantic actions 意味検査 semantic checks 記号表 symbol tables <C-PROG> MAIN OPENPAR <PARAMETERS> CLOSEPAR <MAIN-BODY> <MAIN-BODY> CURLYOPEN <DECL-STMT> <ASSIGN-STMT> CURLYCLOSE <DECL-STMT> <TYPE><VAR><VAR-LIST>; <VARLIST>, <VAR> <VARLIST> <VARLIST> NULL 3

記号表 int a,b; 変数 a と b の型宣言 現在のスコープ ( 有効範囲 ) 内で integer 型である これによって a と b が使用可能となる 意味動作 意味動作の例 起動の仕方 起動するとどうなるか? 記号表 識別名 型 スコープ a int "main" b int "main" 意味動作の代表例 宣言された変数を記号表に書く 記号表で変数名を探す 変数の対応をとる ( スコープに関する規則等 ) 型のチェック ( 整合性 ) 意味的な文脈の維持 ( 型等 ) a b c t1 = a b t2 = t1 c 意味動作の起動 文法中に意味動作記号を書き込む 意味動作は パーサにより 構文解析の途中で適宜呼び出され実行される 意味動作は 計算を行なったり 記憶したり 値を返したりすることができる スタックが使える 記号表を用いて 型チェック等ができる 意味的な文脈 意味動作の例 <decl-stmt> <type>#put-type<var-list>#do-decl <var-list> <var>, <var-list>#add-decl <var-list> <var> <var> ID#proc-decl #put-type 意味スタックに 型 をつむ #proc-decl 変数の宣言レコードを作る #add-decl 宣言のチェーン (decl-chain) を作る #do-decl 意味スタック上のチェインを逆方向にたどり それぞれの変数を記号表に入れる 意味動作 記号表への書き込みと読み出し以外に何を? 二種類 型チェック ( 束縛, 型整合性, スコープ, etc.) 通訳 translation ( 中間変数値を生成, 意味文脈を保存すべく その値を伝播させる ). id3 id2 #type #do-decl Name Type Scope id1 1 3 id2 1 3 id3 1 3 4

意味動作 ( 通訳 translation) 対象 : a = b c d; 意味動作の呼出し process-id: "c" の意味記述をスタックにつむ 文法 : <ASSGNSTMT> <VAR> = <EXPR>#do-assign; <EXPR> <VAR><EXPRTAIL> <VAR> ID#process-id <EXPRTAIL> <OP>#process-op<VAR>#do-infix<EXPRTAIL> <EXPTAIL> NULL "c" "" "b" "=" "a" Top-down パージング パージング 構文解析 パージング 構文解析 ( 意味解析も少し ) 1. 根節 root node 葉 leaves 2. 抽象的範疇 具体的範疇 3. 文法規則を左 右 4. 手順は 予測 predictive parsing とも言う Bottom-up パージング 1. 葉 leaves 根節 root node 2. 具体的範疇 抽象的範疇 3. 文法規則を右 左 4. 手順は パターンマッチング " 再帰下降 recursive descent 基本アイデア : CFG は 一つの非終端記号に一つの関数を対応させると ( 相互 ) 再帰呼び出しをする関数の集合に写像することができる 例えば 文法の一部である次の例では : A bb cc tree *A () { switch (nexttoken()) { case TOK_b: { tree *t = B(); const(maketree(b), t); case TOK_c: { tree *t = C(); const(maketree(c), t); default:... error... return return 再帰下降型パージング例 ::= ::= * <factor> <factor> <factor> ::= '(' ')' num ident 次の予測 注 : num と ident は終端記号と考えよう 本当のところは 字句解析の話のときに 5

無限 多分 予測の仕方が悪い!? 採るべきであったのは 採るべきであったのは ::= ::= * <factor> <factor> <factor> ::= '(' ')' num ident ::= ::= * <factor> <factor> <factor> ::= '(' ')' num ident <factor> <factor> <ident> 選択の間違いに気づいたら 後戻りして 再試行... <num> しかし 解析し残しがあるのは大問題 問題の所在 下記の文法は 下降型パージングに適した形ではない この文法には 左再帰 left-recursive の生成規則あり ::= ::= * <factor> <factor> <factor> ::= '(' ')' num ident 再帰 recursion 再帰がうまく動くには 終了条件のチェック 繰り返し ダメなのは 繰り返し 終了条件のチェック 文法を右再帰 right-recursive にすればよい 6

右再帰にする ::= 拡張 BNF Extended Backus-Naur Form EBNF は { を用いて 0 回以上の繰り返しを表す ::= { 正規表現と同等のアイデア : Num ::= [0-9][0-9]* ::= { head tail BNF に戻ると ::= <e_tail> <e_tail> ::= <e_tail> 最左導出 leftmost derivation 注 : 最左導出のような顔をしている というのが正しい表現 <e_tail> <e_tail> は空列を表す 特殊な非終端記号 <e_tail> 最右導出 rightmost derivation 注 : 最右導出のような顔をしている というのが正しい表現 <e_tail> <e_tail> <e_tail> 再び ::= * <factor> <factor> EBNF ::= <factor> { * <factor> BNF ::= <factor><t_tail> <t_tail> ::= * <factor><t_tail> これで 下降パージングができる! 7

文法例 ::= <e_tail> <e_tail> ::= <e_tail> ::= <factor> <t_tail> <t_tail> ::= * <factor> <t_tail> <factor> ::= '(' ')' num id 再帰下降型構文解析 Recursive Descent Parsing top-down パージングの代表 各非終端記号がある関数に対応 ( 終端記号である ) トークン token には何が? 字句解析 scanner が対応する 字句解析関数を呼ぶには : gettok() と仮定する token の型を定義する必要あり ちょっとした小道具 enum {SUCCESS, FAILURE; int Succeed(int arg) { if(arg == SUCCESS) return 1; else if(arg == FAILURE) return 0; else /* ここはエラー処理 */ enum {PLUS, MULT, LPAREN, RPAREN, NUM, ID; enum {SUCCESS, FAILURE; int expr(void) { if( Succeed(term()) && Succeed(e_tail()) ) else return FAILURE; ::= <e_tail> <e_tail> ::= <e_tail> ::= <factor> <t_tail> <t_tail> ::= * <factor> <t_tail> <factor> ::= '(' ')' num id int e_tail(void) { if( gettok() == PLUS && Succeeds(term()) && Succeeds(e_tail()) ) else /* epsilon! がある故 */ ::= <e_tail> <e_tail> ::= <e_tail> ::= <factor> <t_tail> <t_tail> ::= * <factor> <t_tail> <factor> ::= '(' ')' num id int term(void) { if(succeed(factor()) && Succeed(t_tail())) else return FAILURE; ::= <e_tail> <e_tail> ::= <e_tail> ::= <factor> <t_tail> <t_tail> ::= * <factor> <t_tail> <factor> ::= '(' ')' num id 8

int t_tail(void) { if( gettok() == MULT && Succeeds(factor()) && Succeeds(t_tail()) ) else /* epsilon! がある故 */ ::= <e_tail> <e_tail> ::= <e_tail> ::= <factor> <t_tail> <t_tail> ::= * <factor> <t_tail> <factor> ::= '(' ')' num id int factor(void) { if( gettok() == LPAREN && Succeeds(expr()) && gettok() == RPAREN) ) else if (gettok() == NUM) else if (gettok() == ID) else return FAILURE; ん? ::= <e_tail> <e_tail> ::= <e_tail> ::= <factor> <t_tail> <t_tail> ::= * <factor> <t_tail> <factor> ::= '(' ')' num id なぜ まずいか? /* Version 2 */ int factor(void) { int TokType; if( (TokType = gettok()) == LPAREN && Succeeds(expr()) && gettok() == RPAREN ) else if( TokType == NUM TokType == ID ) else return FAILURE; それでもバグは残っている!!! 問題の所在 成功すれば 問題なし この世の習い 問題の所在 成功すれば 問題なし この世の習い 失敗したときには その失敗を知るまでの間に食べてしまったトークンを何らかの方法で回復しないといけない i.e. 例えば こんな感じのプログラムに対して : if(something with factor) return Success; else if(something else with factor) return Success else if(another thing with factor) etc. 失敗したときには その失敗を知るまでの間に食べてしまったトークンを何らかの方法で回復しないといけない 押し戻せばよい? 例えば unget で? 9

問題の所在 成功すれば 問題なし この世の習い 失敗したときには その失敗を知るまでの間に食べてしまったトークンを何らかの方法で回復しないといけない 押し戻せばよい? 例えば unget で? そうでもない 消費してしまったのはトークンであって 文字ではないから 解 押し返すわけにはいかない 字句解析器で工夫すれば可能 変数やスタックを用いればよい 必要個数の上限が分かっていれば 変数で そうでなければスタックで 結局のところは どこかで 戻しいれ を行なうことになる ただし 先読みすべき個数が分かっていて その個数あれば次にあるべき非終端記号が高々一個に決まる場合 戻し入れ なしにパースが可能となる では? (T1=token) T1==( Yes <factor> ::= '(' ')' num id No No No T1==ID T1==NUM Recover T1 結果 Save T1 T1=token Yes Recover T1 Yes Yes Failure <factor> <t-tail> <e_tail> <e_tail> <factor> <t-tail> ) Failure num num * <factor> <t_tail> Yes Success? Success Success num これでよいか? 勿論! 何の問題もない しかし プログラムを一々書くのは面倒! 同じことの繰り返しが多い 改善の方法がありそう LL(1) 構文解析 スタックを明示的に用いたアルゴリズム 再帰型ではなく 繰り返し型 開始記号をスタックに積む スタック上の非終端記号を 構文規則に従い 書き換える ( スタック上で ) 右辺の記号列左端がトップにくる 目的は 入力列の先頭部分と一致するように スタック上にものをおくこと スタックトップが入力列 ( トークン ) 先頭と一致すれば 両方を消去する もし たまたま スタックと入力列が同時に空となれば パースは成功 10

構文規則 S ( S ) S LL(1) の動作例 これは 対応のとれた括弧列を生成する S ( S ) S ( ( S ) S ) S ( ( S ) S ) ( S ) S ( ( ) ) ( ) 問題 再帰降下型のときと同様に 構文規則は左再帰があっては困る 構文規則は また 曖昧であっては困る すなわち ある入力列が構文解析可能であれば その方法は唯一である必要がある この簡単な例においてさえ 次に展開する規則の選択には 選択肢がある どの選択をすればよいか? どういうときにどのような選択をすればよいかを表にしておく 構文解析表 parse table という それが可能 ( ただし1トークン先読みを許す ) な文法を LL(1) という 構文解析表 構文解析表 parse table は 終端記号 ( 入力列の先頭にある ) と現在の状態 ( 次に展開する非終端記号 ) から 次にどの構文規則を次に選択するかを指示するように作る ところで, もし規則 : A があったどき A を用いるときと A を用いるときとをどう区別したらよいだろうか? これは と の First 集合を定めることにより解決する First 集合 : その記号列から導出される終端記号列の先頭終端記号の集合 と の First 集合が排他的であればよい 入力列先頭がどちらに属するかで 次に適用する規則が決定できる 構文解析表 更に 次のような規則は いつ 適用すべきかを知る必要がある ( この規則には First 集合というアイデアが適用できないため 別の考え方が必要 ) A これは 非終端記号 A の後に ( これは上記の規則では決まらない A を右辺に持つ規則から決まる ) どんな終端記号がくるかを知ることにより 解決できる 終端記号の こうした集合を A の Follow 集合という 次の終端記号が A の Follow 集合に含まれれば A を適用してよいことになる 構文解析表 簡単な例 LL(1) の構文解析表は 終端記号 ( トークン ) に対応する列と 非終端記号に対応する行とからなる 入力列は 終端記号 ( トークン ) の列であるとする スタック先頭は 終端記号 ( トークン ) か非終端記号 構文解析表の値 (entry) は, その場合に適用する構文規則である 非終端記号 トークン ( ) $ S S ( S ) S S S 11

First 集合と Follow 集合 LL(1) 構文解析表の作成はアルゴリズム的に可能であり 従って 自動化できる 実際の言語の文法に対して これを手で行なうことはつらい! 構文表を作るには 文法で用いる記号に対して まず First 集合と Follow 集合とを作る必要がある 文法例 exp exp term term - term term mulop factor factor mulop * factor ( exp ) number 文法の変換 この文法は 左再帰のため まず 右再帰の文法に変換する必要がある exp exp' - term factor term' term' mulop factor term' mulop * factor ( exp ) number 書き直すと 何をすべきであったか? exp exp' exp' - term factor term' term' mulop factor term' term' mulop * factor ( exp ) factor number 構文規則の右辺の列から導出される終端記号列の先頭に来る終端記号 ( トークン ) の集合を求めること A B であれば first(a)=first(b) となるので ( そうでない場合については 次の次のスライドに ) 先の例では 次の方程式をとけばよいことになる first(exp ) = first(term) first(exp' ) = first() { first() = {,- first(term ) = first(factor) first(term') = first(mulop) { first(mulop) = {* first(factor)= {(, number exp exp' exp' - term factor term' term' mulop factor term' term' mulop * factor ( exp ) factor number どうやって? 反復法! 方程式の等号を代入記号と読替える 初期値をいずれも空集合として 収束するまで繰り返す 収束するか? 収束する! 各集合とも小さくなることはない 繰り返しの過程で ある終端記号がある集合に追加されることがあっても 減らされることはない けれども 天井がある 高々有限集合だから first(exp ) first(term) first(exp' ) first() { first() {,- first(term ) first(factor) first(term') first(mulop) { first(mulop) {* first(factor) {(, number 注意事項 A B であれば first(a)=first(b) となるかというと そうでもない first(a) first(b) であることは確かであるが B となる可能性があるからである A BCD のとき B とも C ともなりうるならば first(a)=first(b) first(c) first(d) 今回の例ではこうした事態は考えなくてよい 空列になる非終端記号は exp' と term' だけで いずれも 右辺の先頭には来ていないから 12

構文規則 exp first(exp) = { (, number exp' first(exp') = {, -, exp' first(exp') = {, -, first() = {, - - first() = {, - term factor term' first(term) = { (, number term' mulop factor term' first(term') = { *, term' first(term') = { *, mulop * first(mulop) = { * factor ( exp ) first(factor) = { (, number factor number first(factor) = { (, number Follow 集合 First 集合は ( 当該非終端記号が左辺に現れる構文規則の ) 右辺の記号列から導出される終端記号列の先頭に現れる終端記号の集合であった 非終端記号 A の Follow 集合は A の後に続く終端記号の集合であり 構文規則 A が適用可能かどうかを判断するのに用いられる すなわち スタックのトップが A であり 入力列の先頭が A の follow 集合に含まれるなら A を適用しようということになる Follow 集合 非終端記号 A が与えられたとき, 終端記号 ($ を含む ) の集合 Follow(A) は次のように定義される 1. A が開始記号であれば, $ Follow(A) 2. もし構文規則 B A があれば, First( ) - { Follow(A) 3. もし構文規則 B A があり, かつ, First( ) であれば, Follow(B) Follow(A) 注 : このことから 構文規則が B A の形をしていれば Follow(B) Follow(A) といえる 文法 exp exp' exp' - term factor term' term' mulop factor term' term' mulop * factor ( exp ) factor number 文法 exp exp' exp' - term factor term' term' mulop factor term' term' mulop * factor ( exp ) factor number 赤で書いた構文規則は非終端記号を右辺に持たないため follow 集合には影響を与えない 考慮する規則のみ exp exp' term factor term' term' mulop factor term' factor ( exp ) 13

番号を振ろう まとめると (1) exp (2) exp' (3) term factor term' (4) term' mulop factor term' (5) factor ( exp ) Follow(term) = First(exp') Follow(exp') = Follow(exp) Follow(term) = Follow(exp) Follow() = First(term) Follow(term) = First(exp') Follow(term) = Follow(exp') Follow(factor) = First(term') Follow(factor) = Follow(term) Follow(term') = Follow(term) Follow(mulop) = First(factor) Follow(factor) = First(term') Follow(factor) = Follow(term') Follow(exp) = { ) Follow(exp) = { Follow(exp') = { Follow() = { Follow(term) = { Follow(term') = { Follow(mulop) = { Follow(factor) = { Follows 集合の計算に移る 結果 Follow(term) = First(exp') Follow(exp') = Follow(exp) Follow(term) = Follow(exp) Follow() = First(term) Follow(term) = First(exp') Follow(term) = Follow(exp') Follow(factor) = First(term') Follow(factor) = Follow(term) Follow(term') = Follow(term) Follow(mulop) = First(factor) Follow(factor) = First(term') Follow(factor) = Follow(term') Follow(exp) = { ) Follow(exp) = { $ ) Follow(exp') = { $ ) Follow() = { ( number Follow(term) = { - $ ) Follow(term') = { - $ ) Follow(mulop) = { ( number Follow(factor) = { * - $ ) No change! First(exp) = { ( number First(exp') = { - First() = { - First(term) = { ( number First(term') = { * First(mulop) = { * First(factor) { ( number Follow(exp) = { $ ) Follow(exp') = { $ ) Follow() = { ( number Follow(term) = { - $ ) Follow(term') = { - $ ) Follow(mulop) = { ( number Follow(factor) = { * - $ ) 構文解析表 LL(1) の構文解析表は 終端記号 ( トークン ) に対応する列と 非終端記号に対応する行とからなる 入力列は 終端記号 ( トークン ) の列であるとする スタック先頭は 終端記号 ( トークン ) か非終端記号 構文解析表の値 (entry) は, その場合に適用する構文規則である 構文解析表の作成 以下の 2 ステップを 各日終端記号 A と構文規則 A について繰り返せ 1. First( ) 中のトークン a について, 表 Table[A][a] に A を追加. 2. もし First( ) ならば, Follow(A) ( トークンか $) の各 a について, 表 Table[A][a] に A を追加. 例 非終端記号 : exp 構文規則 : exp First(term) = { ( number 従って exp term exp を Table[exp][(] と Table[exp][number] に追加 14

表 [N][T] ( number ) - * $ exp exp exp exp' exp' exp' term term factor term' term factor term' exp' - term' term' term' term' term' mulop factor term' mulop factor factor ( exp ) factor number mulop * exp' term' 構文解析表の作成 以下の 2 ステップを 各日終端記号 A と構文規則 A について繰り返せ 1. First( ) 中のトークン a について, 表 Table[A][a] に A を追加. 2. もし First( ) ならば, Follow(A) ( トークンか $) の各 a について, 表 Table[A][a] に A を追加. 例 非終端記号 : term' 構文規則 : term' mulop factor term' First(term') = { * 従って term mulop factor term を表 [term ][*] に追加 表 [N][T] ( number ) - * $ exp exp exp exp' exp' exp' term term factor term' term factor term' exp' - term' term' term' term' term' mulop factor term' mulop factor factor ( exp ) factor number mulop * exp' term' 構文解析表の作成 以下の 2 ステップを 各日終端記号 A と構文規則 A について繰り返せ 1. First( ) 中のトークン a について, 表 Table[A][a] に A を追加. 2. もし First( ) ならば, Follow(A) ( トークンか $) の各 a について, 表 Table[A][a] に A を追加. 例 非終端記号 : term' 構文規則 : term' Follow(term') = { - $ ) 従って term を表 [term ][], 表 [term ][-], 表 [term ][$] および表 [term ][)] に追加 表 [N][T] ( number ) - * $ exp exp exp exp' exp' exp' exp' - exp' term term factor term' term factor term' term' term' term' term' term' mulop factor term' mulop mulop * term' factor factor ( exp ) factor number 15