PureNScripterで擬似ハッシュ

今週のスクリプタ便り | FCめがぶーWEBページを読んだ。
ので、気分転換に昔書き散らしたのを清書してみた。
なお、このスクリプトは、拙作の半角全角判定(文字数取得機能追加) - 永字八法をベースにしている。

どういうものか。

一つの文字列変数を構造体と言うよりもハッシュや辞書配列のように扱う何か。
(半角全角ライブラリに加え)四つの命令を追加する。

  • getstr
  • setstr
  • getnum
  • setnum

名前だけ見れば機能は推測できると思う。一つの文字列変数をオブジェクトとしてみなし、与えたキーに対応する文字列または数値を取得または設定する。
書式は共通で、三つの引数をとる。

  1. オブジェクトとみなす文字列変数
  2. キーに相当する文字列変数
  3. 設定したい文字列または数値・あるいは、取得した文字列または数値を格納する変数

しかしまあ、速度は度外視してるから、まさかこれでアクションゲームは作らないと思う。RPGのキャラクターを格納したりするにはいいんじゃないかな。

注意点

キーや値との境目に、タブ「\t」を使っているので、キーや値にタブを混入させると色々困ったことになる。
いやまあ、普通にやってれば普通は混入しないとは思うんだけれども、一応指摘しておく。
それと、一旦オブジェクトとしてみなした文字列変数を他の手段でいじったりしたら、どういう挙動をするかの保証はないとする。

スクリプト(Aパート)

define節、game以上に記述。

mov %0,100 ; お好みで。

; ここからは、http://d.hatena.ne.jp/eiji8pou/20100820/1282234185 を参照のこと
numalias is_zen_result,%0:inc %0
numalias is_zen_target,%0:inc %0
numalias is_zen_cmp,%0:inc %0
numalias is_zen_len,%0:inc %0
numalias letter_result,%0:inc %0
numalias letter_base,%0:inc %0
numalias letter_loop,%0:inc %0
numalias letter_len,%0:inc %0
numalias cut_base,%0:inc %0
numalias cut_head,%0:inc %0
numalias cut_rest,%0:inc %0
numalias cut_len,%0:inc %0
numalias cut2_base,%0:inc %0
numalias cut2_left,%0:inc %0
numalias cut2_right,%0:inc %0
numalias cut2_temp,%0:inc %0

defsub is_zenkaku
defsub is_hankaku
defsub letter_len
defsub letter_num
defsub cut_head_letter
defsub cut_text
defsub is_ja
; ここまでは、http://d.hatena.ne.jp/eiji8pou/20100820/1282234185 を参照のこと

numalias ob_ob,%0:inc %0
numalias ob_mode,%0:inc %0
numalias ob_key,%0:inc %0
numalias ob_val,%0:inc %0
numalias ob_res,%0:inc %0
numalias ob_res2,%0:inc %0
numalias ob_new,%0:inc %0
numalias ob_k,%0:inc %0
numalias ob_v,%0:inc %0
numalias ob_letter,%0:inc %0
numalias ob_flag,%0:inc %0
numalias ob_temp,%0:inc %0
stralias ob_sp,"	"

defsub getstr
defsub setstr
defsub getnum
defsub setnum

スクリプト(Bパート)

start節end以下に記述

*getnum
	getparam s%ob_ob,$ob_key,i%ob_res2
	getstr $%ob_ob,$ob_key,$ob_temp
	mov %%ob_res2,0
	notif $ob_temp="" atoi %%ob_res2,$ob_temp
return

*setnum
	getparam s%ob_ob,$ob_key,%ob_val
	itoa $ob_val,%ob_val
	setstr $%ob_ob,$ob_key,$ob_val
return

*getstr
	getparam s%ob_ob,$ob_key,s%ob_res
	mov %ob_mode,0 ; getモード
goto *obstr_main

*setstr
	getparam s%ob_ob,$ob_key,$ob_val
	mov %ob_mode,1 ; setモード
goto *obstr_main

*obstr_main
	if %ob_mode=0 mov $%ob_res,"" ; こたえを返す変数を初期化
	if %ob_mode=0 if $%ob_ob="" return ; getモードで、オブジェクトが空の時のショートカット
	if %ob_mode=1 if $%ob_ob="" goto *obstr_before_end
	mov %ob_flag,0 ; フラグを初期化
	mov $ob_new,"" ; 新しいオブジェクトを初期化
	
