C++ 構文解析 構文解析器の状態保存と復元

Slides:



Advertisements
Similar presentations
山元進.  for 文  while 文  do ~ while 文  文のネスト  break 文  continue 文.
Advertisements

University of Electro-Communications Human Interface section 基礎プログラミングおよび演 習 第7回.
2.5 プログラムの構成要素 (1)文字セット ① ASCII ( American Standard Code for Interchange ) JIS コードと同じ ② EBCDIC ( Extended Binary Coded Decimal for Information Code ) 1.
コンパイラ 2011年10月17日
言語処理系(4) 金子敬一.
データ構造とアルゴリズム 第10回 mallocとfree
プログラミング基礎I(再) 山元進.
文法と言語 ー文脈自由文法とLR構文解析2ー
コンパイラ 第9回 コード生成 ― スタックマシン ―
プログラミング言語論 第6回 型 情報工学科 篠埜 功.
第2回:Javaの変数と型の宣言 プログラミングII 2007年10月2日.
コンパイラ 2012年10月15日
ML 演習 第 7 回 新井淳也、中村宇佑、前田俊行 2011/05/31.
構造体 構造体, 構造体とポインタの組み合わせ,.
C言語講座 第3回 ポインタ、配列.
プログラムの制御構造 選択・繰り返し.
プログラミング論 ファイル入出力
東京工科大学 コンピュータサイエンス学部 亀田弘之
暗黙的に型付けされる構造体の Java言語への導入
第3回 2007年4月27日 応用Java (Java/XML).
B演習(言語処理系演習)第3回 字句解析 田浦.
関数の定義.
アルゴリズムとプログラミング (Algorithms and Programming)
プログラミング入門2 第2回 型と演算 条件分岐 篠埜 功.
B演習(言語処理系演習)第4回 田浦.
ソフトウェア制作論 平成30年10月3日.
04: 式・条件分岐 (if) C プログラミング入門 基幹7 (水5) Linux にログインし、以下の講義ページ を開いておくこと
プログラミング入門2 第11回 情報工学科 篠埜 功.
indentについて forやifの「中身」を右に寄せる. forやifの「外枠」は右に寄せない. int x; x = 3;
10-1 SAXの概要 10-2 Saxプログラミングの基礎 10-3 saxのプログラム例
復習 前回の関数のまとめ(1) 関数はmain()関数または他の関数から呼び出されて実行される.
プログラミング論 ファイル入出力
コンパイラ資料 4章 記号表 改訂1版( ).
文法と言語 ー文脈自由文法とLR構文解析2ー
高度プログラミング演習 (05).
一時的な型 長谷川啓
プログラムの制御構造 配列・繰り返し.
文法と言語 ー文脈自由文法とLR構文解析3ー
ソフトウェア制作論 平成30年10月10日.
フロントエンドとバックエンドのインターフェース
型の compatibility とポインタ演算
記号表の検索と登録 長谷川啓
岩村雅一 知能情報工学演習I 第12回(C言語第6回) 岩村雅一
コンパイラ 2011年10月20日
B演習(言語処理系演習)第2回 田浦.
アルゴリズムとプログラミング (Algorithms and Programming)
参照されないリテラル 長谷川啓
C#プログラミング実習 第3回.
プログラミングⅠ 平成30年12月10日 森田 彦.
C#プログラミング実習 第2回.
高度プログラミング演習 (11).
C言語講座 制御(選択) 2006年 計算技術研究会.
ドキュメントジェネレータ 詳細仕様 長谷川啓
プログラミング 平成28年11月15日 森田 彦.
X64 函数呼び出し規約 長谷川啓
コンパイラ 2012年10月11日
Inline 展開のアルゴリズム 長谷川啓
岩村雅一 知能情報工学演習I 第12回(後半第6回) 岩村雅一
演算子のオーバーロード.
文法と言語 ー文脈自由文法とLR構文解析2ー
:: の扱い 長谷川啓.
プログラミング入門2 第3回 条件分岐(2) 繰り返し文 篠埜 功.
情報処理Ⅱ 第3回 2004年10月19日(火).
情報処理Ⅱ 2006年10月20日(金).
C言語講座 四則演算  if ,  switch 制御文.
計算技術研究会 C言語講座 第二回 制御構文 if , switch.
6.3 インタプリタ (1)インタプリタ(interpreter)とは
プログラミング演習II 2003年10月29日(第2,3回) 木村巌.
構文主導翻訳.
デフォルト引数 長谷川啓.
Presentation transcript:

