VZは論理行および表示行の2種類の行番号モードを持っています。論理行と表示行の違いは次の通りです。
電算機に縁のない人の100%が連想する1行は表示行の方で,1以上の表示行が論理行を構成します。
行番号モードは,オプションDnで切り替えます。Dn-
(Dn=0
)が論理行モード,Dn+
(Dn=1
)が表示行モードです。ステータスラインの行番号は,このモードによって表示が変わるほか,判別しやすいように行番号とカーソル桁位置を区切る記号が「:」か「|」かに切り替えられます。
マクロで使用するのは論理行番号(ln)の方にしてください。表示行番号(ld)は環境によって変化するので,表示行番号を使用すると所期の動作が得られない可能性が99%くらいあると思われます。
&m("%5u",ln) ;論理行番号を5桁で表示
書式指定はなるべく%u
を使いましょう。%d
では,大きなファイルで32767行を超えると負の値となってしまいます。なお,lnを比較する場合は符号つきになりますので注意してください。カーソル位置を比較するときは,ファイルの絶対番地を使った方が安全です。小さな一時作業ファイルの中でlnを比較するのは問題ありません。
他に,論理行が複数の表示行となる場合に,論理行頭から数えて何行目の表示行にカーソルが位置しているかを判別する変数lh(論理行内部におけるY座標)もあります。
現カーソルの桁位置は変数lxで確認できます。例えば,次のサンプルのように判定を行います。
#> ;行末へ
(lx<40)?{ ;40桁より短ければ
&#V(40) ;空白文字を充填し
#> ;行末へ
}?{ ;長い行は
#21 ;ひとつタブを入れる
}
";" ;コメント記号を付与
lxは表示行の先頭を0とした桁位置です。それに対して,ステータスラインに表示されている桁位置は表示行頭を1としています。このことが原因で誤解を生じ話がすれ違う(実話)こともありますので,lxは桁位置と言うよりもカーソルのx座標と表現した方がよい場合もあります。したがって対話型で桁位置を確認するマクロを作る時は,誤解を招かないように値を調整(±1)する必要があるかもしれません。
:G &g("指定カラム位置?") ;数値の入力を促す
(r<1)?. ;数値以外なら終了する
(r=>we)?>G ;範囲外数値なら再入力
c=r-1, ;値を調整する
カーソルのカラム位置を示す変数lxは,論理行頭も表示行頭も共に0になりますので,次の例では誤動作する可能性があります。
&x(0) #s ;△ 前の行の行末に移動するつもり
つまり,(lx==0)
という評価では,論理行頭にカーソルがあるかどうかの判定としては確実ではありません。このような場合に利用できるのが変数lh(論理行内でのY座標)です。桁折りされた複数の表示行からなる論理行の1行目ではlh==0
で,2行目以降は表示行数に応じた値になります。つまり,次の式で論理行頭を判定します。
(lx==0 && lh==0)?. ;論理行頭なら終了
(lx || lh)??. ;同上(等価な表現)
(lx+lh)??. ;同上(ちょっと工夫)
カーソル位置の文字コードは変数cdに格納されています。cdは2バイトの大きさがあり,ASCIIの1バイトコードは上位バイトにゼロ(ヌル),下位バイトに文字コードが格納されます。漢字など2バイトコードは2バイトの数値と同様に格納されます。つまり文字コードを取得するには,2バイトのユーザー変数に代入するだけで取得できます。
a=cd, &m("%c",a)
cdは「数値」として格納されているため,値を増減するだけで文字コードをずらすことができます。
a=cd, ;文字「1」の上にカーソルを置いて実行すると
a++, ;「cd:1 / a:2」と表示されます
&m("cd:%c / a:%c",cd,a)
なお,バッファに文字列を格納した場合は,1文字単位の数値としては扱えないので注意が必要です。文字列から2バイトを取り出して値を増減しても文字コードずらしはできず,離れたコードに化けてしまいます。
蛇足ぎみですが,文字列をメモリに格納した場合,そのままの順序でメモリ上に置かれます。敢えて「そのまま」と表現したのは理由があります。インテル8086系CPUでは数値がリトルエンディアン(little endian:幼い印度人ではありません。念の為)で格納されますが,文字列の場合は上位・下位を逆転させずに格納されます。文字列の中の文字コードを単純な数値として扱うことはできませんので,文字コードを操作しようとする場合は注意が必要です。
例えば,「あ」の文字コードは82A0hですが,文字列の一部としてメモリ上にある場合,下位に82hが上位にA0hが格納されます。82A0hを数値としてメモリ上に転送した場合は,下位にA0hが上位に82hが格納されます。82A0hという数値に1を加算すれば期待通り82A1hになりますが,82A0hという文字をずらすつもりで加算を行っても83A0hになってしまいます。
カーソル位置の文字タイプは変数ctに格納されています。文字タイプの参照は,内部的にも語の移動を行うために欠かせません。文字タイプには次の7種が設定されています。
ctの値 | その値を持つ文字のグループ |
---|---|
0 | EOF(End of File):実際には特定の文字があるわけではない |
1 | 改行類(\n :0D0Ah,\l :0Ah,\r :0Dh) |
2 | 改行類以外の制御コード(00h〜1Fh),空白類(' ':20h,' ':8140h) |
3 | 半角記号と半角カナ文字(数字,英字,'$':24h,'_':5Fhを除く21h〜DFh) 全角記号("ヽヾゝゞ〃仝々〆〇ー"(8152h〜815Bh)を除く) |
4 | ひらがな |
5 | 上記以外の全角文字 |
6 | 上記以外の半角文字 |
VZが1語として扱うのは,同じ文字タイプの文字が連続する文字列です。1語削除の場合は,この規則に従って文字列のカットが実行されます。1語移動の場合は,同じタイプの文字で構成される文字列を1語とみなしつつ,連接する文字列が前の文字列より下位のタイプであればさらにカーソルを進めて,文字タイプが上位の文字が出現するところで停止します。基本的に文字タイプが下位の改行や空白類は常に語の区切りとなります。
言葉ではわかりにくいので,具体的な例を挙げましょう。
aiuエオ柿食えば,… kaneガ鳴る。
この文で語の先頭という扱いになるのは英字だけです。行頭で #f
(^F)を実行して最初にカーソルが止まるのは「aiu」の先頭で,次にカーソルが止まるのは「kane」の先頭です。【aiu(6)エオ柿食(5)えば(4),…(3) (2)】のように,文字タイプが上位から下位に向かって連接しているため,このような現象が起こります。通常はこんな文字列はないでしょうから,比較的期待する位置で止まることになるでしょう。
一方,記号と英数字が混在するVZマクロコードの場合,この規則では止って欲しくない位置で止ってしまうという問題点があります。例えば,空白の次に #F "hoge" #m
というコードがあったとします。まず'#'(ct==3)で停止し,その位置からコマンド#f
を続けて実行すると '5'→'"'→'#'→'m' という具合に頻繁に止ります。なにやら「褌の川流れ」という風情です。VZはVZマクロを記述するためにあるエディタではないということになりますが,こういう問題はマクロで解決するのが一番で,空白文字の直後だけを停止位置とする1語移動マクロを書き,マクロの編集中はコマンド#f
/#a
の代わりに使用するという運用が考えられます。(あ,こういうのは蛇足ですね。)
なお,ctの他にckという変数が用意されています。ckは文字コードが全角であれば真,半角であれば偽の値を持つフラグ的変数です。改行の\n
(0D0Ah)は2バイトですが,半角文字と扱われます。
本題に入る前に,標準組込マクロを参照してみてください。
16 ^K0 "マーク(1-4)" ?. i=r, &#M(r) (r)?. &m("マーク #%d",i)
ここで使用されているコマンド関数&#M()
は,編集ファイルにおけるカーソル位置の番地(注)を標準で用意されている5個の大きめな引出に納める機能を持っています。引数は引出の数に応じた0〜4が使えますが,0番の引出はVZ自身が頻繁に書き換えるので中期記憶には向いていません。ユーザー側から自由に使えるのは1番〜4番の引出です。
マクロを開始したときに &#M(1)
と引出を明示して記憶させ,マクロ終了時に必要があれば &#U(1)
として同じ引出から値を取り出すことにより,開始位置に戻ることができます。場合によっては,ユーザーが意識的に記憶させた位置情報を破壊することになりますが,それを承知の上なら特に問題はありません。
一方,配布可能な状態にある公開マクロにおいては,1番〜4番の引出を勝手に開けて中身を弄るのは行儀のよくないこととされています。この場合は,別の場所に引出を用意して記憶するのがよいでしょう。自家薬篭ってやつですね。
この別の引出を準備する前に,そもそも0番〜4番の引出がどこにあるのかを説明する必要がありますので,少し冗長な説明にも我慢してください。
さて,VZはオープンできるファイルの数には限りがあります。tcというオプションでオープン可能なファイル数の上限を決めてやるわけですが,VZはファイルをオープンしていなくてもtcで指定された数の薬篭を抱え込みます。これをテキスト構造体と呼びます。テキスト構造体は,ひとつで228もの引出を持つ大きさがあり,編集テキストに関する情報がつめ込まれています。実際には2個分の大きさの引出が多いので引出の数自体は86個ほどです。フルパスを記録している特別大きな引出の他に,4個分の大きさの引出がいくつかあり,それらがカーソル位置の番地を記憶するための引出です。
その引出のうち,最初のマーク0番の引出はカーソルをジャンプさせるたびに中身が更新されますので,一時的な作業に使うにはもってこいです。ただし中身は頻繁に入れ替わってしまうので,本当に一時的にしか使えません。要するに,この引出に放り込まれた数値を取り出して他の引出に保管しておき,必要があれば0番の引出に戻してやってVZに使ってもらえばよいのです。
それでは,引出を用意しましょう。実行されるマクロの内部に4バイトの引出を入れる空間を確保してやるだけです。なお別法としてはユーザ変数を2個用意して2バイトずつ保存するというお手軽な方法もありますが,ユーザ変数はいつどこから変更されるかわからない不安定なものですので,ロング演算関数の便利な使い方を覚えるという意味でも,自前で確保したバッファに保存する方法を採用します。
00: (3+$)."$[4]"
この例のように,ローカルバッファを確保します。このバッファはローカルマクロとして呼び出すことによって初めてバッファとして機能します。したがって,使用する前には次のように呼出を行います,
&> p=r, ;pはバッファの先頭オフセットアドレスを指す
次に,現在のカーソル位置をコマンド #M
でマークして,番地を取得させます。現テキストのテキスト構造体は変数waが指す番地を先頭とする228バイトの領域で,マーク0番の引出はwa+126
を先頭とする4バイトです。現カーソル位置の番地を単純に表示するマクロは次のように書けます。
= Test = カーソル位置のロングポインタを十進・十六進数で表示
* m
1 ^\ "" ?.
#M ;wa..63,wa..64 mark
&m("%lu / %lxh",wa..63,wa..64,wa..63,wa..64)
*
wa+126
に取得した番地を,中身がすりかわらないうちに用意したバッファに複写します。p..0=wa..63, p..1=wa..64,
としてもかまいませんが,便利なロング演算関数があって一発でできます。
&Le(p,wa+126) ;ロング整数の代入
&Le()
は便利な関数ですので,[L]ongの[e]qualと覚えておきましょう。。4バイトの転送にも使用可能ですので,4の倍数の大きさの構造体のようなバッファのコピーであれば,1バイトずつ転送するよりも高速に処理できます。
まとめてみましょう。
&00 p=r, ;バッファを確保
#M ;マークする
&Le(p,wa+126) ;即座に保存する
00: (3+$)."$[4]"
このように保存することにより(マクロ自体がマクロバッファから削除されない限りは),いつでも呼び出して利用できます。
おまけで,保存した値の利用手順も簡単に書いておきましょう。
&00 ;rに番地が返される
&Le(wa+126,r) ;rが変化しないうちならユーザ変数を使わなくてもよい
#U ;すりかえられた戻り先が保存しておいた番地になる
00: (3+$)."$[4]"
カーソル移動コマンド #e
や #x
でスクロールすると,スクリーンの上端やメッセージラインの1行上にカーソルが固定されてしまったり,行きつ戻りつするとカーソルのY位置が泳いでしまって,マクロ終了時の目線が定まらないという場合があります。このような場合は,スクロールコマンドの #w
や #z
を使うとカーソルのY座標を変えずにカーソルが移動できます。
これを応用して,カーソル行がカーソルに付いてくるような効果を持たせることもできます。実用と言うよりも,視覚効果に近くなります。
= test
* m
1 ^^ "" ?.
#_ #> (lx+lh)??#s ;[EOF]行なら戻る
(wy/2){ #e #z } ;画面の中程へ
*
カーソルを動かしてよいなら簡単です。カーソル移動コマンドが移動に失敗すると-1を返すことを応用すればよいからです。
#s
(r)?&m("ファイルの先頭です")
カーソル不動で判定するのは意外に難しいのですが,どのくらい厳密にやるかという見切りの問題で,大抵は行番号(ln)とカーソル位置のオフセット(cp)をどちらか,あるいは組み合わせて使えば大丈夫でしょう。
= test
* m
1 ^\ "" ?.
#] m="ファイルの先頭ではありません",
(ln+lx+lh-1)?{ &m(m) . }
(cp-2)?{ &m(m) . }
&m("%16sす",m)
*
カーソルを動かしてもよい場合は,#d
([-->])の返り値で判定できます。
#d (r==-1)?.
上から下に向かってのテキスト処理では,#x
([DOWN])によっても判定できるでしょう。この判定を怠ると,必ず無限ループに陥ることになります。
カーソル不動で判定するのは非常に難しくなります。一般にファイル末尾の判定は,カーソル位置の文字タイプを示す変数ctが0であるか否かにより確実に行えると思われがちですが,ctが必ずしも正しい値を示すとは限らないということを知っておくべきです。
:e (ct==0)?. ;ctが0なら終了 (注意!これは確実ではない)
#n ;;処理;;
#> #d #> >e ;一定の条件下で無限ループになる可能性も
次のマクロを実行してみてください。無限ループの原因となるファイルが作られます。
= t
* m
1 ^^ "" ?.
#R "nul" #m (s)?'y'
(5){ #m }
&m("現在のctは[%d],cdは[%xh]です。何かキーを押してください。",ct,cd)
mx+, &p mx-,
"$(d)"
&m("現在のctは[%d],cdは[%xh]です。ファイルを閉じてください。",ct,cd)
#] .
*
[EOF]というのは,実際に[EOF]を示す制御文字1Ahが存在しているわけではなく,ファイルサイズによって仮想出力されているに過ぎません。オプションEz([EOF]コードの付加)が存在する理由は,CP/MというMS-DOS以前のOSとの互換性のためであって,VZが特に必要としているわけではないのです。ファイラーでサイズ0の新しいファイルを作成して開いてみてください。ファイルサイズは0なのに,あたかも[EOF]があるかのように終端マークがありますね。
上のマクロで作成されたファイルの終端には\r
(0Dh)があります。DOS以外の環境で作成されたテキストで使われる改行コードです。このようなケースもあるので,[EOF]の判定にはctを使わず,カーソル移動の返り値で判定するのがよいでしょう。
VZは標準(オプションEnが無効)で行末以降に擬似カーソルを表示し,水平方向の目線のぶれを生じさせないようになっています。行末以降に擬似カーソルが表示されている場合でも内部的には行末にカーソルがあるのですが,現在桁を示す変数lxは行末以降のカーソルの浮いた場所の値になります。この状態でlxを参照する関数,例えば&#V(x)
を実行しても,所期の動作が得られないことがあります。
= t
* m
1 ^^ "" ?. (mr-$80)??.
En[-,
#< #n #n
&#V(40) #> ";" ;40桁位置にセミコロンを出力
#x ;その位置から下へ移動するとカーソルは浮いている
&#V(40) #> ";" ;指定桁位置まで空白類が出力されない
En],
*
このような場合は,#?
でカーソル位置を正常化することにより,所期の動作を得ることができます。
#x #? ;浮いているカーソルを本来の位置に戻す
オプションEnを有効にすると,擬似カーソルが行末以降に表示されなくなります。
前項のようなマクロの場合,En[+, 〜 En],
で処理を括ると手間が省けるので,高速に実行させる効果を期待できます。
サイレントでなにか処理を行ったあと,カーソル行を起動時のY位置,あるいは任意のY位置で終了したいという場合があります。このようなときに使えるのが変数kyです。kyはkeepされたY座標の意で,値を代入することのできる編集テキスト変数です。このkyに値を代入したのちに&d
で再表示すると,指定した位置にカーソル行を合わせることができます。
1 ^QC "" ?.
&s #_
ky-, &d
この例は基本コマンドの拡張というべきものですが,コマンド#_
が起動時のカーソル位置にかかわらず画面の下端になるのに対し,ky=0
で画面の上端に位置させます。
起動時の行位置で再表示したい場合は,次のように保存した行位置を書き戻します。
1 ^QC "" ?.
&s ly[,
#_ ky], &d
カーソル位置の数値を取得する関数&n
が用意されています。ただし,&n
はカーソル位置以降の数値を取得するため,通常は数値の先頭にカーソルを移動しなければなりません。また,取得できる数値は16ビットで扱える範囲で,負の数も取得できますが-1(FFFFh)を取得することはできません。数値以外の文字上で実行したときの返り値が-1だからです。
マクロの中で使用する場合は,いかに正確に数値の先頭位置へカーソルを合わせるかが鍵となります。テキストの種類によってふさわしいコードは千差万別になりますが,意外な手抜きをできることもあるでしょう。
1 ^^ "" ?.
&f(cd,"0123456789")
(r<0)?{ &m("数字の上で起動しましょう.") . }
(-1){ #s (r)?{} &f(cd,"0123456789") (r+1)??{} }
(cd!='-')?{ m-, #d }?{ m+, } ;負の記号なら不動
&n n=r,
(m)?{ &m("[ %d ]",n) }?{ &m("[ %u ]",n) }
なお,&n
関数が取得できるのは16ビットに納まる範囲です。これを超える場合は納まる範囲に数値が丸められてしまって所期の動作を得られません。例えば,-33333 は32203なんてへんてこなことになってしまいます。
また,A〜Fの英字を含まない十六進数,八進,二進数値であっても十進数として評価されます。当然ですが…。
カーソル位置の十六進数値を取得する関数&h
が用意されています。ただし,&h
は&n
と同様にカーソル位置以降の数値を取得するため,通常は数値の先頭にカーソルを移動しなければなりません。また,取得できる数値は16ビットで扱える範囲で,負の数は取得できません。
&n
の注意事項と同じですが,取得にあたっては数値の先頭にカーソルを移動しなければなりません。またオーバーフローした場合の値は最下位2バイト分になります。
1 ^^ "" ?.
c="0123456789ABCDEFabcdef",
&f(cd,c)
(r<0)?{ &m("十六進数の上で起動しましょう.") . }
(-1){ #s (r)?{} &f(cd,c) (r+1)??{ #d } }
&h n=r,
&m("[ %04xh ]",n)
VZには二進数を取得する関数は装備されていませんので,すべて自前でなんとかする必要があります。
以下は,以前にniftyのfgaldcにあったマクロ作成会議室において,皆であれこれ楽しみながら作ったものです。ご参考までに。
= bit2dux v0.05 = by H_ear
* mad for VZ!
1 ^^ ""
?. &s
#M ly[, ;カーソル行位置を記憶
(ks&1)?? ;シフト実行ならカーソル位置始点
#a ;数列(等)の先頭へ
i-, j-,
:L &SF("01",cd) ;カーソル位置が 0 or 1 か
(r)??{ ;0 or 1 ではない場合で
(i)?>M ;;計算済なら mes & mark で終了
?>v ;;計算ショジョ(^^;なら先へ進む
} ;↑fgalpk mes 14
j=j<<1+cd-$30, ;桁をくり上げながら加算 H-Oka(__)
i++,
(i>16)?{ i-, >M } ;16桁(64k)以上は overflow
:v #d
(ct<2)??>L ;1行内のみ対象 夢幻る〜ぷ防止 JUN(__)
:M #U ky], ;カーソルを元の位置に戻す
(i)?{
&m(" It's ... %d / %u / %02xh",j,j,j)
}?{
&m(" Overflow") ;二進数でない場合も含む
}
:Z &d #] .
*
なお,製品版のVZには二進数を出力する書式指定はなく,十六進数の確認によく使用される「マクロ文の実行」([ESC]@)も二進数を表示しません。そのため拡張された「マクロ文の実行」(通称「エスカット」「エスカルゴ」)では,激辛マクロ師がそのワザを尽くして二進表示=ビット表示に挑戦しています。高橋版VZでは,書式指定%b
が追加されてビット表示もできるようになりました。
ただ,自分としては16ビットの二進数を読み取ることが困難なので,m.gann師におねだりして対応していただいた4ビット単位表示の特製版を愛用しています。
12 [ESC]@ "" ;Instant Macro by m.gann (4bit*4) (s%3)?. r[, ss-, &g("Statement:",32) (s%3)?#[ (r+1)?{ ;安全終了 &Sc(pa,pw,62) &v(pn.-15=r.1-) r=pa-1, ;pa へコピー :R (2){ &SF(r+1,'"') (r)?,?{} } ?{ r.0-, >R } ;2個目の " をヌルに r][, &e(pa) :p (s>3)?{ &c >p } (pn.-15)?&w(60) ;マクロ文実行、待機 p[36+pr, (i[16){ (i%4)??p--.i=32, p.i--=49-(r&1<<(15-i))!, } ;eggchi &m("%s: %d %u %xh %20s '%c' $"%16s$"",pw,r,r,r,p,r,r) ;結果表示 (ks&1>s&&mr-$80)?{ &s #< &?("%s",pu+bu) #m } i], p], ;結果出力 (ks&5-4)?&d } (r]) ;#if 98 (ks&17-16)
これはFAQなんでしょうか。(^^;
16ビットに納まる数値を表わす文字列であれば,以下のようにして簡単に数値化することができます。
a="31555",
&e(a)
&m("%u",r)
さて,16ビットを超える数値を表わす文字列となると,こう簡単にはいきません。一番泥臭くしかし確実な方法としては,下位の桁から10のべき乗を加算していくか,上位の桁から10倍しつつ下位の桁を足していくという方法をとることです。しかしこれとて口で言うのはたやすいですが,ロング演算の連続でおそろしく面倒なことになりますし,何より大変に時間のかかる処理になります。
こういうのは,達人の作成した機械語部品で片付けてしまいましょう。(^^;
以下のコードは Emmett博士による文字列→数値変換ルーチン部品で,nifty:FGALDC/MES/15/04414(1997/01/27)に書き込まれたマクロの引用です。
00: ; = get longint_str to ax,dx = ; input: ax=segment, si=offset ; output: ax,dx [&m("%ld",ax,dx)] ; ax==dx==-1: 10進数ではない ; ---------------------------------------------------------------- ; ----- longdec ----- &i(3+$)."$ $(56,8E,D8,33,C0,8B,D0,BF,0A,00,50,AC,8A,C8,58,80)$ $(F9,30,7C,05,80,F9,39,7E,02,EB,15,50,8B,C2,F7,E7)$ $(8B,D8,58,F7,E7,03,D3,80,E9,30,B5,00,03,C1,EB,DA)$ $(59,2B,F1,83,FE,01,75,05,B8,FF,FF,8B,D0,C3)"
「絶対番地」の取得はできません。…と言うか,意味がありません。CPUが管理するリアルモードあるいはプロテクトモードいずれにしても,大変大きなメモリ空間であり,またEMSメモリ上に確保された作業エリアに読み込まれている場合なども含めて,アクセスが非常に難しいからです。
ここは一切をVZに任せて,読み込まれたファイルの先頭からの番地ということでお茶を濁してはいかがでしょうか。(^_^;
それにしたってロング整数を扱うわけですから,随分と厄介であることは確かです。でも,一定の手順をふめば簡単ですから慣用句として覚えてしまいましょう。
カーソル位置のファイル先頭からの相対番地は,マークを利用すると簡単に取得できます。次の部品はマークしたカーソル位置の番地を,十進数/十六進数で示すものです。ワード値が組でダブルワードになっています。一対のワード値を組にしてダブルワードとし,ロング整数で表示・出力する書式指定子が%ld,%lu,%lx
です。
#M ;wa..63,wa..64 mark0
&m("%lu / %lxh",wa..63,wa..64,wa..63,wa..64)
マーク0番というのは,大きくジャンプした後で戻る位置として記憶されているものです。通常の使い方としては,大きく離れた位置の文字列を検索してヒットした行を複写し元の位置に戻って貼り付ける,…というような作業を行っているときに ^QP で元の位置に戻れるわけです(活用してますか? ^QPを「旧プレイス」とか「旧ポイント」なんて語呂あわせで覚えましょう。)が,このときに記録されているカーソル位置の番地です。
ダブルワードは4バイトあれば記憶できますので,通常のユーザ変数2個あれば事足りるわけですが,ユーザ変数はいつどこで書き換えられてしまうかわからないので保存の保証がありません。もっとも突き詰めてしまえば,マクロ内に確保した自前バッファだってそのマクロがマクロバッファに存在する間だけのイノチですから,ある程度割り切ってしまう場面もあるでしょう。用途に応じた適切な選択こそが必要です。