概要
このシリーズはHSPの.ax
ファイルの作成をするhspcmpを読んでいくものです。
わかったことをどんどん書き込んでいくスタイルでいきます。
間違いに気づいた方や、加筆すべきポイントを見つけた人はコメントで指摘してください。
あとここに掲載されたコードは
Hot Soup Processor (HSP) / OpenHSP Copyright (c) 1997-2020, onion software/onitama in collaboration with Sencha, Yume-Yume Yuuka, Y-JINN, chobin, Usuaji, Kenji Yuukoku, puma, tom, sakura, fujidig, zakki, naznyark, Lonely Wolf, Shark++, HyperPageProject, Chokuto, S.Programs, Yuki, K-K, USK, NGND001, yoshis, naka, JET, eller, arue, mjhd_otsuka, tds12 All rights reserved.
から一部改変したものです。
必要に応じて元のコードからインデントを調整したり、コメントや空行を省いています。
あと、これは2023/06/23時点のコードになります。
以前別名義で投稿していたものをほぼそのまま再投稿したものです。
またこれらのコードはOpenHSP 3.6のコードに基づいているため、3.6正式版や最新(3.7β)のものと異なる可能性があることに注意してください。
tagstack.cpp(tagstack.h)
#define特殊展開マクロ用スタック
引用: OpenHSPのチュートリアル - HSP開発wiki
だそうです。
つまり、プリプロセスの際に%t
やら%n
やらを処理するために使われます。
ここでは使用されたスタックの一つ一つを「タグ」として管理し、処理しています。
#difine一覧
- TAGSTK_MAX 256
- 全体で積めるスタック数の最大値
- TAGSTK_SIZE 124
- スタックの内容の文字数の最大値。ただし終端文字も含みます。
- TAGSTK_TAGMAX 256
- 登録できるタグの最大値
- TAGSTK_TAGSIZE 56
- タグ名の文字数の最大値。ただし終端文字も含みます。
TAGINF構造体
タグの本体です
- char name[TAGSTK_TAGSIZE]
- タグ名
- int check
- タグのスタックが解決されているかどうか。
- int uid
- GetTagUniqueNameメソッドで何回呼び出されたか。
TAGDATA構造体
タグごとの1スタックの内容を表します。
- int tagid
- タグのID
- char data[TAGSTK_SIZE]
- スタックの内容
CTagStackクラス
これがtagstack.cpp/.hの本体です。
フィールド
- TAGINF mem_tag[TAGSTK_TAGMAX]
- タグの管理配列。
- タグIDとはこの配列で何番目に登録されているかを示します。
- TAGDATA mem_buf[TAGSTK_MAX];
- スタック本体。ここにすべてのスタックの中身が登録されます。
- char tagerr[8]
- エラーメッセージ
- char tagent
- 次の処理で使われる
mem_tag
のインデックス - タグがどれだけ登録されたかを示します。
- 次の処理で使われる
- char lastidx
- 次の処理で使われる
mem_buf
のインデックス - すべてのタグを含め、全部でどれだけのスタックが積まれたのかを表します。
- 次の処理で使われる
- char gcount
- GetTagUniqueNameメソッドで何回ありえないタグIDが指定されたか。
コンストラクタ
一種類しかありません。ただフィールド変数を初期化しているだけです。
引数
なし
コードを見てみる
CTagStack::CTagStack()
{
int i;
for(i=0;i<TAGSTK_TAGMAX;i++) {
mem_tag[i].name[0] = 0;
mem_tag[i].uid = 0;
}
for(i=0;i<TAGSTK_MAX;i++) {
mem_buf[i].data[0] = 0;
mem_buf[i].tagid = -1;
}
tagent = 0;
lastidx = 0;
gcount = 0;
strcpy( tagerr, "%err%" );
}
mem_buf
のtagid
は-1
で、tagerr
は%err%
で初期化され、これ以外は0
で初期化されます。
ただし、mem_tag
のcheck
は初期化されません。(StackCheckメソッドで初期化されるため)
StrCmpメソッド
指定した文字列が等しければ-1
を、等しくなければ0
を返します。
ただし、一致するのは文字コードが同じときのみです。(つまり大文字小文字・全角半角が区別されます)
引数
- char *str1
- 比較する文字列その1
- char *str2
- 比較する文字列その2
使われている変数
- int ap
- いま何文字目を比較しているのかを表す
- char as
- いま比較されている
str1
の文字
- いま比較されている
コードを見てみる
int CTagStack::StrCmp( char *str1, char *str2 )
{
int ap;
char as;
ap=0;
while(1) {
as=str1[ap];
if (as!=str2[ap]) return 0;
if (as==0) break;
ap++;
}
return -1;
}
while
で全部の文字の比較が終わるか、一致しない文字が現れるまでループしていだけです。
SearchTagIDメソッド
指定したタグ名のタグIDを検索して、あればその値を、なければ-1
を返します。
引数
- char *tag
- タグ名
使われている変数
- int i
- ループカウンタ
コードを見てみる
int CTagStack::SearchTagID( char *tag )
{
int i;
if ( tagent==0 ) return -1;
for(i=0;i<tagent;i++) {
if ( StrCmp( mem_tag[i].name, tag )) return i;
}
return -1;
}
初めにそもそもタグが一つでも登録されているのかを調べ、なければ-1
を返します。
つぎに、タグの個数(tagent
)の数だけループを回してStrCmpメソッドでtag
と一致する登録されたタグ名(mem_tag[i].name
)を検索して、一致するものがあればそのID(i
)を、なければ-1
を返します。
RegistTagIDメソッド
指定されたタグ名のタグを登録し、そのタグのIDを返します。
引数
- char *tag
- 登録するタグ名
使用されている変数
- int i
- タグID
- int len
- タグ名の文字数。ただし終端文字も含みます。
コードを見てみる
int CTagStack::RegistTagID( char *tag )
{
int i,len;
if ( tagent>=TAGSTK_TAGMAX ) return -1;
i = tagent; tagent++;
len = strlen( tag );if ( len>=TAGSTK_TAGSIZE ) tag[TAGSTK_TAGSIZE-1]=0;
strcpy( mem_tag[i].name, tag );
return i;
}
初めに、登録するタグが最大個数(TAGSTK_TAGMAX
)を超えないことを確認して、タグIDを設定してtagent
を1
増やします。
次に、タグ名の文字数(終端文字も含む)が最大値(TAGSTK_TAGSIZE
)を超えるかどうか確認して、超えるのならばそこで\0
を挿入します。
最後に、mem_tag[i].name
にタグ名をコピー(登録)して、タグIDを返します。
GetTagUniqueNameメソッド
指定されたタグのタグ名を含んだ新たな文字列を生成します。
生成される文字列は、_%s_%04x
に従います。(s
はタグ名またはTagErr
、x
は適当な数)
引数
- int tagid
- 指定するタグのタグID
- char *outname
- 生成された文字列が書き込まれるポインタ
使われている変数
- TAGINF *t
- 指定されたタグの情報
コードを見てみる
void CTagStack::GetTagUniqueName( int tagid, char *outname )
{
TAGINF *t;
if (( tagid < 0 )||( tagid >= TAGSTK_TAGMAX )) {
sprintf( outname,"TagErr%04x",gcount++ );
} else {
t = &mem_tag[tagid];
sprintf( outname,"_%s_%04x", t->name, t->uid++ );
}
}
一つ目の分岐では、ありえないタグIDが指定されたときで、新たな文字列を作り出しています。
二つ目の分岐では、指定されたタグの情報をt
に取り出して、タグ名を用いて新たな文字列を作り出しています。
GetTagIDメソッド
指定したタグ名のタグIDを返します。タグが登録されていなければ、登録をしてそのタグIDを返します。ただし、登録できなかった場合は-1
を返します。
引数
- char *tag
- 指定するタグ名
使用されている変数
- int i
- 返すタグID
コードを見てみる
int CTagStack::GetTagID( char *tag )
{
int i;
i = SearchTagID( tag );
if ( i<0 ) { i = RegistTagID( tag ); }
return i;
}
初めに、SearchTagIDメソッドでタグ名からタグIDを取得し、取得できなければRegistTagIDメソッドでタグを登録してそのタグIDを取得します。
最後に、取得したタグIDを返します。
GetTagNameメソッド
指定したタグIDからタグ名を取得します。ありえないタグIDが指定されるとtagerr
が返されます。
引数
- int tagid
- 指定するタグID
コードを見てみる
char *CTagStack::GetTagName( int tagid )
{
if (( tagid < 0 )||( tagid >= TAGSTK_TAGMAX )) return tagerr;
return mem_tag[tagid].name;
}
ただありえないタグIDをはじいて、タグIDからmem_tag[tagid].name
でタグ名を取得しているだけです。
StackCheckメソッド
全てのスタックが解決されていれば0
を返し、指定した変数の値を\t
に書き換えます。
解決されていないスタックがあれば、そのスタックが存在するタグの個数を返し、指定した変数の値をそのスタックの情報に書き換えます。
引数
- char *res
- 書き換えられる変数
使われている変数
- int i
- ループ中で検証される
mem_buf
のインデックス
- ループ中で検証される
- int n
- 解決されていないスタックが存在するタグの個数
- TAGDATA *t
- ループ中で検証されるスタックデータ
コードを見てみる
int CTagStack::StackCheck( char *res )
{
int i,n;
TAGDATA *t;
strcpy ( res, "\t" );
for(i=0;i<TAGSTK_TAGMAX;i++) { mem_tag[i].check = 0; }
if ( lastidx<1 ) return 0;
ここでは、res
を\t
で書き換えて、全タグ(のスタック)が解決されたことを示すように変更しています。
あと、lastidx < 1
の時は全スタックが解決されているので0
を返します。
i = lastidx;
while(1) {
i--; if ( i<0 ) break;
t = &mem_buf[i];
if ( t->tagid >= 0 ) mem_tag[ t->tagid ].check = -1;
}
n=0;
for(i=0;i<TAGSTK_TAGMAX;i++) {
if ( mem_tag[i].check ) {
if ( n ) strcat( res, ", " );
strcat( res, mem_tag[i].name );
n++;
}
}
return n;
}
ここでは、全スタックが解決されたかどうかを調べています。
初めに、スタックが登録されているmem_buf
の一番最後のインデックスをi
に代入します。
次に、while
で解決されていないスタック(TAGDATA->tagid >= 0
)のタグのcheck
を-1
に設定します。
あと、for
で解決されてないスタックが存在するタグ(mem_tag[i].check != 0
)のタグ名をres
に書き込み、n++
をします。
最後に、解決されていないスタックが存在するタグ数を返します。
PushTagメソッド
指定したタグのスタックを積み、積んだスタックが全体で何番目のスタックなのかを返します。
引数
- int tagid
- 指定するタグのタグID
- char *str
- 積むスタックの内容
使われている変数
- int i
- 積むスタックが全体で何番目のスタックなのか
- int len
- 積むスタックの内容の文字数。ただし終端文字含みます。
- TAGDATA *t
- 積むスタックのデータ
コードを見てみる
int CTagStack::PushTag( int tagid, char *str )
{
int i,len;
TAGDATA *t;
if (( tagid < 0 )||( tagid >= TAGSTK_TAGMAX )) return -1;
if ( lastidx >= TAGSTK_MAX ) return -1;
ここでは、ありえないタグIDが指定されていないことやスタックを最大まで登録していないかを確認し、されていたら-1
を返します。
i = lastidx; lastidx++;
t = &mem_buf[i];
t->tagid = tagid;
len = strlen( str );if ( len>=TAGSTK_SIZE ) str[TAGSTK_SIZE-1]=0;
strcpy( t->data, str );
return i;
}
ここでは、初めに、使うmem_buf
のインデックスをi
に代入し、すでに初期化で確保されているmem_buf
のi
番目のポインタをt
に代入します。
また、次に使うmem_buf
のインデックスをlastidx
に代入します。
次に、t->tagid
にtagid
を代入し、積むスタックの内容の文字数(終端文字含む)が最大値(TAGSTK_SIZE
)を超えていないことを確認してからt->data
にstr
をコピー(登録)します。ここで指定したタグのスタックを積んでいます。
最後に、積んだスタックが全体で何番目のスタックなのかを返します。
PopTagメソッド
指定されたタグのスタックをPOPして、その値を返します。
引数
- int tagid
- 指定するタグのタグID
使われている変数
- int i
- ループ中で検証される
mem_buf
のインデックス
- ループ中で検証される
- TAGDATA *t
- ループ中で検証されるスタックデータ
- char *p
- POPするスタックの内容
コードを見てみる
char *CTagStack::PopTag( int tagid )
{
int i;
TAGDATA *t;
char *p;
if (( tagid < 0 )||( tagid >= TAGSTK_TAGMAX )) return NULL;
if ( lastidx<1 ) return NULL;
i = lastidx;
ここまでで、指定されたタグIDがありえないものかどうかや、そもそもスタックが積まれているのかを確認して、問題があればNULL
を返します。
そのあと、lastidx
の値がi
に代入されます。
while(1) {
i--; if ( i<0 ) return NULL;
t = &mem_buf[i];
if ( t->tagid == tagid ) break;
}
p = t->data;
ここでは、すべてのタグのスタックを総当たりで検証しています。
まず、検証するスタックをt
にmem_buf
から取り出します。
そして、t
が指定されたタグのスタックであるかどうかを検証して一致するとループから抜け、p
にそのスタックの内容(t->data
)を代入します。
t->tagid = -1;
i = lastidx - 1;
while(1) {
if ( i<0 ) break;
if ( mem_buf[i].tagid != -1 ) break;
lastidx = i;
i--;
}
return p;
}
ここでは、POPしたスタックの無効化と次の処理で使うmem_buf
のインデックスを設定しています。
初めに、t->tagid = -1
でスタックを無効化して、スタックが登録されているmem_buf
の一番最後のインデックスをi
に代入します。
次に、ループで一番最後からのPOP済みのスタック分だけlastidx
を巻き戻します。
最後にp
(POPするスタックの内容)を返します。
LookupTagメソッド
指定されたタグの指定されたスタックの内容を読み取りその値を返すか、指定されたスタックが存在しなければNULL
を返します。
ただし、POPしません。
引数
- int tagid
- 指定するタグのタグID
- int level
- 指定するスタックの層(
0
~9
) - つまるところ、
%p
の後ろの値
- 指定するスタックの層(
使われている変数
- int i
- ループ中で検証される
mem_buf
のインデックス
- ループ中で検証される
- int lv
-
level
の値が代入されます。
-
- TAGDATA *t
- ループ中で検証されるスタックデータ
コードを見てみる
char *CTagStack::LookupTag( int tagid, int level )
{
int i,lv;
TAGDATA *t;
if (( tagid < 0 )||( tagid >= TAGSTK_TAGMAX )) return NULL;
if ( lastidx<1 ) return NULL;
lv = level;
i = lastidx;
ここまでで、指定されたタグIDがありえないものかどうかや、そもそもスタックが積まれているのかを確認して、問題があればNULL
を返します。
そのあと、どの層のスタックを読みだすのかをlv
に代入し、lastidx
の値がi
に代入されます。
while(1) {
i--;if ( i<0 ) return NULL;
t = &mem_buf[i];
if ( t->tagid == tagid ) {
if ( lv==0 ) break;
lv--;
}
}
return t->data;
}
ここではすべてのタグのスタックを総当たりで検証しています。
まず、検証するスタックをt
にmem_buf
から取り出します。
そして、t
が指定されたタグのスタックであるかどうかを確かめて、それが指定された層のスタックであればループを抜けt->data
(スタックの内容)を返し、そうでなければlv--
をしています。
なぜこの処理でよいのかというと、このループではスタックの上層から読みだしていっており、指定される値(level
)は上から〇番目のスタックを表すので、指定されたタグのスタックを検証するたびにlv
(level
)の値を引いていくことで、lv
が0
になった時のt
が読み出したいスタックになるからです。
次は
次回は、token.hを解説します。(投稿は未定)