hinekure.net が http://hspdev-wiki.net/ から自動クローリングした結果を表示しています。画像やリソースなどのリンクが切れています。予めご了承ください。
OpenHSPのチュートリアル/CLabelを読む - HSP開発wiki
トップ    編集凍結 差分バックアップ添付複製名前変更リロード   新規一覧単語検索最終更新   最終更新のRSS

CLabelを読む

hspcmp/label.h, hspcmp/label.cpp のCLabelクラスを読みます。 CLabel は、hspcmp で識別子を管理するためのクラスです。プリプロセス時とコンパイル時に使用されます。

基本的なインターフェース

Regist
識別子を登録し、その識別子に対応するIDを返します。
Search, SearchLocal?
で名前からそれに対応するIDを検索します。なければ -1 を返します。
GetOpt?, SetOpt?, GetData?, SetData?, GetData2, SetData2
識別子に対応付けられたデータを取得(Get)、設定(Set)する関数です。

LABOBJ構造体

識別子1つの情報を管理する構造体です。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
-
|
|
|
|
|
|
|
|
|
|
!
typedef struct LABOBJ {
    int        flag;                // exist flag
    int        type;                // object type
    int        opt;                // option code
    short    eternal;            // eternal flag
    short    ref;                // reference flag
    int        hash;                // hash code
    char    *name;                // object name (lower case)
    char    *data;                // data field
    char    *data2;                // data field (opt)
    LABREL    *rel;                // relation id
} LABOBJ;
flag
通常は 1 で、#undef されると -1 になります。
type
LAB_TYPE_* または TYPE_* の定数の値が入ります。
opt
識別子に対応付けられた整数値です。
eternal
スコープを無視して永続的に使用できる識別子の場合、1 になり、そうでない場合 0 になります。例えば、#deffunc などは前者で、global がない #define などは後者です。
ref
識別子の参照回数です。#deffunc, #defcfunc, #func で定義された識別子の場合のみ使われます。
hash
識別子を表現する文字列のハッシュ値です。
name
識別子を表現する文字列 ( へのポインタ ) です。
data
識別子に対応付けられたC形式文字列 ( へのポインタ ) です。
data2
識別子に対応付けられた、'\0' も含められるバイト列 ( へのポインタ ) です。
rel
未使用 #func, モジュール削除のための連結リストです。詳しく後述します。

コンパイル中には type, opt の値はそれぞれ、Code SegmentのType値、Code値に使われます。たとえば "mes@hsp" は type = TYPE_EXTCMD, opt = 0x0f です。
一方、プリプロセス中には、type に LAB_TYPE_* の定数値が入ります。opt の値は、識別子の種類(typeの値)に依存します。

LAB_TYPE_*

繰り返しになりますが、プリプロセス時に登録される識別子のtype値は LAB_TYPE_* 定数の値が入ります。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 
 
 
 
 
 
 
 
 
 
