:: の扱い 2019.03.28 長谷川啓
C コンパイラでもあった問題 正規表現 [_a-zA-Z][_a-zA-Z0-9]* (名前)にマッチした文字列 記号表を検索するべきかどうか ざっくりとした結論 型が要求されるポイントでは記号表を検索して typedef-name を拾う必要がある C コンパイラ (PDFのドキュメント) 2.2.1 字句解析ルーチンと記号表
C++ はさらに :: がある namespace N { } N::outer::inner noi; struct outer { // ... struct inner { static int si; }; struct S { /* ... */ }; extern double S; // ok : 変数名とタグ名は別空間 } N::outer::inner noi; int N::outer::inner::si; int N; // エラー: N の多重定義 double N::S; struct N::S ns;
C++ 文法抜粋 nested-name-specifier: class-or-namespace-name: type-name: class-or-namespace-name :: nested-name-specifier class-or-namespace-name :: ... class-or-namespace-name: class-name namespace-name type-name:
名前 ⇒ 字句 名前 X を見たら、直前と直後の字句が :: かどうか判定する 直前の字句が :: ならばX に対して記号表を検索する 検索して見つからなければその時点でエラー 直後の字句が :: ならば X に対して記号表を検索する 直後の字句がまた名前 Y なら、Y に対して記号表は検索しないで Y の字句の種類は後で決める X に対してはそれ以外の名前⇒字句の規則を適用する 場合によって記号表の検索を適宜行う
N::outer::inner noi; N の直後は :: なので N に対して記号表を検索する outer の直前は :: なので outer に対して記号表を検索する outer は class-name であると判定される outer :: は nested-name-specifier に還元される inner の直前は :: なので inner に対して記号表を検索する inner は class-name であると判定される inner は outer とは異なり type-name に還元される noi の直前も直後も :: ではないのでこれ以外の規則が適用される この場合は decl_spec のスタックに型があるので記号表は検索されない
スコープの移動 class-or-namespace-name が還元されるときにスコープの移動をする: /* yacc/bison */ : class_name { $1 の表すクラスにスコープを移動; } | namespace_name { $1 の表す名前空間にスコープを移動; }
スコープの移動(続き) 以下でも良さそうだが間違い: nested-name-specifier: class-or-namespace-name :: { ここでスコープの移動 } class-or-namespace-name :: { ここでスコープの移動 } nested-name-specifier ... 前ページよりスコープの移動のタイミングが遅れる outer や inner は検索できない :: の前にスコープの移動を行うのが正しい つまりそれは前ページと同じ
シフト/還元衝突の対応 yacc/bison のレポートファイル 以下のコードを yyparse の適切な場所に挿入する: State X1 R11 nested_name_specifier: class_or_namespace_name COLONCOLON . nested_name_specifier R12 | class_or_namespace_name COLONCOLON . ... State X1 で例えば次の字句が class-name だった場合必ずシフトするのは間違い 以下のコードを yyparse の適切な場所に挿入する: if (yystate == X1 && peek() != COLONCOLON) { yyn = R12 + 1; goto yyreduce; }
還元/還元衝突の対応 yacc/bison のレポートファイル このままだと R22 の規則が使われることがない State X2 R21 type_name: class_name . R22 class_or_namespace_name: class_name . このままだと R22 の規則が使われることがない 以下のコードを yyparse の適切な場所に挿入する: if (yystate == X2 && peek() == COLONCOLON) { yyn = R22 + 1; goto yyreduce; }
:: に対するスコープの移動の後処理 もともとあったスコープ位置に戻す必要がある N::outer::inner noi; このポイントではスコープ位置はルートだった このポイントでスコープ位置を N::outer からルートに戻す
このポイントでは N::T::f のパラメータスコープ このポイントではルート void N::T::f ( N2::T2* p) { N3::T3::x を参照; このポイントで N::T::f のボディのスコープに戻す必要がある このポイントでは N::T::f のボディのスコープ } このポイントでルートに戻す必要がある。