本章では、Vlrfsasmの文法で予め定められている14個の関数について解説します。なお、出力関数は含めていません。
値の幅を取得する関数は、#です。
この関数の引数は1つのみで、幅を取得したい値を指定します。数値リテラルを指定すれば64が返ってきます。
当然返り値にも幅があり、この関数では64ビットに固定されています。ただし、実際に意味があるのは下位20ビットのみで、上位44ビットは常に0です。
値を連接する関数は、'です。
この関数の引数は、1つ以上ならいくつでもよいです。左側の引数が上位になるように連接され、返り値の幅は各引数の幅の和です。文字コードが同じ文字列ならば、この関数で繋げて1つの文字列にできます。また、構造体を作ることもできます。
幅を読み取る関数があれば、変更する関数もあります。
以下の3関数の引数は共通の形で、第一引数に対象の値、第二引数に幅を指定します。第二引数は、幅が20ビットに満たない場合、上位に0をつけて拡張されます。また、を超える場合は、524288と解釈されます。
関数:は、第一引数について、幅を第二引数に変えて返します。つまり、第一引数の幅が第二引数より小さいときは、上位に必要なだけ0を付け足して返します。一方、大きいときは、上位のビットを削って返します。等しいときは第一引数がそのまま返ってきます。
関数<は、第一引数の下位に、第二引数ビットの0を付け足します。
関数>は、第一引数の下位から第二引数ビットを削り、残りを返します。なお、第一引数の幅が第二引数以下であるときには、幅0の値を返します。
以下の5つの関数は、値の計算を行う関数です。ビット否定関数を除く4つは、1つ以上ならいくつでも引数を取ることができます。返り値の幅は、繰り上がりなどに関わらず常に第一引数の幅と同じです。引数の順番によって結果が変わることに注意してください。また、幅の違う値同士の計算は、最下位を揃えて行われます。第一引数より短い引数があった場合、足りない上位は0として扱われます。
ビット否定関数!は、Vlrfsasmの組み込み関数で最も単純なものです。引数を1つ取り、全てのビットを反転して返します。返り値の幅は引数の幅と同じです。
加算関数+は、引数の和を返します。なお、この関数は他の関数や機能を用いて表現することができますが、実行時の計算量を抑えるため、組み込み関数にしています。
ビット積関数&は、1ビットごとに論理積を取ります。1つでも0があれば結果も0ですから、第二引数以降に第一引数より短いものがないか注意しましょう。
ビット和関数|は、1ビットごとに論理和を取ります。
排他的ビット和関数^は、1ビットごとに排他的論理和を取ります。
選択関数?は、特殊な関数です。構文というべきかもしれません。これは、第一引数に従い、第二引数と第三引数のいずれかを返します。
第一引数は、条件です。第一引数の全てのビットが0であるか、または第一引数の幅が0であるとき、条件は偽です。そうでないとき、つまり1つでも1のビットが含まれるときが真です。第二引数と第三引数にはどんな値でもちょうど1つずつを指定できます。どの引数も省略してはいけません。
条件が真と判断された場合、第二引数の値を計算して返します。偽であれば第三引数を実行して返します。この順序が選択関数の特殊な点で、遅延評価を行います。Vlrfsasmはここ以外では正格評価を行い、引数を計算してから関数を実行します。しかし、選択関数は条件分岐の役割を持つので、遅延評価とすることで再帰を使えるようにしています。
エラー関数~も特殊な構文です。これは最早関数ですらありません。値を返さないどころか、Vlrfsasmの実行をそこで止めてしまいます。引数の値は文字列で、ログの最下部に出力されます。
引数に文字列リテラルを直接指定することはできません。これは、実装時に面倒に思った結果、UTF-16LEで解釈する仕様になってしまったからです。次章で解決策を説明します。
値の特定の位置にラベルを貼り付けると、出力関数の返り値における先頭からの距離を知ることができます。。貼り付ける関数と値を得る関数は分離されており、離れたラベルの位置も得ることができます。本HP向けの変換には不要なものの、Vlrfsasmをアセンブラとして使うには必要です。
ラベルには大域ラベルと局所ラベルの2種類があります。
大域ラベルの名前は@から始まる符で、プログラムの全域に渡って共通の値を持ちます。
局所ラベルは、それが宣言された関数内でのみ有効なラベルです。さらに、同じ関数でも、もう1度呼び出せば違うラベルになります。Vlrfsasmがただ1点だけ参照透過でないのはこの点です。名前は通常の引数に準じます。局所ラベルの宣言は、関数定義の構文の2つのセミコロンの間に、名前を書くだけです。
ラベルの利用には、いくつかの注意点があります。
まず、同名の大域ラベルを複数貼り付けることはできません。同様に、1回の呼び出し内で同一の局所ラベルを複数貼り付けるのはやはりエラーになります。明示的なものでなくとも、例えば引数がラベル付きの値であって、複数回連接して返り値とし、出力関数にまで流してしまった場合もエラーです。
また、値に貼付されたラベルは、連接関数以外を通すと剥がされてしまいます。「剥がす」というのは手続き的な表現ですから不適当ですが、返り値にラベルが含まれることはない、という意味です。
さらに、「先頭からの距離」などと曖昧な表現をしましたが、これはラベルの位置と値の最上位の端との間のビット数を数えて8で割り、端数を切り捨てたものです。
最後に、Vlrfsasmには型がない、というのは嘘です。実は、値型とラベル名型という2つの型があります。本節の2関数以外の組み込み関数にラベル名型を渡すとエラーになりますし、逆も然りです。ユーザー定義関数の引数として受け渡すのみならば問題はありません。
多くのアセンブリ言語では、近距離ジャンプと長距離ジャンプの間で、命令の大きさに差があります。近距離に収まるかどうかは、仮計算をしてみないとわかりません。そのため、Vlrfsasmは1023回までの内部的な再実行を行います。この実行の各回をステップと呼びます。参照透過性を欠くのはラベルだけなので、全ラベルの位置が前のステップと一致したとき、実行が終了します。
ラベルの位置が振動してしまうと、いつまで経っても安定した結果が得られないので、1023回などと言わず、数回に留められるような設計にしましょう。
また、アドレス取得関数の返り値は、前ステップの同一のラベルの位置になります。大域ラベルなら名前で判断すればよいのですが、局所ラベルは宣言の順番で管理しています。この順番がずれると誤った値を取得することになるので、使うかどうかに関わらず、必ず各ステップで同じ数のラベルを宣言するようにしましょう。
さて、大域ラベルも局所ラベルも、値に貼り付けるには@関数を使います。引数はラベル名のみで、返り値は幅0でラベルが貼られた値です。この返り値を目的の値と連接すれば、その位置を得られるようになるわけです。
本章最後の関数は、`です。前述の通り、両種類のラベル名1つを引数に取り、出力関数の返り値内の位置を返します。なお、出力関数の返り値に指定されたラベルが含まれない場合は、エラーになります。