#define LAB_TYPE_PPVAL 0x110
#define LAB_TYPE_PPMAC 0x111
#define LAB_TYPE_PPMODFUNC 0x112
#define LAB_TYPE_PPDLLFUNC 0x113
#define LAB_TYPE_PPINTMAC 0x114
#define LAB_TYPE_PPEX_INTCMD 0x115
#define LAB_TYPE_PPEX_EXTCMD 0x116
#define LAB_TYPE_PPEX_PRECMD 0x117
#define LAB_TYPE_COMVAR 0x200
#define LAB_TYPE_PP_PREMODFUNC 0x212
LAB_TYPE_PPVAL
#enum, #constで定義される定数。値は、int なら opt に、double なら data2 に格納されます。
LAB_TYPE_PPMAC
#defineで定義されるマクロ。opt が引数の数と ctypeフラグ(PRM_MASK, PRM_FLAG_CTYPE)、dataが置き換えられる文字列、data2がデフォルト引数データ(MACDEF) となっています。
LAB_TYPE_PPMODFUNC
#deffuncなどで定義される命令・関数です。
LAB_TYPE_PPDLLFUNC
#func, #moduleなどで定義される識別子。一緒になっているのはおそらくプリプロセス時にはこれらを区別できるようになっている必要がないからと推測されます。
LAB_TYPE_PPINTMAC
内部マクロ。標準命令*1や、#cmdで定義される識別子で使われます。LAB_TYPE_PPMACとの違いは、プリプロセス行のときはマクロ展開しないという点です。
LAB_TYPE_PPEX_***
hsc3_getsym用。これは、標準スクリプトエディタの色分けに使われます。
LAB_TYPE_COMVAR
#usecomで定義されるインターフェース名。
LAB_TYPE_PP_PREMODFUNC
プロトタイプ宣言されたユーザー定義命令・関数。ただし、HSP3.2現在、プロトタイプ宣言は未実装です。乞うご期待?
Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 
-
|
|
|
|
|
|
!
// 関係のある部分のみ抜粋
class CLabel {
    ……(略)……
private:
    LABOBJ *mem_lab;                    // Label object
    int    cur;                            // Current
    int maxlab;                            // Max Label Size
    ……(略)……
}

mem_labがLABOBJの配列です。登録された識別子のLABOBJは全て、この配列に格納されます。maxlabがmem_labのサイズ、curがmem_labの位置です。

初期化

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 
-
|
|
|
|
|
!
 
 
 
-
|
|
|
|
!
CLabel::CLabel( void )
{
    ……(略)……
    maxlab = def_maxlab;
    mem_lab = (LABOBJ *)malloc( sizeof(LABOBJ)*maxlab );
    ……(略)……
    Reset();
}
 
 
void CLabel::Reset( void )
{
    int i;
    cur = 0;
    for(i=0;i<maxlab;i++) { mem_lab[i].flag = -1; }
    ……(略)……
}

見てのとおり、mem_labを初期化しています。

識別子の登録です。
余談ですが、Register (登録する、という意味の動詞) の間違いだと思います。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 
-
|
|
|
-
|
|
|
|
|
|
|
|
|
!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
int CLabel::Regist( char *name, int type, int opt )
{
    int a;
 
    if ( name[0]==0 ) return -1;
    if ( cur>=maxlab ) {                // ラベルバッファ拡張
        LABOBJ *tmp;
        int i,oldsize;
        oldsize = sizeof(LABOBJ)*maxlab;
        maxlab += def_maxlab;
        tmp = (LABOBJ *)malloc( sizeof(LABOBJ)*maxlab );
        for(i=0;i<maxlab;i++) { tmp[i].flag = -1; }
        memcpy( (char *)tmp, (char *)mem_lab, oldsize );
        free( mem_lab );
        mem_lab = tmp;
    }
 
    a = cur;
    LABOBJ *lab=&mem_lab[cur++];
    lab->flag = 1;
    lab->type = type;
    lab->opt  = opt;
    lab->eternal = 0;
    lab->ref = 0;
    lab->name = RegistSymbol( name );
    lab->data = NULL;
    lab->data2 = NULL;
    lab->hash = StrCase( lab->name );
    lab->rel = NULL;
    return a;
}

maxlabまで使いきったらサイズを拡張 (def_maxlab個) します。
RegistSymbol?は名前を内部のバッファにコピーして、そのアドレスを返します。StrCase?は名前を小文字化させ、ハッシュ値を計算します。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
-
|
|
|
|
|
|
|
|
|
|
-
-
-
|
|
-
|
|
|
|
!
!
!
|
!
|
!
int CLabel::Search( char *oname )
{
    //		object name search
    //
    char as;
    int a;
    int hash;
    char *str1,*str2;
    if (cur==0) return -1;
 
    hash = StrCase( oname );
    LABOBJ *lab = mem_lab;
    for(a=0;a<cur;a++) {
        if ( lab->flag >= 0 ) {
            if ( hash == lab->hash ) {
                str1=oname;
                str2=lab->name;
                while(1) {
                    as=*str1;
                    if (as!=*str2) break;
                    if (as==0) return a;
                    str1++;str2++;
                }
            }
        }
        lab++;
    }
    return -1;
}

