SmileBasic3.xxの変数セマンティクス
プチコンのバグを整理している( https://github.com/okuoku/ptc3sim/issues )と、プチコン3号のBASIC実装には"言語処理系あるある"がかなり含まれているような気がしてくる。
(BASICの伝統に従って、SmileBasicでは戻り値を取るサブルーチンを関数、そうでないものをステートメントとしている。この使い分けはプチコン3号のドキュメントでは曖昧なので、ここでは"手続き"と"関数"に分ける。"手続き"は副作用を目的としている。"関数"はそうではない。結論としては、プチコン3号には真の関数は存在しない。)
DEF OUT の場合、関数名はただのラベルでしかないことがわかる。また、引数も同様であり、呼び出した側の型と実際の戻り値の型が合っていればエラーにならない。
プチコン3号のBASIC(SmileBasic)では、型検査は非常に限られたタイミングでしか行われない。概念的には(実際のコードが読めるわけではないので当然推測)、SmileBasicの変数は実際の変数ストレージとラベルが独立している。ラベルには"A$"とか"A#"のような型修飾を付与することができるが、これは最初に変数ストレージを確保する際にしか使用されない。
変数ストレージには型が有るため、Type mismatchエラーは代入や参照時に依然検出される可能性が有る。
(関数名や手続き名の型修飾は伝統的に付与されている命名規則に過ぎないので、SmileBasicはチェックしない。このような実装は初期MS-BASICからの伝統で、MID$のような文字列関数はハードコードされた $ サフィックスを持つ。
.:A188 43 48 52 A4 4C 45 46 54 chr$ left$
MS-BASICは領域節約のため予約語終端をNULバイトではなく+0x80した文字で表わす。0xA4 - 0x80 = 0x24 '$'。
プチコン3号のマニュアルには"サフィックスで返り値を指定" http://smileboom.com/special/ptcm3/reference/#advanced-control が挙げられているが、これでコンパイラがエラーを出力することは無いため、意味は無い。もちろん、プログラミング慣習としては、文字列を返す関数には $ を付けることが好ましい。)
次のような絵を考える:
SmileBasicにおける手続きのOUTはいわゆる参照渡しであり、ヒープに存在する実際の変数ストレージへのポインタのみを渡す。このときに、"B"という(整数変数のようなサフィックスを持つ)変数に文字列変数ストレージが渡されることをSmileBasic処理系はエラーにしない。
型検査の例外としてはSWAPが有る。SWAPは、引数の型が一致していない場合は、参照を入れ替えることなくエラーとする。このため、SWAP A$, B のようにして、直接的にラベルを差し替えることはできない。
ただし、SmileBasicにおいて文字列は配列のスーパーセットであるため( https://github.com/okuoku/ptc3sim/issues/44 )、配列を経由するとSWAPができてしまう( https://github.com/okuoku/ptc3sim/issues/39 )。(この方法で文字列を操作するとメモリリークが発生するため、将来のバージョンでは禁止されると思われる)
このようなポイントが有るため、SmileBasicは静的型の言語になることに失敗している。また、動的型のための手続き(is_stringとかis_number等のプレディケート)を欠いているため、動的型のプログラミング言語として取り扱うこともかなり難しい。
関数呼び出しは手続き呼び出しの糖衣構文
SmileBasicでは、関数呼び出しは手続き呼び出しの糖衣構文として実装されている。このため、互換インタプリタを作る上では、関数を特別扱いする必要は無い。
例えば、組込み関数であるsin等も、OUT構文を使用して呼び出すことができる。
SIN 3.14 OUT A#
? A#
0.00159265
また、この事実は(C言語等と異なり)関数を戻り値無しで呼び出せないこと( https://github.com/okuoku/ptc3sim/issues/46 )を説明する。この関数の戻り値を厳密に取る必要が有る点はCommon LispよりもSchemeに近い。
関数定義(DEF)も、手続き定義の糖衣構文と見做すことができる。つまり、適当な変数 X を内部的に生成し、それに代入してからRETURNすることになる。
DEF R1(IN) IF IN=="STR" THEN RETURN "STRING" IF IN=="NUM" THEN RETURN 0.1 END REF R2 IN OUT X IF IN=="STR" THEN X="STRING":RETURN IF IN=="NUM" THEN X=0.1:RETURN END R1 "STR" OUT A$ R1 "NUM" OUT B R2 "STR" OUT C$ R2 "NUM" OUT D ? A$,B,C$,D
定義R1は、内部的にはR2のように展開されていると考えられる。よって、R1とR2は同じように使用することができる。