なんだか雲行きの怪しい雑記帖

ぐだぐだ日記とメモと,あと不定期更新

【HSP】不穏な動的実行プラグイン「mist」

いつかやると思ってました
ちょびっと後悔してます

…VM作るのは楽しかったけどな!!



何するプラグイン?

おそらくHSPからHSP”ライク”なスクリプトを食って実行するためのプラグインです

コンセプトというかオブジェクティブはhsp_luaと同じなので省略
 (あっちはもうメンテできないと思ったので今は公開してませんが;;)

pros

  • HSPと互換性のある文法

  •  完全互換ではないですが,変数へ代入から,条件分岐や反復などの制御構文,さらに関数定義やモジュール定義などもほぼ同一です
     逆にサポートされない機能としては「#include」などのプリプロセッサ(特に「#define」などのマクロ定義がないのは痛いですかね),COM系統の命令とその書き方(アロー演算子「->」によるCOMのインタフェース取得と指定メソッドの呼び出し)などです


  • HSP本体との凶悪な強力なグルー性能

  •  mistはかなりHSP本体にべったり依存する形で書いてある箇所があり,随所でHSP内部の機能へmistからアクセス可能です
     例えば変数は内部表現がHSPと互換性があり,mist内の変数とHSP側の変数を対応付けること(バインド)ができます
     また,HSP側で定義したラベルについてもmist内で参照し使用することが可能になっています
     更に,こちらはまだベータ機能ですが,HSP側でインポートした外部DLL関数や,HSP内部で作成したユーザ定義関数,HSP内部命令についてもmist内に取り込むことができます



cons

  • 中途半端な互換性

  •  機能面での互換性では,HSP本体とは別の実装になっている箇所もあり,色々互換性がなかったりします
     例えばはHSP本体では関数における引数は基本的にリファレンスでありread onlyですが,mist内の関数では引数はローカル変数であり関数内で変更可能です
     他にはHSP本体では配列へ順にアクセスしていき,未定義添字への書き込みはその添字が連続である場合は自動的に配列が伸長され再確保されますが,mist内ではそのような処理はやっていません
     この辺はHSP本体側のコードが変化する可能性があるからであり,あまり細かいところを一致させようとしてもイタチごっこになるような気がして,無意味に思えるからやってないだけだったりしますが…


  • 微妙な実行速度

  •  現在のところ,純粋な仮想マシンとしての性能は若干HSPを上回っていますが,実際の計算部分や命令がHSP側に依存しているため,HSP内部の命令を呼び出すためのオーバーヘッドによりHSP本体より若干遅い程度の実行速度となっています
      (ただし,関数呼び出しに関しては,引数がインスタンスとして別々に確保されるため,再帰関数では実行速度はHSPより劣ります)
     この辺は私の気が向いたらJIT化しようかなとか思っていますが,まだまだバイトコード表現など改良の余地が多いのと,あんまり私が専門知識を持っていないので苦戦してます




どんなことできるの?

目指すところがHSP構文上位互換,という訳ではないです,最初に言っておくと

HSPのスクリプトっぽく書いてそれっぽい実行ができる感じを目指しました
それなりなことが出来るような気がします,多分

スクリプトのロード

evalっぽいことが出来ると言いつつ,厳密にはHSPの実行環境とは別の実行環境が用意されており,そこに実行時にスクリプトを追加して実行していくというようなイメージになります

例えば,次のようなHSPスクリプトがあるとします
    l=20 : r=10
v = l+r