識別子から、それに対応するIDを検索します。登録されていない場合、負数(-1)を返します。
引数の文字列を StrCase?() で書き換えるため、使うときには注意が必要です。

ハッシュ値を用いた線形探索で検索しています。

ちなみに、線形探索ではなく std::map (STLのコンテナの1つ、連想配列) を使うようにするパッチも作られていますが、まだ採用はされていません (HSP3.2現在)。

SeachLocal?

これは Search の特殊版です。
例えば、foo というモジュール内で "bar" という識別子があったとき、eternal な "bar" と、eternal な "bar@foo" を同時に検索します。この例では、引数 oname に "bar"、同 loname に "bar@foo" が指定されます。
ここで、eternal な "bar" と非eternalな"bar@foo"が両方ある場合、どちらを用いるかは未定義として実装されています。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 
-
|
|
|
|
|
|
|
|
|
|
|
-
-
-
|
|
-
|
|
!
-
|
-
|
|
|
|
!
!
!
|
!
|
!
int CLabel::SearchLocal( char *oname, char *loname )
{
    //		object name search ( for local )
    //
    char as;
    int a;
    int hash,hash2,myhash;
    char *str1,*str2;
    if (cur==0) return -1;
 
    hash = StrCase( oname );
    hash2 = GetHash( loname );
    LABOBJ *lab = mem_lab;
    for(a=0;a<cur;a++) {
        if ( lab->flag >= 0 ) {
            if (lab->eternal) {
                str1 = oname;
                myhash = hash;
            } else {
                str1 = loname;
                myhash = hash2;
            }
            if ( lab->hash == myhash ) {
                str2=lab->name;
                while(1) {
                    as=*str1;
                    if (as!=*str2) break;
                    if (as==0) return a;
                    str1++;str2++;
                }
            }
        }
        lab++;
    }
    return -1;
}

onameは小文字化して、lonameは小文字化していない (StrCase?ではなく小文字化しないGetHash?を呼んでいる)のは謎です。もっとも、両方とも小文字化されてから呼ばれるので、CLabelで小文字化する必要はないのかもしれません。

登録されたデータをダンプ

どんな識別子がどんなデータで登録されているかを、以下の関数を追加して呼び出せば、調べることができます。(label.hに「void Dump( void );」と宣言をお忘れなく)
プリプロセス時のデータならCToken::GetLabelInfo?に、コンパイル時のデータならCToken::GenerateCode?の「res = GenerateCodeMain?( srcbuf );」の後ろあたりに「lb->Dump();」を追加するといいでしょう。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 
 
 
-
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
|
!
 
 
-
|
|
|
-
|
|
|
|
|
|
|
|
-
|
!
|
-
|
-
|
|
!
|
!
|
|
!
!
#include "../hsp3/hsp3struct.h"
 
static char *get_label_type_name( int type )
{
    switch(type) {
#define T(type) case type: return #type;
    T(TYPE_MARK)
    T(TYPE_VAR)
    T(TYPE_STRING)
    T(TYPE_DNUM)
    T(TYPE_INUM)
    T(TYPE_STRUCT)
    T(TYPE_XLABEL)
    T(TYPE_LABEL)
    T(TYPE_INTCMD)
    T(TYPE_EXTCMD)
    T(TYPE_EXTSYSVAR)
    T(TYPE_CMPCMD)
    T(TYPE_MODCMD)
    T(TYPE_INTFUNC)
    T(TYPE_SYSVAR)
    T(TYPE_PROGCMD)
    T(TYPE_DLLFUNC)
    T(TYPE_DLLCTRL)
    T(TYPE_USERDEF)
    T(LAB_TYPE_PPVAL)
    T(LAB_TYPE_PPMAC)
    T(LAB_TYPE_PPMODFUNC)
    T(LAB_TYPE_PPDLLFUNC)
    T(LAB_TYPE_PPINTMAC)
    T(LAB_TYPE_PPEX_INTCMD)
    T(LAB_TYPE_PPEX_EXTCMD)
    T(LAB_TYPE_PPEX_PRECMD)
    T(LAB_TYPE_COMVAR)
    T(LAB_TYPE_PP_PREMODFUNC)
#undef T
    }
    return "unknown";
}
 