*obstr_main_loop
	mov $ob_k,""
	mov $ob_v,""

*obstr_main_get_key
	cut_head_letter $%ob_ob,$ob_letter,$%ob_ob ; オブジェクトから最初の一文字を取得。
	if $ob_letter=ob_sp goto *obstr_main_get_val
	add $ob_k,$ob_letter
	goto *obstr_main_get_key
	
*obstr_main_get_val
	cut_head_letter $%ob_ob,$ob_letter,$%ob_ob ; オブジェクトから最初の一文字を取得。
	if $ob_letter=ob_sp goto *obstr_main_get_check
	add $ob_v,$ob_letter
	goto *obstr_main_get_val

*obstr_main_get_check
	notif $ob_key=$ob_k goto *obstr_main_get_check2 ; キーが一致しない。
	mov %ob_flag,1 ; 指定されたキーとオブジェクトのキーが一致した。
	if %ob_mode=0 mov $%ob_res,$ob_v ; getモードなら値を返す。
	if %ob_mode=1 mov $%ob_v,$ob_val ; setモードなら値を変更する。
*obstr_main_get_check2
	; オブジェクトを再構成する。
	add $ob_new,$ob_k
	add $ob_new,ob_sp
	add $ob_new,$ob_v
	add $ob_new,ob_sp
	notif $%ob_ob="" goto *obstr_main_loop ; 空になるまで続く。
	mov $%ob_ob,$ob_new ; 再構成
	if %ob_flag=0 if %ob_mode=0 goto *obstr_end
*obstr_before_end
	add $%ob_ob,$ob_key
	add $%ob_ob,ob_sp
	add $%ob_ob,$ob_val
	add $%ob_ob,ob_sp
*obstr_end
return

; ここからは、http://d.hatena.ne.jp/eiji8pou/20100820/1282234185 を参照のこと

;==================
; is_zenkaku
;==================
; 文字列を与え、その文字列の先頭の文字が全角かどうか判定する。
; 第一引数:結果を受け取る数値変数
; 第二引数:判定対象の文字列(変数)
; 結果は、0が半角、1が全角
; 依存:なし
*is_zenkaku
getparam i%is_zen_result,$is_zen_target
mov %%is_zen_result,0
cmp %is_zen_cmp,$is_zen_target,"゚":   if %is_zen_cmp>0 mov %%is_zen_result,1:return
cmp %is_zen_cmp,$is_zen_target,"。":notif %is_zen_cmp<0 return
cmp %is_zen_cmp,$is_zen_target,"~":   if %is_zen_cmp>0 mov %%is_zen_result,1:return
return

;==================
; is_hankaku
;==================
; 文字列を与え、その文字列の先頭の文字が半角かどうか判定する。
; 第一引数:結果を受け取る数値変数
; 第二引数:判定対象の文字列(変数)
; 結果は、0が全角、1が半角
; 依存:is_zenkaku
*is_hankaku
getparam i%is_zen_result,$is_zen_target
is_zenkaku %%is_zen_result,$is_zen_target
mov %%is_zen_result,1-%%is_zen_result
return

;==================
; letter_len
;==================
; 文字列を与え、その文字列の先頭の文字が、1バイトか2バイトかを判定する。
; 第一引数:結果を受け取る数値変数
; 第二引数:判定対象の文字列(変数)
; 結果は、0が文字列無し、1が半角、2が全角
; 依存:is_zenkaku
*letter_len
getparam i%is_zen_result,$is_zen_target
mov %%is_zen_result,0
if $is_zen_target="" return
is_zenkaku %%is_zen_result,$is_zen_target
inc %%is_zen_result
return

