ローカルラベル

  • (by K, 2018.06.14)

(1)

  • C言語の変数の仕組みは面白い。{ }でブロックを作ると、ブロックの中では自由に変数宣言をすることができる。このとき外側の変数と同名でもかまわない。同名の場合、ブロック内ではブロック内で宣言した変数が使われて、ブロックから出ると元に戻る。
  • これによりちょっとしたコード片やマクロなどがかなり書きやすくなる。コピペするだけで再利用も可能だ。変数名を付け直さなくてもよくなる。

(2)

  • 実はアセンブラの世界でも、この便利さを目指したものがある。それがローカルラベルだ。たとえばMASMの場合、@@というラベル宣言と、@f、@bというラベル参照がある。
  • @bは現在の位置から戻って一番近い@@:にマッチする。@fは現在の位置から進んで一番近い@@:にマッチする。だから@@:は何度でも使えてマクロを作るときに便利である。
  • これに対してNASMのローカルラベルはさらに一歩進んでいる。@@方式ではラベルに名前はつけられないし、結局マクロ内では1組しかラベルを使えなかった。NASMは「グローバルラベルが宣言されるたびに異なるローカルラベル空間ができる」という仕様を採用した。これにより、.label1:や.label2:みたいな表現を何回も使うことができる(ピリオドで始まるのがNASMのローカルラベル)。


  • ここでC言語に戻るが、C言語では変数は{ }でネストできるのに、goto用のラベルはネストできない。ラベルは関数内では一意でなくてはいけないのだ。だからラベルを使わないで書くように努力することになる。そうすれば衝突はない。まあC言語はラベルを使わなくてもたいていのことはできる。
    • 多重ループから抜け出したいとき、gotoを使えば素直に書けるが、制御用の変数を追加して条件分岐を追加すれば同等のことはできる。私はそれを美しいとは思わないけど、C言語としてはラベルが使えない状況ではそうやって解決するしかないので、そうするのが流儀である。

(3)

  • jck0は、page0013で紹介しているアセンブラみたいなものである。この言語では同名の変数宣言をしてもエラーにならない(v02以降)。宣言すると以前の同名の変数は一時的に見えなくなり、変わりに新しい変数が見える。変数の削除をするとまた以前の同名の変数が見えるようになる。そういう仕様を採用した。・・・変数の削除というのは参照用の名前空間から消えるだけであって、実体が消えるわけではない。もしポインタなどで参照していればその値が壊されないことが確認できるだろう。
  • これはいうなればブロックを書かなくてもできる自由な変数宣言である。これによりjck0は変数名の管理に限って言えばC言語と同等の書きやすさを提供できる。
  • さらにgotoラベルについても同様の仕組みを導入した。だからローカルラベルが実現できていることになる。しかもこの仕組みを採用したのでネストも可能である。


  • ラベルは変数と違い、前方参照がありうる。
    // コード片1
    if (...) goto label;
    ...
    label:
    ... 
    
    // コード片2
    if (...) goto label;
    ...
    label:
    ...
  • こういう場合、コード片2のif-gotoはいったいどちらのlabelに行けばいいのだろう。普通に考えたらコード片1のlabelに行くことになるだろう(その先に宣言があることを知らない限り、それ以外の対応は思いつかない)。いやでもそうではなくて、それぞれのコード片のlabelに行ってほしいかもしれない。そこで次のような構文を考えた。
    // コード片1
    reserve(label);
    if (...) goto label;
    ...
    label:
    ... 
    
    // コード片2
    reserve(label);
    if (...) goto label;
    ...
    label:
    ...
  • リザーブ命令は、ここで新しいラベル名を宣言するけど、実際のラベル位置はあとで宣言します、という意味だ。これにより、コード片2のif-gotoではlabelが新規に作り直されるので、コード片1のlabelにはマッチしない。めでたくコード片2のlabelにマッチするようになる。
  • jck0ではパーサーをシンプルにするためにもっと汚らしい書き方をするのだけど、しかし意味的にはここで説明したとおりである。

(4)

  • ローカルラベルが使えると上位の言語処理系はこんなに楽になる。
  • たとえば do { をjck0に翻訳するとしたらこれだけでよい。
    $reserve continue break ; 
    $label continue0 ; 
  • そして } while (...); をjck0に翻訳するとしたらこれだけで良い。
    $label continue ; 
    $if ... $goto continue0 ; 
    $label break ; 
    $del continue0 continue break ; 
  • この置換は、do~whileがネストしていても全く問題なく使えるし、ループの中で $goto break ; や $goto continue ; すれば問題なく対応する場所へジャンプできる。
  • つまり上位の言語処理系は、面倒なラベル管理をしなくてよくなる。

こめんと欄


コメントお名前NameLink

トップ   編集 凍結 差分 バックアップ 添付 複製 名前変更 リロード   新規 一覧 単語検索 最終更新   ヘルプ   最終更新のRSS
Last-modified: 2018-06-15 (金) 15:15:10 (521d)