void CLabel::Dump( void )
{
    LABOBJ *p = mem_lab;
    LABOBJ *pend = p + cur;
    int i = 0;
    while (p < pend) {
        printf("#ID:%d (%s) flag:%d type:%s opt:%#x eternal:%d ref:%d",
               i,
               p->name,
               p->flag,
               get_label_type_name(p->type),
               p->opt,
               p->eternal,
               p->ref);
        if(p->data) {
            printf(" data=<%s>", p->data);
        }
        puts("");
        if(p->rel) {
            LABREL *l = p->rel;
            while(l) {
                printf(" -> %d (%s)", l->rel_id, mem_lab[l->rel_id].name);
                l = l->link;
            }
            puts("");
        }
        p ++;
        i ++;
    }
}

以下はプリプロセス時のダンプの一部です。

#ID:0 (goto) flag:1 type:LAB_TYPE_PPINTMAC opt:0 eternal:1 ref:0 data=<goto@hsp>
#ID:1 (gosub) flag:1 type:LAB_TYPE_PPINTMAC opt:0 eternal:1 ref:0 data=<gosub@hsp>
#ID:2 (return) flag:1 type:LAB_TYPE_PPINTMAC opt:0 eternal:1 ref:0 data=<return@hsp>

標準命令の名前に"@hsp"をつけるようにマクロ定義されているのがわかります。

そして、以下がコンパイル時のダンプの一部です。

#ID:0 (goto@hsp) flag:1 type:TYPE_PROGCMD opt:0 eternal:1 ref: 0
#ID:1 (gosub@hsp) flag:1 type:TYPE_PROGCMD opt:0x1 eternal:1 ref: 0
#ID:2 (return@hsp) flag:1 type:TYPE_PROGCMD opt:0x2 eternal:1 ref: 0

コンパイル時にはtypeにはCode SegmentのためのType値、optにはCode SegmentのためのCode値が使われているのがわかります。ここで登録されたType値やCode値がCode Segmentに出力されるときに使われます。

IDからデータを取得・設定する関数

関数名を見ただけで何をするのか分かるので、説明の必要もないでしょう。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 
 
 
 
 
 
 
 
 
 
 
 
void SetEternal( int id );
int GetEternal( int id );
void SetFlag( int id, int val );
int GetFlag( int id );
void SetOpt( int id, int val );
int GetOpt( int id );
void SetData( int id, char *str );
char *GetData( int id );
void SetData2( int id, char *str, int size );
char *GetData2( int id );
int GetType( int id );
char *GetName( int id );

#undefされるとSetFlag?(id, -1)が呼ばれ、無効になりますが、データは消していないようです。

RegistList?

hspcmd.cppで定義されているような標準命令の一覧から、コンパイル用に識別子を登録します。 hspcmp.cppで定義されているhsp_prestrは "$000 15 goto" のような文字列の配列になっています。$000がCode SegmentのためのCode値(16進数)、15がCode SegmentのためのType値です (TYPE_PROGCMD == 15)。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 
-
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
|
!
|
!
int CLabel::RegistList( char **list, char *modname )
{
    //		キーワードリストを登録する
    //
    char *p;
    char **plist;
    char tmp[256];
    int id,i,type,opt;
    i = 1;
    plist = list;
    while(1) {
        p = tmp;
        strcpy( p, plist[i++] );
        if (p[0]!='$') break;
        p++;
        p = GetListToken( p );
        opt = HtoI();
        p = GetListToken( p );
        type = atoi( token );
        p = GetListToken( p );
        strcat( token, modname );
        id = Regist( token, type, opt );
        SetEternal( id );
    }
    return 0;
}