;==================
; letter_num
;==================
; 文字列を与え、その文字列が文字数でどれだけの長さになるかを返す。
; 第一引数:結果を受け取る数値変数
; 第二引数:判定対象の文字列(変数)
; 結果は、文字数
; 依存:letter_len
*letter_num
getparam i%letter_result,$letter_base
mov %%letter_result,0 ; 初期設定
if $letter_base="" return ; 空文字列なら、0を返す。
len %letter_base,$letter_base ; バイト長さを取得
if %letter_base=1 mov %%letter_result,1:return ; 1バイト長さなら、1を返す。
mov %letter_loop,0 ; 初期設定その2
*letter_num_loop
mid $letter_loop,$letter_base,%letter_loop,1 ; 一文字を取得
letter_len %letter_len,$letter_loop ; その文字の全角半角判定
inc %%letter_result ; 一文字は一文字
add %letter_loop,%letter_len ; 文字のバイト数分ずらす。
if %letter_loop<%letter_base goto *letter_num_loop
return

;==================
; cut_head_letter
;==================
; 文字列を与えると、1バイトか2バイトかを判別して、その先頭の文字を返す。
; 第一引数:分割したい文字列
; 第二引数:先頭の文字列を受ける文字列変数
; 第三引数:先頭の文字を落とした残りを受ける文字列変数
; 依存:letter_len
; 第一引数と第三引数を同じ文字列変数を使うと便利かも。
*cut_head_letter
getparam $cut_base,s%cut_head,s%cut_rest
mov $%cut_head,"" ; 初期設定
mov $%cut_rest,"" ; 初期設定
if $cut_base="" return ; ショートカット
len %cut_base,$cut_base
if %cut_base=1 mov $%cut_head,$cut_base:return ; ショートカットその2
letter_len %cut_len,$cut_base ; 先頭の文字の長さを取得。
if %cut_len=%cut_base mov $%cut_head,$cut_base:return ; ショートカットその3
; それ以外ならば、
mid $%cut_head,$cut_base,0,%cut_len ; 先頭から1文字ないし2文字を取得
mid $%cut_rest,$cut_base,%cut_len,%cut_base-%cut_len ; 残りを格納
return

;==================
; cut_text
;==================
; 文字列と数値を与えると、その文字数で分割して返す。
; 第一引数:分割したい文字列
; 第二引数:何文字で分割するか。
; 第三引数:先頭から第二引数の文字列
; 第四引数:残りの文字列
; 依存:cut_head_letter
; 数値を140にすると、Twitterに使える。
*cut_text
getparam $cut2_base,%cut2_base,s%cut2_left,s%cut2_right
mov $%cut2_left,""
mov $%cut2_right,""
if $cut2_base="" return ; ショートカット
*cut_text_loop
cut_head_letter $cut2_base,$cut2_temp,$cut2_base ; 分割して$cut2_tempに入れる。
add $%cut2_left,$cut2_temp
if $cut2_base="" return
dec %cut2_base
if %cut2_base>0 goto *cut_text_loop
mov $%cut2_right,$cut2_base ; 残りを入れる。
return

;==================
; is_ja
;==================
; 文字列を与え、その文字列に2byte文字が含まれているかどうかを返す。
; 第一引数:結果を受け取る数値変数
; 第二引数:判定対象の文字列(変数)
; 結果は、0が全て半角、1が全角が含まれる。
; 依存:なし
*is_ja
getparam i%is_zen_result,$is_zen_target ; 結果を格納する変数,対象の文字列
mov %%is_zen_result,0 ; 結果を0で初期化
len %is_zen_len,$is_zen_target ; 対象の文字列の長さを取得
mov %is_zen_target,-1 ; 対象も文字列へのカーソル
*is_ja_loop
inc %is_zen_target ; カーソルを一つすすめる。
if %is_zen_len-2 < %is_zen_target return ; 残り文字数が2未満ならば、0で回答
mid $is_zen_len,$is_zen_target,%is_zen_target,2 ; カーソル位置から2文字を取得。
cmp %is_zen_cmp,$is_zen_len,"゚":   if %is_zen_cmp>0 mov %%is_zen_result,1:return
cmp %is_zen_cmp,$is_zen_len,"。":notif %is_zen_cmp<0 goto *is_ja_loop ; ループする。
cmp %is_zen_cmp,$is_zen_len,"~":   if %is_zen_cmp>0 mov %%is_zen_result,1:return
goto *is_ja_loop ; ループする。

; ここまでは、http://d.hatena.ne.jp/eiji8pou/20100820/1282234185 を参照のこと