Edited at

HSPのモジュール仕様を暴く


0.初めに

結構踏み込んだことが書いてあるつもりなので言語研究者向けの内容です。

他でいうところの言語の名前空間・クラス・インスタンス(オブジェクト)・構造体などの知識が必要です。


1.モジュール機能

HSPにはモジュールといった名前空間や静的クラスのようなものがあります。

これを使うことで名前空間が適用され、簡単には内部の要素にアクセスできなくすることができます。

しかし、仕組み的にはかなり単純なものでPrivateな変数の使用はできません。

実体としてはで要素の名前に@モジュール名をつけているだけで実質的に全てがグローバルな要素であるためです。

名前を被りにくく追記してくれる機能なので一種のシンタックスシュガー(糖衣構文)とも言えるかもしれません。

例えば、次の処理はほぼ同一のものとなります。

#module test

#enum a 0
#enum global b 0
#const c 10
#const global d 20
#define e "e"
#define global f "f"

#deffunc g
h="h"
i@="i"
return

#defcfunc local j
return "j"

*k
return "k"

*l@
return "l"
#global

mes a@test
mes b
mes c@test
mes d
mes e@test
mes f
g
mes h@test
mes i
mes j@test()
gosub*k@test
mes refstr
gosub*l
mes refstr

goto*testdef

#enum a@test 0
#enum b 0
#const c@test 10
#const d 20
#define e@test "e"
#define f "f"

#deffunc g
h@test="h"
i="i"
return

#defcfunc j@test
return "j"

*k@test
return "k"

*l
return "l"
*testdef

mes a@test
mes b
mes c@test
mes d
mes e@test
mes f
g
mes h@test
mes i
mes j@test()
gosub*k@test
mes refstr
gosub*l
mes refstr

二つ目のプログラムはモジュールで囲んでいた部分にそれと同様の名前を付けていただけです。

基本的にはモジュール機能はシンタックスシュガーですが、利用すると以下の様な恩恵を得られます。


  • モジュール内で呼び合う場合はローカルな変数・定数・マクロ・ラベルをモジュール名無しで呼ぶことができる(命令・関数は不可)。

  • モジュール内の自動実行を阻止できる(自動で実行されないので関数や命令の呼び出しが必須になる)。

一般的な言語で言われるグローバル空間を汚さないというのは不可能だと思いますが、HSPで極力きれいに名前空間を保つのにモジュール機能はかなり大切な機能だといえます。

ここからが本題です。


2.モジュール変数/モジュール型変数

本機能の詳細については拙作のHSPでモジュール型変数入門をご確認ください。

ここで述べたいこととしてはモジュール変数という機能はよくクラスに近いものだと言われますが、クラスっぽいのは見せかけだけで実体としてはvarnameで示されるように"struct"、つまり構造体の方が近いということです。

その特異点について以下より提示します。


特異点1: モジュール型変数は一切のメソッドを持たない

一般的なクラスであれば、クラスのインスタンスはメソッドを持ちます。

HSPにはあるオブジェクトでしか使用できない関数というものは一切ありません。

#modfuncや#modcfuncといったモジュール関数/命令がそのように見えるかもしれませんが、これら関数は第一引数にモジュール型変数を受け取りさえすれば別モジュールで生成されたオブジェクトであっても作動が可能なのです。

別のクラスのインスタンスが持っているメソッドには絶対にアクセスすることはできません。

できるように見える場合でもそれは継承やオーバーライド・オーバーロードなどで別の実装がされているに過ぎません。

ですが、HSPではモジュールに結合したメソッドではなくただの関数であるため、

異なるモジュールで生成されたモジュール型変数でも複数モジュール間で相互にアクセスしあうことができます。

例として次のプログラムをご確認ください。

#module A hoge

#modinit
hoge="A"
return

#modcfunc getHoge
return hoge
#global

#module B hoge
#modinit
hoge="B"
return
#global

newmod aa,A
newmod bb,B

mes getHoge(aa)
mes getHoge(bb)

HSPではこのようにBで生成されたオブジェクトがAに含まれているモジュール関数/命令の使用が可能となっています。

例えばVBで同様に書いてみるとエラーとなってしまいます。

Imports System

Class A
Private hoge As String
Sub New
hoge="A"
End Sub

Function getHoge() As String
Return hoge
End Function
End Class

Class B
Private hoge As String
Sub New
hoge="B"
End Sub
End Class

Module Program
Sub Main
Dim aa As New A
Dim bb As New B

Console.WriteLine(aa.getHoge())
Console.WriteLine(bb.getHoge()) 'Error!!
End Sub
End Module

これがクラスを持つ言語の一般的な振る舞いであるため、HSPの機能を誤認させる一因となっています。


特異点2: モジュール関数/命令はただの関数/命令そのものである

先ほどの話から続くものです。先ほどでも示したように、HSPにメソッドというものはありません。

ここで、HSPのプリプロセス後のファイルを出力する#cmpopt ppout 1を使用してみましょう。

#cmpopt ppout 1


#module A hoge
#modinit
hoge="A"
return

#modcfunc getHoge
return hoge
#global

#module B hoge
#modinit
hoge="B"
return
#global

newmod aa,A
newmod bb,B

mes getHoge(aa)
mes getHoge(bb)

これで出力されたhsptmp.iを確認してみると次のようになります(そのままだと見にくいのでインデントで整形だけ行ってあります)。

#module a

goto@hsp *_a_exit
#struct a var hoge@a
##3

#deffunc __init modinit a
hoge@a="A"
return@hsp

#defcfunc gethoge modvar a
return@hsp hoge@a

*_a_exit
#global
##10

#module b
goto@hsp *_b_exit
#struct b var hoge@b
##12

#deffunc __init modinit b
hoge@b="B"
return@hsp

*_b_exit
#global
##16

newmod@hsp aa,a
newmod@hsp bb,b

mes@hsp gethoge(aa)
mes@hsp gethoge(bb)

これを見るとわかるように#modinitや#modfunc, #modcfuncは実際には#deffuncであるということです。

そして、特筆したいのが#defcfunc gethoge modvar aの部分です。

これは関数がモジュール型変数を受け取り、その関数内にモジュール変数を適用させるという動作を行っています。

これがどういうことなのかと詳細については次項にて。


特異点3: モジュール型変数の性質は構造体や型を持たない配列に近いものである。

まず、次のプログラムを見ていただきたいと思います。

#module A a1,a2,a3

#modinit
mes "newA"
return
#global

#module B b1,b2,b3
#modfunc setb int _b1,double _b2,str _b3
b1=_b1
b2=_b2
b3=_b3
return
#global

#module C c1,c2,c3
#modcfunc getc1
return c1
#modcfunc getc2
return c2
#modcfunc getc3
return c3
#global

newmod abc,A
setb abc,1,3.14,"A"
mes getc1(abc)
mes getc2(abc)
mes getc3(abc)

モジュールAでモジュール型変数を生成し、モジュールBでセッター、モジュールCでゲッターを定義しています。

また、モジュール変数名は全てのモジュールで異なる名前としています。

実際これでも正しいプログラムとして動作するのです。

これに先ほどのmodvarキーワードが関連してきます。

まず、modvarはモジュール型変数を受け取るために内部で設定されている型です。

このmodvarはモジュール型変数に順番で格納された値を、

modvarの所属しているモジュールのモジュール変数に書き出すということを行っているのです。


とここまで書いてあるのですが、下書き状態で長きに放置していたため、この後何を書こうとしていたのか忘れました。思い出したら追記します。

多分追記終わり?

機会があれば清書するかも。