引数modnameには"@hsp"が指定されて呼び出されます。
GetListToken?は引数のアドレスからスペース以外の文字で構成されたトークンを取り出します。取り出したトークンはメンバ変数の固定長文字列tokenにコピーされます。そして、読み進めたアドレスを返します。たとえば引数に" foo bar"を指定すると、tokenには文字列"foo"がコピーされ、引数の文字列アドレス+strlen(" foo")が返されます。

HtoIはtokenを16進数文字列としてintに変換する、つまり atoi の16進数バージョンです。

RegistList2

RegistList2はRegistList?のプリプロセス版です。 「登録されたデータをダンプ」で見たように、標準命令の名前に"@hsp"をつけるようにマクロ定義しています。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 
-
|
|
|
|
|
|
|
|
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
!
|
!
int CLabel::RegistList2( char **list, char *modname )
{
    //		キーワードリストをword@modnameの代替マクロとして登録する
    //
    char *p;
    char **plist;
    char tmp[256];
    int id,i,type,opt;
    i = 1;
    plist = list;
    while(1) {
        p = tmp;
        strcpy( p, plist[i++] );
        if (p[0]!='$') break;
        p++;
        p = GetListToken( p );
        opt = HtoI();
        p = GetListToken( p );
        type = atoi( token );
        p = GetListToken( p );
        //id = Regist( token, type, opt );
 
        id = Regist( token, LAB_TYPE_PPINTMAC, 0 );        // 内部マクロとして定義
        strcat( token, modname );
        SetData( id, token );
        SetEternal( id );
    }
    return 0;
}

GetNumEntry?, GetCount?

識別子の個数 (つまりメンバ変数curの値) を返します。
なぜか同じ意味のメンバ関数が二つもありますが、ミスか、それとも何か意図があるのかはわかりません。

symbolバッファ

symbolバッファは、(LABOBJの) name や data, data2 のためのバッファです。malloc でバッファを確保するのはすこし重いので、確保するときは大きくどーんと確保して、後で必要な大きさに切って使います。いわゆるストレージというヤツです。

小分けで確保した部分は、一度使うと、もし不要になっても hspcmp が処理を終えるまで解放されない、かつ使い回せない仕様になっています。

symblock
symbolバッファ (へのポインタ) の配列です。
curblock
symblock の使っている要素の数です。言い換えると、symblockの、次に使われる要素の番号です。
maxsymbol
それぞれのsymbolバッファのサイズです。symbolバッファはどれも同じサイズのようです。
symbol
今使っているsymbolバッファ (つまりsymblock[curblock - 1]) へのポインタです。
symcur
symbol の中で使用しているサイズです。言い換えると、未使用領域の先頭の位置です。

symbolバッファの初期化

「初期化」の項であえて無視したsymbolバッファの初期化部分。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
-
|
|
|
|
|
|
!
 
 
-
|
|
|
|
|
!
CLabel::CLabel( void )
{
    int i;
    maxsymbol = def_maxsymbol;
    maxlab = def_maxlab;
    mem_lab = (LABOBJ *)malloc( sizeof(LABOBJ)*maxlab );
    for(i=0;i<def_maxblock;i++) { symblock[i] = NULL; }
    Reset();
}
 
void CLabel::Reset( void )
{
    int i;
    cur = 0;
    for(i=0;i<maxlab;i++) { mem_lab[i].flag = -1; }
    DisposeSymbolBuffer();
    MakeSymbolBuffer();
}

今回は、mem_labの初期化部分はスルーして読みます。
symblockの全要素をNULLにし、DisposeSymbolBuffer?MakeSymbolBuffer?を呼びます。