C++ 構文解析 構文解析器の状態保存と復元 2019.03.18 長谷川啓

例1 void f() { int (n); // 宣言 ... int (n+n); // 式 }

例1 int の導出 (n+n); int (n); int simple-type-specifier decl-specifier simple-type-specifier の次の字句が ‘(‘ であった場合に, 残りが init-declarator-list なのか ‘(‘ expression-list ‘)’ なのか不明 decl-specifier-seq

宣言を優先させる yacc/bison のレポートファイル State X1 R11 type_specifier: simple_type_specifier . R12 postfix_expression: simple_type_specifier . '(' expression_list ')' R13 | simple_type_specifier . '(' ')' '(' shift, and go to state Y1 '(' [reduce using rule R11 (type_specifier)] $default reduce using rule R11 (type_specifier) このままの動作だと simple-type-specifier の後の ‘(‘ で R11の規則が使われることはない.

宣言を優先させる (続き) 以下のコードを yyparse の適切な場所に入れる. if ( yystate == X1 && yychar == ‘(‘ ) { yyn = R11 + 1; goto yyreduce; } 勿論このままだと、函数スタイルのキャストは構文エラーを引き起こす。

構文解析器の状態を保存する さらに以下のように修正する if (yystate == X1 && yychar == ‘(‘ ) { if (!retry[X1] ) { // 1 回目なら save(yystate, ...); // 構文解析器の状態を保存 yyn = R11 + 1; // 宣言だとして構文解析をする goto yyreduce; }

構文解析器の状態を復元する 以下のコードを yyerror が呼び出される前の位置に挿入する if (構文解析器の状態が保存されている) { restore(&yystate, ...); // 構文解析器の状態を復元する ++retry[yystate]; // リトライカウンタをインクリメント goto yynewstate; }

1回目の構文エラーを起こすまでの字句も保存する int get_token() { int ret = ... ... if (構文解析器の状態が保存されている) { // retry するときのために字句をため込んでおく // 字句に属性を付加しているのならば、それも保存する } return ret;

例2 struct T { ... }; int a; ... T t1(); // 戻り値の型が T の函数の宣言 T t2(a); // 型 T の変数の定義. a で初期化.

函数の宣言は優先されているが... yacc/bison のレポートファイル State X2 R21 declarator: direct_declarator . R22 direct_declarator: direct_declarator . '(' parameter_declaration_clause ')' ... '(' shift, and go to state Y2 '(' [reduce using rule R21 (declarator)] $default reduce using rule R21 (declarator) このままの動作だと declarator の後の ‘(‘ で R21 の規則が使われることはない. つまり例2 の変数の定義は構文エラーになる

構文解析器の状態を保存する 以下のコード yyparse の適切な場所に追加する: if (yystate == X2 && yychar == ‘(‘ ) { if (!retry[X2] ) // 1 回目なら save(yystate, ...); // 構文解析器の状態を保存 else { yyn = R21 + 1; goto yyreduce; }

例3 double f(); void g(int a) { int(f())+a; }

int(f())+a; int の次の ‘(‘ まで読み込んだ時点で例1の X1 になる f の次の ‘(‘ まで読み込んだ時点で例2の X2 になる + を読み込んだ時点で X2 で保存した状態を捨てて X1 で保存した状態を復元するべき f を記号表に登録するのはマズいから

最後に保存した状態を捨てる 以下のコードを yyparse の適切な場所に追加する: if (yystate == X2) { switch (yychar ) { case ’(’: case ’[’: ... break; // 宣言として構文解析してエラーにならないもの default: if (最後に保存した状態はX2 のもの) { 最後に保存した状態を捨てる  goto yyerrlab; }