0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

hspcmp(exe版)のコードを読む 3 ~tagstack.cpp/.h編~

Posted at

概要

このシリーズは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

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

コンストラクタ

一種類しかありません。ただフィールド変数を初期化しているだけです。

引数

なし

コードを見てみる

tagstack.cpp(192~207行目)
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_buftagid-1で、tagerr%err%で初期化され、これ以外は0で初期化されます。
ただし、mem_tagcheckは初期化されません。(StackCheckメソッドで初期化されるため)

StrCmpメソッド

指定した文字列が等しければ-1を、等しくなければ0を返します。
ただし、一致するのは文字コードが同じときのみです。(つまり大文字小文字・全角半角が区別されます)

引数

  • char *str1
    • 比較する文字列その1
  • char *str2
    • 比較する文字列その2

使われている変数

  • int ap
    • いま何文字目を比較しているのかを表す
  • char as
    • いま比較されているstr1の文字

コードを見てみる

tagstack.cpp(15~29行目)
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
    • ループカウンタ

コードを見てみる

tagstack.cpp(32~42行目)
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
    • タグ名の文字数。ただし終端文字も含みます。

コードを見てみる

tagstack.cpp(45~55行目)
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を設定してtagent1増やします。
次に、タグ名の文字数(終端文字も含む)が最大値(TAGSTK_TAGSIZE)を超えるかどうか確認して、超えるのならばそこで\0を挿入します。
最後に、mem_tag[i].nameにタグ名をコピー(登録)して、タグIDを返します。

GetTagUniqueNameメソッド

指定されたタグのタグ名を含んだ新たな文字列を生成します。
生成される文字列は、_%s_%04xに従います。(sはタグ名またはTagErrxは適当な数)

引数

  • int tagid
    • 指定するタグのタグID
  • char *outname
    • 生成された文字列が書き込まれるポインタ

使われている変数

  • TAGINF *t
    • 指定されたタグの情報

コードを見てみる

tagstack.cpp(58~69行目)
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

コードを見てみる

tagstack.cpp(72~80行目)
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

コードを見てみる

tagstack.cpp(83~89行目)
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
    • ループ中で検証されるスタックデータ

コードを見てみる

tagstack.cpp(92~101行目)
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を返します。

tagstack.cpp(102~117行目)
	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
    • 積むスタックのデータ

コードを見てみる

tagstack.cpp(120~127行目)
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を返します。

tagstack.cpp(128~134行目)
	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_bufi番目のポインタをtに代入します。
また、次に使うmem_bufのインデックスをlastidxに代入します。
次に、t->tagidtagidを代入し、積むスタックの内容の文字数(終端文字含む)が最大値(TAGSTK_SIZE)を超えていないことを確認してからt->datastrをコピー(登録)します。ここで指定したタグのスタックを積んでいます。
最後に、積んだスタックが全体で何番目のスタックなのかを返します。

PopTagメソッド

指定されたタグのスタックをPOPして、その値を返します。

引数

  • int tagid
    • 指定するタグのタグID

使われている変数

  • int i
    • ループ中で検証されるmem_bufのインデックス
  • TAGDATA *t
    • ループ中で検証されるスタックデータ
  • char *p
    • POPするスタックの内容

コードを見てみる

tagstack.cpp(137~146行目)
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に代入されます。

tagstack.cpp(147~152行目)
	while(1) {
		i--; if ( i<0 ) return NULL;
		t = &mem_buf[i];
		if ( t->tagid == tagid ) break;
	}
	p = t->data;

ここでは、すべてのタグのスタックを総当たりで検証しています。
まず、検証するスタックをtmem_bufから取り出します。
そして、tが指定されたタグのスタックであるかどうかを検証して一致するとループから抜け、pにそのスタックの内容(t->data)を代入します。

tagstack.cpp(153~162行目)
	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
    • 指定するスタックの層(09)
    • つまるところ、%pの後ろの値

使われている変数

  • int i
    • ループ中で検証されるmem_bufのインデックス
  • int lv
    • levelの値が代入されます。
  • TAGDATA *t
    • ループ中で検証されるスタックデータ

コードを見てみる

tagstack.cpp(165~175行目)
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に代入されます。

tagstack.cpp(176~185行目)
	while(1) {
		i--;if ( i<0 ) return NULL;
		t = &mem_buf[i];
		if ( t->tagid == tagid ) {
			if ( lv==0 ) break;
			lv--;
		}
	}
	return t->data;
}

ここではすべてのタグのスタックを総当たりで検証しています。
まず、検証するスタックをtmem_bufから取り出します。
そして、tが指定されたタグのスタックであるかどうかを確かめて、それが指定された層のスタックであればループを抜けt->data(スタックの内容)を返し、そうでなければlv--をしています。

なぜこの処理でよいのかというと、このループではスタックの上層から読みだしていっており、指定される値(level)は上から〇番目のスタックを表すので、指定されたタグのスタックを検証するたびにlv(level)の値を引いていくことで、lv0になった時のtが読み出したいスタックになるからです。

次は

次回は、token.hを解説します。(投稿は未定)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?