MakeSymbolBuffer?

新しいsymbolバッファを追加します。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
 
-
|
|
|
|
!
void CLabel::MakeSymbolBuffer( void )
{
    symbol = (char *)malloc( maxsymbol );
    symblock[curblock] = symbol;
    curblock++;
    symcur = 0;
}

DisposeSymbolBuffer?

全てのsymbolバッファを破棄します。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 
-
|
-
-
|
|
!
!
|
!
void CLabel::DisposeSymbolBuffer( void )
{
    int i;
    for(i=0;i<def_maxblock;i++) {
        if ( symblock[i] != NULL ) {
            free( symblock[i] );
            symblock[i] = NULL;
        }
    }
    curblock = 0;
}

ExpandSymbolBuffer?

sizeバイト書き込める領域を返します。 現在のsymbolバッファを使い切って足りない場合はMakeSymbolBuffer?を呼び出して新しいsymbolバッファを追加します。
ただし、size が maxsymbolより大きいことは考慮していません。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 
-
|
|
|
-
|
|
|
!
|
!
char *CLabel::ExpandSymbolBuffer( int size )
{
    char *p;
    p = symbol + symcur;
    symcur += size;
    if ( symcur >= maxsymbol ) {
        MakeSymbolBuffer();
        symcur += size;
        return symbol;
    }
    return p;
}

RegistTable?

symbolバッファに、指定サイズのバイナリデータを登録します。SetData?, SetData2ではこれが使われます。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 
-
|
|
|
|
|
|
|
|
!
char *CLabel::RegistTable( char *str, int size )
{
    //		シンボルテーブルにテーブルデータを登録
    //
    char *p;
    char *src;
    src = str;
    p = ExpandSymbolBuffer( size );
    memcpy( p, src, size );
    return p;
}

RegistSymbol?

symbolバッファに識別子の文字列をコピーして、そのアドレスを返します。RegistTable?(str, strlen(str)+1)とはほとんど変わりませんが、サイズを maxname バイトに制限しています。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
-
|
|
|
|
|
|
|
|
|
|
|
|
|
-
|
|
|
|
|
!
|
|
|
|
|
!
char *CLabel::RegistSymbol( char *str )
{
    //		シンボルテーブルに文字列を登録
    //
    char *p;
    char *pmaster;
    char *src;
    char a1,a2;
    int i;
    //int hush;
    i = 0;
    p = ExpandSymbolBuffer( strlen(str)+1 );
    pmaster = p;
    src = str;
    a2 = *src;
    while(1) {
        a1=*src++;
        *p++ = a1;
        if ( a1 == 0 ) break;
        if ( i >= (maxname-1) ) { *p=0; i++; break; }
        i++;
    }
    //if (i) a1 = str[i-1];
    //symcur+=i+1;
    //hush = (a1+a2+i)&31;
    //return hush;
    return pmaster;
}

未使用 #func, #module 削除用の連結リスト

LABOBJ::ref, LABOBJ::relは未使用な #func, モジュールを削除するためにあります。

LABOBJ::ref の型 LABREL がどんな型か見てみましょう。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
 
 
-
|
|
!
typedef struct LABREL LABREL;
 
struct LABREL {
    LABREL    *link;                // link to next (NULL=end)
    int        rel_id;                // related id
};

単方向連結リストになっています。ラベルが参照する複数のラベルを繋ぎます。

それが実際にどのように使われているか「登録されたデータをダンプ」のダンプ結果を見て調べてみましょう。以下のスクリプトをプリプロセスしたダンプを見てみます。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#module mod_f
#deffunc f1
    return
#deffunc f2
    return
#deffunc f3
    return
#global
 
#module mod_g
#deffunc g1
    return
#deffunc g2
    return
#deffunc g3
    return
#global
 
    f1 : f1 : f1 : f1 : f1
    f2 : f2 : f2
       (...前略...)