これを実行時に,HSPとは別の実行環境(mist環境:ライブラリ名がmistのため)で実行させることができるプラグインです
#include "mist.hsp"
mstload {"
l=20 : r=10
v = l+r
"}

こうすると,mist環境内で変数「l」と「r」,そして「v」が定義され,「v」の値は「30」となった状態になります

mist環境では,スクリプトをどんどん追加していくことが可能であり,先ほどの例では「l」と「r」の初期化と,「v」への代入が一気に行われましたが,次のようにスクリプトを分割して追加していくような書き方にすることも可能です
#include "mist.hsp"
mstload "l=20 : r=10"
mstload "v = l+r"

スクリプトを少しずつ追加して,追加された分だけその時々に実行されるため,全体の実行結果は先ほどの例と全く同じになります
実行処理のイメージ補足
#include "mist.hsp"
mstload "l=20 : r=10"
mstload "v = l+r"

を実行する場合,まず最初の「mstload」ではmist環境で次のようなスクリプトが実行されることになります
    l=20 : r=10

実行される行はハイライトしてありますが,一行しかないので全部実行されたことと等価ですね
さて,そして2つ目の「mstload」を実行した際,mist環境では次のスクリプトのうち,ハイライトされた部分が実行されます
    l=20 : r=10
// ここまでは既に前回の「mstload」で実行されている
v = l+r

このように,「mstload」でHSPっぽい実行環境にどんどんスクリプトが追加されていき,追加された分だけ実行されるというようなイメージになります

※なお,「mstload」は「スクリプトを追加し,追加した分だけ実行」ですが,関数やモジュールを定義したいだけの場合には「スクリプトを追加する」処理だけを実行する「mstcompile」っていう命令もあります


mist環境内の変数の値をとってくる命令「mstget」を使って,少しずつスクリプトを追加していった時の「v」の値の様子は次のような感じになります
#include "mist.hsp"
mstload "l=20 : r=10"
mes "v="+mstget("v")
mstload "v = l+r"
mes "v="+mstget("v")

実行結果
v=
v=30

最初の実行ではまだ「v」という変数は存在しないため,値は表示されていません(正確には,存在しなかった場合は空文字が返されます)
2つ目の実行が終わった時に「v」という変数が存在し,かつ値として「l+r」が代入されているため,その値が表示されています

このように,スクリプトを少しずつ追加していくことが可能な,HSPとは別の実行環境を実現するようなプラグインです

変数

    // integer
mstload "v = 80"
mes "v(80) = "+mstget("v")

// double
mstload "v = 0.2"
mes "v(0.2) = "+mstget("v")

// string
mstload {"v = "str""}
mes "v(str) = "+mstget("v")

実行結果
v(80) = 80
v(0.2) = 0.200000
v(str) = str

 既に先ほど色々説明しましたが,もう一度念のため
 mstloadは渡されたスクリプトを追加して実行する命令です
 mstgetはその名前の変数の内容をとってくる命令です

 上記例では変数を扱ってます,使えないと辛いので
 変数の型はint,double,stringの三つ
  (内部処理として本当はもう少しありますが,まだいい感じな処理になるよう書いてないです)


演算

    mstload "l = 20 : r = 12"

// 足し算
mstload " res = l+r"
mes "add : "+mstget("res")

// 引き算
mstload "res = l-r"
mes "sub : "+mstget("res")

// 掛け算
mstload "res = l*r"
mes "mul : "+mstget("res")

// 割り算
mstload "res = l/r"
mes "div : "+mstget("res")

// 剰余算
mstload "res = lr"
mes "mod : "+mstget("res")

// …以下略…

実行結果
add : 32
sub : 8
mul : 240
div : 1
mod : 8

 変数や値同士の演算もできます,出来ないと辛いですし
 なるべくHSPと同じ演算子が同じ意味合いになるようになっていると思います,私の方で抜けがなければ
 注意点は,剰余演算子「¥」は文字列中ではエスケープシーケンスにより「¥¥」(ブログの都合上全角)と書かなくてはいけない点ですかね
 わざわざエスケープシーケンスで二個も書きたくないなぁと思う人は,「%」も同様の意味を持つ(TK_MOD)のでそっちを使うことも可能です

制御フロー

    // if
// 単体
mstload {"
if ( 1 < 100 ) : v = 5
"}
mes "if_mono : "+mstget("v")

// elseつき,省略形
mstload {"
if ( 1 > 100 ) : v = 5 : else : v = 9
"}
mes "if_reg : "+mstget("v")

// ブロック形(私はこっちが好み)
mstload {"
if ( 1 > 0 ) {
v = 2
} else {
v = 3
}
"}
mes "if_block : "+mstget("v")

// continue, break
mstload {"
sum = 0
repeat 10
if ( cnt == 2 ) : continue
if ( cnt == 8 ) : break
sum += cnt
loop
"}
mes "repeat_sum : "+mstget("sum")// 0~7までの和,ただし2抜き,よって26

実行結果
if_mono : 5
if_reg : 9
if_block : 2
repeat_sum : 26


 制御フローは最悪必要であろう「if」と「repeat」があります,無いと辛いですし

 速度面では,ifは内部的にショートジャンプなのでオーバーヘッドそこまで大きくないですが,
 repeat-loopはラベル二個(最初と最後)作って飛ぶのでifに比べて若干コストが高いです
  (匿名ラベルによる処理で,loopはgotoでrepeatの先頭ステートメントへとジャンプします)

 文法的にifのあとにelseが二個来たりする場合とかはどうなるのかチェックしてないです,でも本来ill-formedなんですよねソレ…

 repeat-loopに使われる特殊変数(?)「cnt」は,mistのループ内でない場合はHSP側のcntに問い合わせされるように書いてあります
 ただ,mistのループ内であるかどうかの判定箇所はgosub等の戻ってくるタイプのジャンプでリセットされる(スタックに積まれる)ので,細かい動作がHSPとは異なります
 早い話が「repeat-loop内でgosubした先でcnt使わないでください」ってことです

 switchはやるかもしれませんが,forとwhileは多分やらないです
 なくても何とかなると思います(


バインド

    v = 2
mstbind "v", v
mstload "res = v*2"
mes "res=" + mstget("res")

ddim ar, 8
mstbind "ar", ar
mstset "res", 92.5
mstload "ar(3) = res"
mes "ar(3)=" + ar(3)

実行結果
res=4
ar(3)=92.500000

 もうおなじみですね(苦笑)
 HSP側の変数をmistプラグイン中の変数に対応させます

 「対応」というと抽象的な表現ですが,要するに変数の内容をHSP側とmist側とで共有します
 どちらかの環境で変数が変更されると,もう片方の環境で参照する時に変更された後の値が取得できます
 予めHSP側で変数をバインドさせておき,動的な処理をmistにさせることで変数の内容を書き換えてもらう,という処理も可能です(これにより「eval」に似た動作をさせることができます)

 HSPの変数は内部が最大4次元配列だったりと他言語に比べ相当に変態的なんですが,それに合わせてmist内の変数も実装されている(内部はHSPと同じPVALです)ため,物凄く自然な書き方になりました,というか多分同じです
 この点に関しては,今までLuaやSquirrel等で散々遠回りしてきたので,敢えてVM作るからにはここまでしないと意味がないかなとは思っていました


ラベル

        // mist内goto
mstload {"
goto *lb
v = 92.8
*lb
"}
mes "v="+mstget("v")

// mistからの値返しにはreturnが使える
mstload "return 10.1*2.9"
mes "refdval="+refdval

// mist内gosub
// ちなみに,ラベルは最後に定義されたものが優先(上書き)されます
mstload {"
gosub *lb
return ""+refdval
*lb
return 1.2
"}
mes "refdval="+refdval+" : refstr="+refstr

 もちろんHSP側のラベルもバインドできるようになってます,mist内のラベルはmist内のラベルを指す場合とHSPのラベルを指す場合とででゅあるな構造です
        // hsp内のラベルをmist内にバインドできます
mstreglabel "hsplabel", *hsplabel
mstload "gosub *hsplabel"
mes "after : refstr="+refstr


 注意点としては,mist側のラベルはHSPのソレとは厳密に違う言わば「似非ラベル」なので,HSPにエクスポートするなどはできません,当たり前ですが
 また,レクサとパーサの都合もあるんですが,「v = *label」な書き方はmistでは今のところ扱えません
  (この仕様をどうしようかについてはまだ考えてないですね)
※追記
0x27050よりmistからHSPへラベルのエクスポートが可能になりました。本記事中の「HSPへのラベルのエクスポート」を参照してください



コマンド・関数定義

    mstload {"
return "str:"+func( 10 )

#deffunc func int i
return i
"}
mes "stat="+stat+" : refstr="+refstr

実行結果
stat=1 : refstr=str:10

 ちょっと混乱するかもしれませんが,HSPと違いmist内では「#deffunc」と「#defcfunc」の違いはありません
 いずれの構文(プリプロセッサ)で定義してもコマンド・関数両形式で呼ぶことが可能です
 (HSPではコマンドか関数かで定義し,その文法に則って呼ぶ必要がありますが,名前重複が許されないのでこれの意義がよく分かっていないんですよね…ので無くしました)

コマンド形式で読んだ場合,戻り値はその型によって適宜HSP側の「stat」か「refdval」か「refstr」に反映されます
それ以外の型がコマンド形式で返された場合は何も処理されません(ただし,エラーにもなりません)
 (具体的には,NIL(戻り値なし)やラベル型など)

コマンド・関数による変数の名前隠蔽について
HSPと違いmistでは関数が後のevalで定義されることもありうるため,右辺値として「f(a)」のような「関数呼び出し」とも「変数アクセス」ともとれる表現は「呼び出し表現(CallExpression)」として処理されます
この辺が少しややこしいのですが,変数よりもコマンド・関数の方を優先する仕様になっているため,CallExpressionが処理される段階でmistの環境が保持する「f」としてコマンド・関数があるかないかで処理が変わります
    mstload {"
f(0) = 10
*a
v = f(0)// このfは実行時に変数か関数か解決
return
"}
mes "v:var f(0)="+mstget("v")// 最初は変数しかないので変数「f(0)」と等価

mstcompile {"
#deffunc f int p
return p+12
"}
mstgosub "a"// ここを実行する際,関数「f」があるので関数「f(0)」と等価
mes "v:func f(0)="+mstget("v")

逆に言うと,変数として使っていた「f」は,同名のコマンド・関数が定義された時点で名前的に隠蔽され,基本的にアクセス不能になります
モジュールも指定した処理は可能なため,例えば「f@m1(a)」と「f@m2(a)」なる書き方をした場合は使い分けは可能ですが,モジュール内での探索も変数よりもコマンド・関数の名前の方が優先されます
但し,これらは「f(a)」な書き方をした場合の話であり,単項目の「f」及び代入先として現れる「f(a)」は,変数の「f」へ問い合わされるように書いてあります



HSP側の命令呼び出し

    mstopenhsplib

form = "sin( rad )"
mes "form="+form+" : "+mstload("return "+form)

form = "cos( rad )"
mes "form="+form+" : "+mstload("return "+form)

mstload {"
pos 400 : color ,,255 : mes "nyaa"
cos rad// mist内では関数とコマンドの区別はない,戻り値によって適宜システム変数へ返される
"}
mes "refdval="+refdval

「mstopenhsplib」コマンドによって,現在のmist環境にHSP側の標準命令の一部が定義されます
「sin」や「cos」といった数学系の関数の他,「pos」や「mes」などのGUI命令も使用可能になります※

マルチスレッド対策としての一部コマンドの内部処理化
後述しますが,mistでは限定的ながらマルチスレッド処理をサポートしています
しかしHSP自体はスレッドセーフではないため,mist内でのマルチスレッドにおいては,あるスレッドがHSPのコマンド・関数を実行中の際は他のスレッドがHSPの命令を実行しないようにブロックする処理が入ります
これは,「pos」や「mes」などのGUI命令は同期的に動いてくれるため有難いのですが,逆にマルチスレッドで高速に計算したいような処理があったとき,「sin」や「cos」など並列に動いて欲しいような関数もブロックされることを意味します
これではマルチスレッドにしてもあんまり意味がないような気がしたので,一部の数学関数系はmist内で独自に処理されます
厳密にHSP側の処理と結果が同じであることは保証できなくなりますが,並列実行に対応しているため,マルチスレッドにおいてもブロックすることなく処理されます
これらの計算の厳密性を追求する場合は,「mstopenhsplib」の第一引数を色々弄ると出来るようになるのですが,まだリファ作成中…


※ただ,HSP側の命令をmist内から呼ぶのは本来できないことをやっているハックなのでそこそこ不安定です(see:その他)
 特に「stop」,「wait」,「await」などの待機命令系はあんまりちゃんとチェックしてないですが,タイミングによっては落ちると思います


HSP側のユーザ定義関数の呼び出し:experimental

#module
#deffunc func_add var v, int n
v+=n
return

func_add
// HSPのAXファイルに参照を埋め込むために書いてるだけ
// それにしても,引数とかは省略しても通るのが<s>マジキチ</s>素敵だと思う
// プリプロセッサで関数シグネチャ書いてるのって一体何なんだろう,的な
#global

mstcaptfunc
mes ""+stat+"個のユーザー定義命令・関数をmist内にキャプチャしたようです"

// mist からの呼び出し
res = 0.0
mstbind "res", res
mstload "func_add res, 10"
mes "func_add(10) : res="+res

前述したHSPの一部標準命令を実装が,ユーザ定義関数についても流用できそうだったのでやってしまいました
ただ,手順がHSP内部命令よりも少しだけ厄介なのと,現バージョンの実行コードに強く依存しているため,こっちの方が不安定です

HSP,EXE化するとき変数については最適化で名前が落ちるのに対して,コマンドと関数は名前が落ちないんですね…
内部で格納される場所がDLL系の命令とほとんど区別がなく,一方でDLL系の命令は動的に「LoadLibrary」したあと「GetProcAddress」するために名前が必要だ,という論理が背景にありそうですが,詳細は知りません


HSPへのラベルのエクスポート

    mstopenhsplib

mstcompile {"
*mist_label
mes "mist_label is called!!"
return
"}

// mistのラベルをHSPのラベルにエクスポートして取得
mist_label = mstgetlabel("mist_label")
gosub mist_label

mist内で定義したラベルについては、HSP側から「mstgetlabel」命令によって,HSPで扱えるラベルとして取得することができます
ただし、mist内で定義したラベルは当然ながらmistが保持しているものため、mstinitやmstdestroyによって環境の破棄・初期化が行われると無効な場所を指すラベルになってしまうので注意してください
エクスポート可能なラベル数には上限があったり、mist内で定義したラベルはmist中で扱おうとする限り自動でエクスポートされないなど、いくつか注意事項がありますので、詳しい使い方はサンプルの「【エクストラ9】ラベルエクスポート」を参照してください


マルチスレッド処理

    mstcompile {"
#deffunc msum int s, int n, local sum
sum =0
repeat n
sum += s+cnt
loop
mes ""+sum
return
"}
repeat 5
mstthreadcall "msum", 10+cnt, 10// タスクのPush
loop
mstsetworkernum 5// ワーカーの数
mstthreadjoin// タスクの実行&終了待機

145
155
165
175
185

HSPのユーザ定義関数の呼び出しも相当凶悪でしたが,こっちも負けず劣らず邪悪に見えますね

上手くマルチスレッドで示せる例がなかったのであまり適当ではない気がしますが,何かしらの計算(今回は適当な数列の和の計算)を並列で行うような例です
計算結果は実行する度に順番が変わったり変わらなかったりします

マルチスレッドとしてはワーカースレッドパターンに則って実装しており,タスクを投げるとそれらが各ワーカー(スレッド)に分配されて処理されます
タスクとしては「関数呼び出し」と「ラベルへのgosub」が指定可能です
特にこの「関数呼び出し」ではその関数の引数も一緒にタスクとして保存されるため,各タスクに異なる引数を与えることで異なる処理をマルチスレッドに処理させることが可能となります(これが,HSP本体と違いmistでは関数の引数が呼び出しごとに実体を持つ理由だったりします)

タスクが実際にワーカーに分配され処理されはじめるのは「mstthreadjoin」を実行した時としています
「mstthreadcall」だけでは実行はされません(タスクが積まれるだけ)
「mstthreadjoin」ではタスクが全て終わるまで呼び出し元をブロックします
これらはmist内でのマルチスレッド処理に関わらずHSP側の処理がスレッドセーフに実行されることを保証する関係の実装で,今回の例で言うとmist内でのマルチスレッドにおいて「mes」の処理をHSPにお願いする時スレッドセーフに実行されます(他スレッドからの割り込みを受け付けません)
 (HSPでは命令や関数の引数の解析処理はstaticな変数を介して行うため,「mes」などのコマンド実行中に他スレッドからの割り込みを許すと引数の整合性が合わなくなるだけでなく,そのままクラッシュすることが多々あります)


だうんろーど

 普通に限りなく未完成に近いα版なのでだうんろーどした人は例外なく人柱です
 「動かなくてもイイ!」ぐらいの根性がある方のみ,どうぞ

 2013/07/15 ver.alpha deprecated...
 2013/07/20 ver.alpha deprecated...
 2013/07/29 ver.alpha deprecated...
 2013/08/04 ver.alpha deprecated...
 2013/09/21 ver.alpha deprecated...
 2013/10/09 ver.alpha deprecated...
 2014/02/05 ver,alpha deprecated...
 2014/03/15 ver.alpha deprecated...
 2014/03/21 ver.alpha deprecated...
 2014/09/23 ver.alpha deprecated
 2014/10/05 ver.alpha deprecated
 2015/03/23 ver.alpha deprecated
 2015/04/01 ver.alpha deprecated
 2015/04/29 ver.alpha(0x25000) deprecated
 2015/05/21 ver.alpha(0x25010) deprecated
 2015/05/23 ver.alpha(0x26000) deprecated
 2015/06/21 ver.alpha(0x26010) deprecated
 2015/08/16 ver.alpha(0x26020) deprecated
 2015/10./11 ver.alpha(0x26030) deprecated
 2016/03/05 ver.alpha(0x26040) deprecated
 2016/03/06 ver.alpha(0x26042) deprecated
 2016/04/23 ver.alpha(0x27000) deprecated
 2016/04/30 ver.alpha(0x27020) deprecated
 2016/05/08 ver.alpha(0x27040) deprecated

 2016/05/24 ver.alpha(0x27050) GoogleDrive
  VMの 1 バイト目がラベルであった場合有効なラベルとして処理されない問題を修正
  mistのラベルをHSP側に公開する機能の追加
   mstgetlabel命令を追加
   mist中の値評価で自動でエクスポートするオプション MIST_ENVCONF_ENABLE_AUTO_EXPORT_LABEL を追加
  dllが巨大化しすぎた気がするので速度最適化のポリシーを少し変えてビルドするようにしました
   前バージョンより遅くなった気がしますがバイナリは小さくなりました
   環境(CPU)によっては分岐処理のキャッシュヒットがよくなって早くなる場合もある、かも

 2019/04/09 ver.alpha(0x27060) GoogleDrive
  ver 0x27050エンバグしたswitchの分岐処理が正しく動作してないのを修正
   VMの相対ジャンプ時のプログラムカウンターの仕様変更に追従できていなかった問題
  ビルド時のプラットフォームツールセットvs2015相当に更新
   これによりバイナリのサイズが増えました
  Xbyak、FSBAllocatorをコードに含まないように修正(ライセンス項目から外れます)
   この修正によりmistを使う上で必要なライセンス表記がOpenHSPのみになりました
   元々XbyakはJITのテスト用で、現状含める必要がないため、必要になったときに追加しようと思います

 ※HSP3.2以降だとまだ安定しているようです,HSP側のコアにべったりなので基本は最新版(3.3から3.4)をお使いください

詳説

いくつか説明不足だったところがあるように思ったので

mstload中のreturnについて

最初の方で「mstload」は渡されたスクリプトを追加して実行する命令だと書きました
ただ,一行evalの使い方として次のような書き方が可能です
#include "mist.hsp"
mstload "l=20 : r=10"
mes "l+r="+mstload("return l+r")

l+r=30

確かに「mstload」はスクリプトを追加して実行しますが,その追加された部分を実行する際は「gosub」と同じような扱いになります
なので,「mstload」中での「return」は,「gosub」した先のサブルーチンが値を返せるのと同様に値を返すことが可能です
ちなみに,「mstload」で読み込んだコードの最後には擬似的に「return」文が追加されており,ユーザが「mstload」中に「return」を書かなかった場合は,最後の何も値を返さない「return」が実行されるような作りになっています

変数の存在する・しないについて

とても紛らわくてちゃんと説明してなかったんですが,「変数が存在する」条件についてです
HSPでは変数が出現するとその変数は存在し,かつ自動的に初期化されます
mistでもこれに準拠しているため,「変数が存在しない」とは「mist環境内に食わせたスクリプト中に変数名が出てこない」ことと等価です
例えば,「mstget」を使用した際に考えられる変数の状況3つについて再現したソースコードが次
#include "mist.hsp"
// 何もスクリプトを追加しない場合
mes "v:initial="+mstget("v")

// スクリプトを追加だけした場合
// vは存在するが,初期化された状態
mstcompile {"
*label
v = 92.5
return
"}
mes "v:compiled="+mstget("v")

// vへの代入コードを実行後
mstgosub "label"
mes "v:assigned="+mstget("v")

v:initial=
v:compiled=0
v:assigned=92.500000

二番目の「mes」で分かると思いますが,コンパイルされた時点で変数「v」は存在し,かつ初期化された(int型の配列16要素)状態になります
ちょっとややこしいですね

関数・ラベルの多重定義について

全ソースコードが分かった状態でコンパイルする本家とは違い,mist環境内では少しずつスクリプトを追加する動作であるため,関数・ラベル等の多重定義が行われた場合,エラーとはせず新しい定義の方を優先する動作になります
#include "mist.hsp"
mstcompile {"
#deffunc f
return 90
"}
mes "f:first="+mstcall("f")

mstcompile {"
#deffunc f
return 92.5
"}
mes "f:overwrite="+mstcall("f")

f:first=90
f:overwrite=92.500000

今回は「mstcall」を使いましたが,これはmist環境内のコードについても同様な動作になります

その他


メモリについて

 ユーザが投げてきたスクリプトは全てVM内のコードバッファに単純に追加されます
 繰り返し,「ユーザが投げてきたスクリプトは全てVM内のコードバッファに追加されます
 つまり,スクリプトを投げれば投げるだけメモリ食います
 リテラル等もリテラル用バッファに追加されます,intとdoubleは即値として処理されますが…
    repeat x
mstload "~script~"
loop

 こう書くと鬼のような速度でメモリ使用量が増えていきます,十分注意してください
 なお,内部実装的にVMのコード用バッファstd::vectorなので,後々再確保になる度にデータコピーなどのペナルティが増していきます

 基本的な想定される使い方としては,一回スクリプト食わせてから,その中のサブルーチンやら関数やらを外から呼ぶようにする,という事になりますネ


.hpiで作っている理由について

 実は.hpi形式(HSPプラグイン形式)のプログラムって,少し頑張るだけでランタイム側に移植できる(らしい)んですよね
 と,いうことは,hspdishなどの方にも…?
 個人でやる分には楽しいかもしれない,私スマホとか持ってないけど

 あとはまぁ,バインドとかHSPの中身にフックしてるので,この条件はほぼ必須だったりもしましたが

実際にランタイム化してDLL化した場合

 ランタイムをexeじゃなくてDLLで吐けば好きなコードが外部から実行できると思ってやってみた図
mist_hsp_dllrt_cpp.png
 これ,具体的にどこにメリットがあるかって,別にC/C++じゃなくてもネイティブ呼び出せる言語なら可能なので,例えばC#とかで呼ぶとこうなる
mist_hsp_dllrt_cs.png
 画像として載せていませんが,その他RubyなどのDLLが使えるLLから利用できるのを確認しています.

HSP側の命令呼び出しの実装

そのうち時間あったら書く,でもどう実装したかうろ覚え

例えば今後やるとしたら

 ”モジュール変数はやらないです”


がべーじこれくしょん

引数のスコープについて

HSPではコマンド・関数定義が「#deffunc」や「#defcfunc」で始まりますが,終わりのキーワードは存在しません
これは「#deffunc」等の実装の問題だと思うのですが,そうすると「#deffunc」等で定義したコマンド・関数の引数の生存範囲が特定できません
そのため,本家では同じモジュール空間内に引数と同じ名前を持つ変数が存在することを許されていません
mistでは本家と違い引数にスコープが存在し,その範囲は原則的には「#deffuncが定義された後,次の#deffuncまたは#defcfunc,あるいはモジュールの区切り(#module,#global)に到達するまで」となっています

ところえ,mistでは「スクリプトを動的に追加する」ことが可能です
本当はスクリプトを動的に追加する際も,前の意味解析の状態を引き継ごうかと思ったのですが,それだとあまりに収集がつかなくなる自体になりうると思ったため,引数のスコープについては先ほどの原則の他に「同じ翻訳ステップ中に存在すること」が条件として追加されます
関連記事
スポンサーサイト



コメント

hgimg3とmistの併用

こんにちは。
こちらのmistが非常に便利なのでゲーム製作等に使わせてもらっています。
今回、hgimg3とmistを併用しようとするとエラーが起こることに気が付きました。
mistの仕組み的には仕方が無いのかとも思うのですが、もし修正できるようでしたらお願いします。
ランタイムDLLの方は問題無いのですが、hpiの方が機能が多いのでこちらを使いたいと思っています。
何とかしてhgimg3との併用はできないでしょうか。
一応最小限のコードを:
#include "hgimg3.as"
#include "mist.hsp"
mstinit ; Error!

  • 2015/04/02(木) 16:36:04 |
  • URL |
  • cats #cf9WELWw
  • [ 編集 ]

Re: hgimg3とmistの併用

こんにちわー。

> 今回、hgimg3とmistを併用しようとするとエラーが起こることに気が付きました。
HSP3掲示板の方で回答があったりしますが、hgimg3と他のプラグインを同時に使用する際はデフォルトの状態だと競合します。
http://hsp.tv/play/pforum.php?mode=pastwch&num=4907
http://hsp.tv/play/pforum.php?mode=pastwch&num=20167

リンク先にも書いてありますが一応、最も簡単にはhgimg3.asの「#regcmd 18」を「#regcmd 19」などに変更するとな解決できる…ハズです。
実は諸事情で手元の環境で確認できてないので申し訳ないですが、たぶん上記の症状だと思いますので確認していただければ幸いですー。

  • 2015/04/04(土) 10:17:28 |
  • URL |
  • えくー #-
  • [ 編集 ]

Re: hgimg3とmistの併用

上記hgimg3とmistが併用できない問題について検証してきたので追記です。
結果から言うと上に書かせていただいた対処方法は万能じゃありませんでした、ごめんなさい;;
使っているhspのバージョンが3.5かそれ以前かで対処が異なります。

3.5以前の場合は上記対処で問題ありません。
 (ただ、3.5以前の場合ですとhgimg3側の命令がうまく呼び出せないエラーになりますので、catsさんの場合は下記対処かな、と思います)

hsp3.5(beta)では、正式リリースの3.4からコンパイル時のプラグイン登録処理が変更されており、内部のプラグインのカウンタをいい感じに調整してやらないとダメなようです。
解決策としては、hgimg3.asの修正は必要なく、「mist.hsp」内の「#regcmd "misthsp_init", ~~」の行をコピーしてすぐ下に貼り付けてください。(つまり、内容が全く同じ「#regcmd」が二行になります)

手元のhsp3.4とhsp3.5beta1ではmistの命令実行とhgimg3のテストコードが同一スクリプトで走らせることができるのを確認しています。

  • 2015/04/04(土) 23:17:56 |
  • URL |
  • えくー #-
  • [ 編集 ]

Re: Re: hgimg3とmistの併用

ご回答ありがとうございます。
HSP3.5β1を使用していたので、mist.hsp側を修正することで動作することが可能になりました。
わざわざ検証していただき、本当にありがとうございました。
今後も使用させていただきます。

  • 2015/04/07(火) 14:38:51 |
  • URL |
  • cats #5hX13xjc
  • [ 編集 ]

バイトコードでの入出力

はじめまして、mistをゲームで使用しようとしている者です。
mistでは文字列を一度コンパイルしてバイトコードに変換しているとのことですが、そのバイトコードから直接実行することはできないのでしょうか?
つまり、mstloadのバイトコード版のようなものが欲しいのです。
もし可能なら実装してくださると幸いです。

  • 2015/10/02(金) 00:53:31 |
  • URL |
  • F #-
  • [ 編集 ]

Re: バイトコードでの入出力

> Fさん
はじめましてー、ご意見ありがとうございます。
恐らく、Luaなど他の言語で見られるような、スクリプトを予めバイトコード(バイナリ)に変換しておける機能を指しているのだと思いますが…。
結論から言いますと、機能として実装する気は結構ありますが、コードを見てみたら思ったより依存が多く、実装にはちょっと時間がかかるかもしれません。
(具体的には、実行速度を稼ぐため関数などのシンボルは全て定数化された参照を持っているのですが、バイトコードを読み込む際はバイトコード内のそれらの値を全て読み込み時に統合し書き換えるところが一番実装ネックになっています…。)

また、スクリプトを予めバイトコード化することによるメリット・デメリットは簡単には次のようになると思いますが、デメリットを見ていくとあんまりメリットが美味しくなさそうというのも少々あります。
<<<メリット
 ・スクリプトをパースする時間を削減できる
 ・アプリに埋め込むスクリプトのサイズを小さくできる(バイナリ)
 ・元のスクリプトを隠蔽できる
<<<デメリット
 ・バイトコードの仕様変更や、スクリプトレベルの最適化手法更新に追従できなくなる
 ・「#define」などのプリプロセッサが変更できなくなる
 ・例えバイナリ化しても文字列や変数名などはデータとして残る

以上です。
ただ、何だかんだでなんとか次の更新の時ぐらいまでには入れたいとは思っています…。

  • 2015/10/04(日) 17:47:45 |
  • URL |
  • えくー #-
  • [ 編集 ]

お返事ありがとうございます
HSP由来のデメリットが存在するのは承知しています
実装する気はあるとのことですので、気長にお待ちしております

  • 2015/10/04(日) 18:16:58 |
  • URL |
  • F #-
  • [ 編集 ]

Re: タイトルなし

> Fさん
(見てらっしゃるかわかりませんが。。。)2015/10/11の更新(ver.0x26030)でスクリプトのバイナリ化をサポートしました。
注意点としては私の前のコメントの通りです、特にバージョン間の互換性はないので取り扱いには注意してください。(ただし、ランタイムとバイナリのバージョンが合わないとエラーにする処理が仕込んであるので、大丈夫だとは思いますが…)

また、今回の変更はmistの内部的にはかなり大規模なモノのため動作が不安定になっている可能性はありますので、私の方でしているテストではエラーはありませんでしたが、もし何か致命的な不具合等ありましたらお知らせ頂けるとありがたいです。
以上です。

  • 2015/10/11(日) 23:11:49 |
  • URL |
  • えくー #-
  • [ 編集 ]

おお 対応してくださってありがとうございます!
早速試してみます!

  • 2015/10/12(月) 12:19:22 |
  • URL |
  • F #-
  • [ 編集 ]

外部関数のarray引数が使えない?

どうも、こちらのmistプラグインを使わせていただいています。
さて、定義した関数をmistから呼び出しているのですが、
引数arrayに指定した変数が呼び出すたびに初期化されるみたいです。
これは仕様でしょうか。
mstloadでモジュールにしたり、mstbindで変数を共有したりしましたが、どれも上手くいきません。

例:

#include "mist.hsp"
#module
#deffunc func_pass array a
dim a, 7, 5
a(0, 0) = 123
return
#deffunc func_error array a
mes strf("%d, %d", length(a), length2(a))
;mes a(0, 0) ; エラー
return
#global
mstcaptfunc
mstopenhsplib
// HSPから利用
func_pass a : func_error a
// mistから利用
mstload "func_pass b : func_error b"
stop

mistバージョン:最新(alpha)
hspバージョン:3.4

解決法などありましたら、ご返信をよろしくお願いいたします。

  • 2016/03/04(金) 00:17:26 |
  • URL |
  • cats #jSFtdjEk
  • [ 編集 ]

Re: 外部関数のarray引数が使えない?

> catsさん
どうもー、ご指摘ありがとうございますー。

> 引数arrayに指定した変数が呼び出すたびに初期化されるみたいです。
> これは仕様でしょうか。
こちらですが、正確に言うと「HSP側で行った変更がmist側に伝わらない」挙動ですね。

試しに最終行を
「mstload "dim b, 32, 8 : func_pass b : func_error b" 」
とすると、lengthが32、length2が8ととれます。

さて、この挙動についてですが、私が引数の型「array」を誤解してた事に起因します。
私としては上記挙動は実は意図したもので、「arrayで渡したものは参照はされるが変更はされない」
ものだと勝手に思っていたのですが、HSP本家の挙動だと書き換え可能なんですね…。

HSP本家がそういう挙動をするならmistとしても対応したい、
かつそれらのケースでは実質メモリリークしてるみたいなものだったので、
本日03/05リリースのver,alpha(0x26040)にて本家と同じ挙動になるよう対処を入れました、

以上、お手数ですがご確認頂ければ~。

  • 2016/03/05(土) 18:17:52 |
  • URL |
  • えくー #-
  • [ 編集 ]

Re: Re: 外部関数のarray引数が使えない?

早速のご対応ありがとうございます。
最新バージョンを確認したところ、確かに修正されていました。
本当にありがとうございます!

  • 2016/03/05(土) 23:23:30 |
  • URL |
  • cats #iV3xkJ4U
  • [ 編集 ]

buttonのラベルについて

どうも、こんにちは。
まず、本題の前にバグと思われる報告を。
どのバージョンからかは分かりませんが、sdim命令が使えなくなっているような気がします。
#include "mist.hsp"
mstopenhsplib
mstcompile "sdim a, 256"
mes mstpoperr()
そこまで重大な問題でもないのですが、もし仕様でなければ、また暇な時にでも修正をお願いいたします。

次に、buttonのラベルについて、HSP側のラベルを指定するとのことですが、
何とかmist内のラベルを指定することはできないでしょうか。
(更に欲を言えばbutton等でgosubも指定できたら嬉しいです。)
変更が難しそうなら現状のままの仕様でも問題ありません。

毎度お世話になっておりますが、以上よろしくお願いします。

  • 2016/03/10(木) 10:05:54 |
  • URL |
  • cats #iV3xkJ4U
  • [ 編集 ]

Re: buttonのラベルについて

> catsさん
どうもー、ちょっと長文ですが返信となります。

> どのバージョンからかは分かりませんが、sdim命令が使えなくなっているような気がします。
> そこまで重大な問題でもないのですが、もし仕様でなければ、また暇な時にでも修正をお願いいたします。
度々ご指摘ありがとうございます。
結構自分でもライブラリ叩いているつもりなんですが、それでもやはりバグは残ってしまうもので、…
こういう形でご報告頂けると有り難いです!

さて、ご指摘頂いたsdimについてですが、「sdim a, 256, 1」のように、最初の次元数まで書くと通るかと思います。
確認したらsdimはコマンドじゃなくて構文の一部で、かつ2引数以上を受け取るという定義になっていました。

この辺内部実装が腐っているという申し訳ない事情と、それ故に実装をクリーンにしてかつプリプロセッサの
真面目な対応をしたいという事情があり、その時の大規模変更に混ぜて修正したいと思います。
それまではお手数ですが上記のような書き方で対応して頂けると有り難いです。


> 次に、buttonのラベルについて、HSP側のラベルを指定するとのことですが、
> 何とかmist内のラベルを指定することはできないでしょうか。
> (更に欲を言えばbutton等でgosubも指定できたら嬉しいです。)
こちらに関しては私も一度考えたことがありますが、現在のステータスとしては「あったら嬉しいが優先度は低い」です。

実現したいこととしては「mistのラベルをHSPからも使えるようにしたい」だと思いますが、
HSP側でmistに処理を投げるだけの中継役のサブルーチンを書き、それを指定すれば事足りるケースが多いと感じたからです。
(※こんな感じのコードです
button gosub "doSomething", *doSomething
stop
*doSomething
mstcall "doSomething"// mist側の好きな処理を呼ぶ
return


一方、いちいちHSP側に転送サブルーチンを定義しないといけないため、例えばUIの一部を外部スクリプトだけで完結して
処理させるといったことは確かに出来ないのですが…。

ただ、あくまで現在対応してないのは(勿論実装の都合もあるんですが)「優先度が低い」=「強いモチベーションがない」
というだけなので、「こういうことが出来て有用だ」とか「こういう使い方に発展させられる、展望ある!」とか、
強力な理由がある場合はご提案頂けると嬉しいです。
(と言いつつも、申し訳ないことに最終的に入れることを確約出来るわけではないんですけどね…^^;;;;;)

以上です、よろしくおねがいします~。

  • 2016/03/11(金) 19:41:18 |
  • URL |
  • えくー #-
  • [ 編集 ]

Re: Re: buttonのラベルについて

ご返信大変ありがとうございます。

>上記のような書き方で対応して頂けると有り難いです。
なるほど、了解しました。

>強力な理由がある場合はご提案頂けると嬉しいです。
今HSPスクリプトエディタを拡張させる機能を作っています。
言ってみればアドオンみたいな感じなのですが、そのコードにmistを使わせていただいています。(完成するかは分かりませんが。)
自分だけで作る分には、おっしゃったようにラベルをHSP側で合わせればよいのですが、ユーザーが勝手にコードを編集することを想定しているので、buttonが使えたら便利だなぁと思いました。
とはいえ標準の描画でも実装できる話なので、またモチベーションが上がった時にでも実装していただけたらと思います。

次のアップデートを楽しみに、気長に待たせていただきます。m(_ _)m

  • 2016/03/12(土) 16:05:11 |
  • URL |
  • cats #iV3xkJ4U
  • [ 編集 ]

文字化けと不具合について

はじめまして、製作中のゲームにmistを使わせていただいてます。
その際に2つ問題がありましたので報告します。
ひとつはHSPの標準命令を使えるようにして、mesで文字列を表示したとき文字化けするときがあります。

もうひとつは、hgimg3と併用したときmstcaptfuncで指定したユーザー定義命令をmistから実行しようとするとHSPのエラーが発生したり強制終了したりします。
mstcaptfunc以外は問題なく動いています。

問題を再現したスクリプトをアップロードしましたので、お手数かけますが確認をお願いします。
ttp://beefort.web.fc2.com/report_mist.zip

HSPは3.4で、mistはver.alpha(0x26042)を使用しています。

  • 2016/04/23(土) 01:15:34 |
  • URL |
  • 黒猫 #.pF2gSjU
  • [ 編集 ]

Re: 文字化けと不具合について

> 黒猫さん
はじめましてー(?)、ご報告ありがとうございます、再現スクリプトまで頂けて大変有り難いです…!

> 文字化け
手元でも確認しました、mesとは関係なく単純に文字列リテラルで、ASCIIから始まってマルチバイトに続くものは全部怪しかったようです、こちらは0x27000にて対処しました。

> hgimg3と併用した際のユーザー定義命令エラー
こちらも同じく手元で確認しました。
詳しい再現条件は掴めてませんが、ランタイム+プラグインの時にHSP本家の挙動が安定していないのを応急処置的な対応で凌いでいた(regcmd19や2回regcmdするなど)ところで綻びがでたのかなと思ってます。
上記と同じくこちらも0x27000にて対処しました。

一つ注意点として、いずれも0x27000で修正されていますが、0x27000はレクサとパーサが新しくなった版をデフォルトにしています。(構文解析周りの処理が大幅に変わっています)
手元で見ているスクリプトでは同じ動作になるよう調整しましたが、色んなスクリプトで旧版と全く同じ動作になる確証は(申し訳ないことに)ないです。
そのため構文解析だけ旧版のままのmist_s.hpiも同梱していますので、新版が不安なようでしたらそちらをお使いください。
以上ですー。

  • 2016/04/24(日) 02:02:30 |
  • URL |
  • えくー #-
  • [ 編集 ]

Re: Re: 文字化けと不具合について

ご返信ありがとうございます。

>0x27000にて対処しました
どちらの問題も解消していただきとても助かりました。
さらにゲームの幅が広がりそうです。

レクサとパーサが新しくなった版を使ってみましたが問題なく動いているようです。
また何かあったときもよろしくお願いします。

  • 2016/04/24(日) 22:27:55 |
  • URL |
  • 黒猫 #.pF2gSjU
  • [ 編集 ]

リンク切れ

DropBoxのリンクが切れてますよ!

Re: リンク切れ

> どなたか
> DropBoxのリンクが切れてますよ!
確認したところ確かにDropBoxのリンクは一時的に止まっっているようです、ご指摘ありがとうございます。
一旦同じファイルをGoogleDriveにもあげてリンク追加しておきました。
DropBoxの方はそのうち直ると思いますが、急ぎでしたらGoogleDriveの方から落としてください。

  • 2016/10/26(水) 22:08:05 |
  • URL |
  • えくー #-
  • [ 編集 ]

switchについて

初めまして、mistすごいですね。
自分のアプリに、ユーザースクリプトを導入しようと思い試させて頂いていますが、条件分岐のswitchって機能してますでしょうか。
チュートリアルの「【3】条件分岐・繰り返し.hsp」を実行した限りでは

// 条件分岐:switch
mstload {"
a = 10
switch a
case 2
v = 2
swbreak
case 10
v = 10
swbreak
default
v = 1
swbreak
swend
"}
mes "switch-swend : "+mstget("v")
------------------------------------------------
switch-swend : 20

と表示され、この手前で入力された 20 がそのまま出力されています。
ご確認よろしくお願い致します。

使用しているバージョンは mstVersionStr()=270.5.0 です。

Re: switchについて

> MIZUSHIKIさん
初めまして、ご指摘ありがとうございます!
コード確認してきたところ確かに条件分岐のswitchは正しく動作していなかったようなので、0x27060で修正させていただきました。

以下詳細です。
ちょうど0x27050でIL(内部中間表現)の仕様を変えたのですが、if文やrepeat-loop等同じように条件によって処理がジャンプする箇所は直していて、switchは変更に追従しておらずエンバグとなってしまっていたようです…。
この辺は本当は単体テストを書くべきなのですが、生憎そういうのを書いていないためコア部分にも関わらずバグっていたようで…大変失礼しました。
また、ほぼ3年振りの久しぶりすぎる更新も理由の一つなんですが、実は開発環境が当時のものが残っておらず、開発環境周り由来の修正も入ってしまっています(内部プラットフォームツールセットの更新とか)。
ついでにOSSも整理しています(XbyakとFSBAllocator)。
これら由来でバグが入る可能性はほぼ無いと思っていますが一応お知らせしておきます。

以上となりますー。

  • 2019/04/09(火) 02:52:55 |
  • URL |
  • えくー #-
  • [ 編集 ]

Re: Re: switchについて

早々に対応して頂き、ありがとうございます。
switchがバッチリ使えるようになってますね!

早速ですが、私の作っているソフト「Touch Panel Gestures」にも反映させて頂きました。
http ://suwa.pupu.jp/TouchPanelGestures.html

タッチパネルジェスチャアプリというニッチなソフトですが、もし環境・興味がありましたらお試しくださいませ。

最後に繰り返しになりますが、迅速なご対応、誠にありがとうございました。

Re: Re: Re: switchについて

> MIZUSHIKIさん
無事直っていたということで安心しました、ご連絡ありがとうございます。
mistは公開してから年数的にはかなり経ってますが、他の方が公開されているソフトで使われているのを実は未だ見たことなかったので素直に嬉しいです、ありがとうございます…!

  • 2019/04/10(水) 13:03:51 |
  • URL |
  • えくー #-
  • [ 編集 ]

コメントの投稿


管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://fe0km.blog.fc2.com/tb.php/65-d6695be9
この記事にトラックバックする(FC2ブログユーザー)