#ID:201 (mod_f) flag:1 type:LAB_TYPE_PPDLLFUNC opt:0 eternal:1 ref:0
 -> 202 (f1) -> 203 (f2) -> 204 (f3)
#ID:202 (f1) flag:1 type:LAB_TYPE_PPMODFUNC opt:0 eternal:1 ref:5
#ID:203 (f2) flag:1 type:LAB_TYPE_PPMODFUNC opt:0 eternal:1 ref:3
#ID:204 (f3) flag:1 type:LAB_TYPE_PPMODFUNC opt:0 eternal:1 ref:0
#ID:205 (mod_g) flag:1 type:LAB_TYPE_PPDLLFUNC opt:0 eternal:1 ref:0
 -> 206 (g1) -> 207 (g2) -> 208 (g3)
#ID:206 (g1) flag:1 type:LAB_TYPE_PPMODFUNC opt:0 eternal:1 ref:0
#ID:207 (g2) flag:1 type:LAB_TYPE_PPMODFUNC opt:0 eternal:1 ref:0
#ID:208 (g3) flag:1 type:LAB_TYPE_PPMODFUNC opt:0 eternal:1 ref:0

LABOBJ::rel から参照されているラベルは「-> label id (name) -> label id (name) ...」のように表示しています ( 以下のコードの通り )。

Everything is expanded.Everything is shortened.
  1
  2
  3
  4
  5
  6
  7
  8
-
|
-
|
|
!
|
!
        if(p->rel) {
            LABREL *l = p->rel;
            while(l) {
                printf(" -> %d (%s)", l->rel_id, mem_lab[l->rel_id].name);
                l = l->link;
            }
            puts("");
        }

これを見ると、「モジュール名のラベル」からそのモジュール内の全部のユーザ定義命令・関数を連結リストで結んでいるようです。

また、f1, f2 の ref がそれぞれ 5, 3 になっています。呼び出しの回数を記録しているようですね。

未使用モジュールの削除の手順、まとめ:

  1. プリプロセス時にユーザ定義命令・関数が定義されたとき、そのモジュールのラベルの rel 連結リストに、定義するラベルを追加する。
  2. プリプロセス時にユーザ定義命令・関数の呼び出しがあれば、その関数の呼び出し回数をインクリメントする。
  3. コンパイル時に、プリプロセス時のラベル情報を使って、モジュール内の全てのユーザ定義命令・関数の呼び出し回数を調べ、すべて 0 の場合、そのモジュールをコンパイルしない。

#funcで定義される関数もほぼ同様で、呼び出し回数をカウントし、0 なら定義を無視されます。

なお、モジュール内で #func 関数を呼んだときは、呼び出し回数を増やすのではなく、連結リストにモジュールのラベルを追加しています。これは、未使用モジュールのみから呼ばれた#func関数も削除されるようにということだと思います。

コメント


URL B I U SIZE Black Maroon Green Olive Navy Purple Teal Gray Silver Red Lime Yellow Blue Fuchsia Aqua White
  • あまりよい実装とは思いませんが、露骨に批判的なのはどうかと思いますので、加筆ついでに修整しておきました。
  • 「(relが、連結リストではなく)配列でもできることをしている」の件について:リンク1本で結合できるのが連結リストの強み(の一つ)なので、記述を削除しました。
    「(symbolバッファが)name や data, data2 が変更されても再利用できない」の件について:一時的に使用するだけなので、大問題ではありませんから、表現を緩めました。 -- UD? 2009-09-22 (火) 1:34:54
  • 校正ありがとうございます! -- fujidig? 2009-10-13 (火) 06:16:20

*1 "mes"->"mes@hsp"のようなマクロ
トップ    編集凍結 差分バックアップ添付複製名前変更リロード   新規一覧単語検索最終更新   最終更新のRSS
Last-modified: 2009-10-13 (火) 06:16:39 (1517d)