2.01.文法

基本的な文法を以下に示します。

(各種記号は基本的に半角です)


(1)区切り記号

半角スペース,タブ,改行が、区切り記号になります。

また、文法上の区切り記号として 命令の終端を意味するセミコロン(;)があります。

(x=60; y=sin(x); text(y,0,0,0); 等)

セミコロンは多くの場合省略可能です。しかし、以下のケースでは省略できません。

(a)returnの戻り値を省略する場合

(b)&&か||の演算子を使った式が()でくくられていない場合

(c)互換モード(spmode(1))で 3項演算子(?:)を使う場合、末尾にセミコロンが必要

(d)ポインタ的なもの(*)が文頭に来る場合、その前にセミコロンが必要

(e)forの()の中のセミコロンは省略不可


(2)コメント

シングルクォート(')でくくった部分はコメントとなり、実行時に無視されます。

('abcde' 等)

また、// を書いた行は、行末までコメントとなり、その部分は 実行時に無視されます。

(//abcde 等)

(オリジナルのSPALMには // はありません)


(3)名前

名前は、半角アルファベットまたはアンダースコア(_)で始まり、

2文字目以降は 半角アルファベット,アンダースコアまたは半角数字から 構成されます。

(abcde _abc abc123 等)

半角アルファベットの大文字小文字は区別されます(ABCとabcは別の名前と判断されます)。

名前は、関数名,変数名,定数名,ラベル名,制御命令名,モジュール名 になります。

組み込みの関数,変数,定数,制御命令と同じ名前は、

ユーザ定義の関数名,変数名,ラベル名としては使えません。

(オリジナルのSPALMではアンダースコア(_)は名前に使えません)

(ラベル名だけは、例外的に半角数字のみのものも許されます)


(4)数値

半角数字とドット(.)(小数点)を使って 数値を表します(1.2345 等)。

また、先頭に0xをつけることで16進数表記(0-9,A-F)で数値を記述できます。

(0x12AB 等) (16進数表記は整数のみです)

また、数値の後にさらに e と数値を付加して指数表記が可能です。

(1.2345e10 1e-1 等) (v8.03から) (16進数表記に指数表記は使えません)

(オリジナルのSPALMでは数値は整数のみです)

(オリジナルのSPALMには数値の指数表記はありません)


(5)文字列

ダブルクォート(")でくくることで 文字列を表します("abcde" 等)。

文字列中でダブルクォート自身を表すときは \ 記号でエスケープして \" と記述します。

また、文字列中で \ 記号自身を表すときは \\ と記述します。

また、複数の文字列を結合する場合には、ドット(.)を使います。

(例えば、s="aaa"."bbb" とすると s="aaabbb" となります)


(6)式と演算子

y=1+2 というように 式を記述できます。

以下の演算子が使用可能です。

レベル1: () ! ~ + - ++ --

レベル2: * / % \

レベル3: + . -

レベル4: << < <= >>> >> > >= == !=

レベル5: & && | || ^ ?:

レベル6: , = += -= *= /= %= \= .= &= |= ^= <<= >>>= >>=

()で計算の順番を明示しなければ、レベルの小さい演算子が優先して計算されます。

(オリジナルのSPALMには \ >>> += -= *= /= %= \= .= &= |= ^= <<=

>>>= >>= はありません)

(互換モード(spmode(1))では、3項演算子(?:)の末尾にセミコロン(;)が必要です)


各演算子の詳細については、「2.2.1.演算子の詳細」を参照ください。



(7)関数

名前の後に()をつけると 関数を表します。

関数は、関数ごとに定められた処理を実行します。

()内には関数の引数を カンマ(,)で区切って記述します(引数のない関数もあります)。

(例. cls() col(0xFF0000) text("Hello",0,0,0) 等)


関数には 組み込みの関数とユーザ定義の関数が存在します。

組み込みの関数はさらに、値を返す関数と値を返さない関数に分かれます。

値を返さない(戻り値がない)関数は、式の中では使えません。


ユーザ定義の関数は、func命令を使って定義します。

例えば 値を2倍にして返すユーザ定義の関数を定義する場合、

func f(x) { return 2*x }

のように記述します。そして y=f(2) として呼び出せば、y=4 という計算結果を得ます。



(8)変数

未使用の名前を使って 変数を作成できます。

変数には、数値や文字列を代入できます。また、演算子による計算が行えます。

(x=100 s="abcde" y=x+1 等)

変数は、特に宣言せずに式の中で使えます(後述のローカル変数を除く)。

代入しなければ初期値は0になります。

また、組み込みの変数があり、これには ユーザが値を代入することはできません。

(width height 等)

また、画像を格納するImage変数があり、

これはグラフィックス系の組み込み関数(loadimg() drawimg() 等)でのみ使用可能です。


また、*と&を変数の前につけると、ポインタとアドレス的な使い方ができます。

例えば、a=&hoge ;*a=5 とすると hoge=5 の意味になります。

(詳細は(12)を参照)


また、変数に[ ]をつけると、配列変数になります。

a[0] a["abc"] a[i] a[i+1] のように

[ ]の中に式を書いて 配列の要素を指定できます。

また、a[0][0] のように 多次元の配列を使うこともできます。

また、@(配列変数名,値1,値2,値3, ...) と書くことで、

配列変数の要素0~ に値を一括代入できます。

(例えば @(arr,100,200,300) を実行すると、

arr[0]=100 arr[1]=200 arr[2]=300 となります)


変数には、グローバル変数とローカル変数の区別があります。

グローバル変数は、プログラム内のどこからでも利用可能な変数です。

一方、ローカル変数は、ユーザ定義の関数内でのみ利用可能な変数です(関数を抜けると消滅します)。

ローカル変数を使用するには、ユーザ定義の関数内で、locまたはlocalをつけて

変数を宣言する必要があります。

グローバル変数については、特に宣言は不要ですが、glbまたはglobalをつけて

明示的に宣言することも可能です。

(local x loc x global x glb x 等)

また、組み込みの変数とImage変数は、常にグローバル変数になります。

(オリジナルのSPALMにはローカル変数はありません。すべてグローバル変数になります)

(onlocal(),offlocal()によってローカル変数の有効/無効を切り換えることができます。

また 互換モード(spmode(1))にした場合も ローカル変数は無効になります)

(v6.00から、ローカル変数はloc(またはlocal)による宣言が必要になりました)


ローカル変数の注意点1:

例えば、

func f(x) { y=100*x return y }

というユーザ定義の関数 f() があった場合に、y はグローバル変数として解釈されます。

グローバル変数の変更は予期しない不具合を起こしやすいため、

もし f() 内の y が一時的な計算用の変数ならば、

func f(x) { loc y=100*x return y }

のように明示的にloc(またはlocal)をつけて y をローカル変数にしてください。

(この場合、先頭で1回locをつけておけば、その後は、同一関数内では常に

ローカル変数として解釈されます)


ローカル変数の注意点2:

例えば、loc y とすると、同名の配列変数(y[0] y[1] y[1][2] 等)も

すべてローカル変数になります。

また、loc y[0] とすると、変数 y および同名の配列変数(y[1] y[1][2] 等)も

すべてローカル変数になります。

(v6.00から)


ローカル変数の注意点3:

v3.38から、ユーザ定義関数の仮引数は、デフォルトでローカル変数になりました。

例えば、

func f(x) { return 2*x }

における仮引数 x は、(loc(またはlocal)をつけなくても) ローカル変数になります。

(ただし、

func f(glb x) { return 2*x }

のように明示的にglb(またはglobal)をつけてグローバル変数にした場合や、

offlocal()やspmode(1)によってローカル変数を無効にした場合には、

x はグローバル変数になります)


変数の一括宣言:

v3.70から、変数の一括宣言が可能になりました。

loc,local,glb,global のいずれかの後に、変数名をカンマ区切りで並べて記述できます。

例えば、

loc a,b,c

のように記述すると、ローカル変数a,b,cが作成されます。

また、例えば、

loc a=1,b=2,c=3

のように記述すると、ローカル変数a,b,cが作成されて、a=1,b=2,c=3で初期化されます。

(変数の一括宣言は、式の中では使えません (例えば y=loc a,b,c 等は不可))



(9)定数

いくつかの組み込みの定数が存在します(HCENTER key1 green 等)。

定数は 実行時に値に置換されます。


v3.34から定数の定義/削除が行えるようになりました。

defconst()によって新しい定数を定義できます。

disconst()によって定数を削除できます。

(オリジナルのSPALMにはこれらの命令は存在しません)



(10)ラベル

ラベルは、

label ラベル名

によって定義します。

ラベルは、gotoかgosubのジャンプ先になります。


ラベル名には、半角数字のみのものも指定できます。

また、ラベル名は、ダブルクォートでくくったものも認識されます。

(過去との互換性維持のため)



(11)制御命令

以下の制御命令が存在します。そして それぞれの構文を持ちます。

switch, if, for, while, do, break, continue, end,

label, goto, gosub, func, return, module


例.

for (i=0;i<10;i++) {

text( i , width/2 , sthigh*row++ , 1 )

}


(オリジナルのSPALMには continue と module はありません)

(オリジナルのSPALMにある try ~ catch には未対応です(エラーになります))

(オリジナルのSPALMにある 分割ロード(#ソース#) には未対応です(エラーになります))

(オリジナルのSPALMにある goto,gosubのジャンプ先に計算式を指定する機能には非対応です(v6.00から))


(現状の SPALM Web インタープリターには、

「ループのプログラムで固まる」という制限事項があります。

(アプリケーション説明の「1.3.制限事項」参照))


各制御命令の詳細については、「2.3.1.制御命令の詳細」を参照ください。



(12)ポインタとアドレス的なもの

*と&を変数の前につけると、ポインタとアドレス的な使い方ができます。

(オリジナルのSPALMには存在しません)

ポインタとは、他の変数を指す変数のことです。

ポインタには、他の変数のアドレス(変数の前に&をつけて取得)を代入します。

そしてポインタの前に*をつけることで、ポインタの指す変数の実体にアクセスします。

例えば、a=&hoge ;*a=5 とすると hoge=5 の意味になります。


注意点として 文頭の*の前にはセミコロン(;)が必要です。

(乗算の*と区別するため。これを忘れると 相当分かりにくい不具合のもとになります)

(ただし、行頭の*の前にはセミコロン(;)が自動挿入されるため、

行頭に関してだけはセミコロン(;)が不要です(v11.00から))


本機能を利用して、例えばユーザ定義の関数で 複数の戻り値を返すことができます。

例1.

func f(a,b,*ret1,*ret2) {

*ret1=a+b

*ret2=a-b

}

f(1,2,&r1,&r2)

この例を実行すると、

r1=3

r2=-1

となります。(複数の戻り値を返す例です)


また、配列変数を返すこともできます。

例2.

func f(a,b,*arr) {

*(arr)[0]=a+b

*(arr)[1]=a-b

}

f(1,2,&r)

この例を実行すると、

r[0]=3

r[1]=-1

となります。(配列変数を返す例です)


(上記例の *(arr)[0] は、arrが指す変数に[0]を付けて配列化するという意味です。

括弧の位置に注意してください。

(これを単に *arr[0] と書くと、arr[0] が指す変数という異なった意味になります))



(13)関数ポインタ的なもの

(12)と似た記述で、関数ポインタ的なものを使うことができます。

すなわち、関数のアドレスを変数に代入して、それを呼び出すことができます。

(オリジナルのSPALMには存在しません)


(注意点として、現状、組み込みの関数をポインタ経由で呼び出すことはできません)


例1.

func f(x) { return x*2 }

g=&f

y=*g(2)

この例を実行すると、

y=4

となります。


例2.

func gm() { return "おはよう" }

func hl() { return "こんにちは" }

func ge() { return "こんばんは" }

@(arr,&gm,&hl,&ge)

sh=sthigh; row=0

for (i=0;i<3;i++) {

text( *arr[i]() , 0 , sh*row++ , 0 )

}

この例を実行すると、

おはよう

こんにちは

こんばんは

と表示されます。(配列変数に格納した関数を、順番に実行する例です)



(14)構造体的なもの

配列変数の[ ]内には文字列を指定することができます。

これを利用して、構造体的なものを記述できます。

すなわち、内部に複数のデータを持つようなデータ構造を表現できます。

また、(12)を利用して、構造体的なものを使用する関数を定義できます。


例.

// 構造体のひな型

// Person["name"]="NoName"

// Person["age"]=0

// 構造体を初期化する関数

func Person_init(*p,name,age) {

*(p)["name"]=name

*(p)["age"]=age

}

// 構造体を使用する関数

func Person_info(*p) {

return *(p)["name"]." is ".*(p)["age"]." years old."

}

// 構造体を初期化して使用する

Person_init(&p1,"John",19)

Person_init(&p2,"Mike",20)

sh=sthigh; row=0

text( Person_info(&p1) , 5 , 5+sh*row++ , 0 )

text( Person_info(&p2) , 5 , 5+sh*row++ , 0 )

この例を実行すると、

John is 19 years old.

Mike is 20 years old.

と表示されます。



(15)簡易モジュール機能

module命令により、簡易モジュール機能を使用できます。

モジュールを使用すると、グローバル変数名等がバッティングするリスクを

減らすことができます。

(オリジナルのSPALMには存在しません)

(現状、モジュールを別のファイルに分離することはできません)

(本命令は、v14.00で追加され、v18.00で制御命令になりました。

記述方法が module(MZ) から module MZ { } に変わったため、

v18.00より前のバージョンとは互換性がなくなっています)


例えば、module MZ { 命令 } とすると、命令の部分は モジュール MZ に所属します。

そして、モジュール内では、グローバル変数名,ユーザ定義関数名,ラベル名 の前に

'MZ#' が自動的に付加されます。

これらの要素には、同一モジュール内であれば 'MZ#' を付けなくてもアクセス可能です。

しかし、モジュールの外部からは 'MZ#' を付けてアクセスする必要があります (MZ#x 等)。


また、逆にモジュール内から、

モジュールに所属しない グローバル変数名,ユーザ定義関数名,ラベル名 に

アクセスする場合には、先頭に # を付けてアクセスする必要があります (#x 等)。


(現状、定数名は、モジュールの影響を受けません)


例1.

module MX {

glb x=100

func f() { text("x=".x,0,0,0) }

}

module MY {

glb x=200

func f() { text("x=".x,0,20,0) }

}

MX#f()

MY#f()

この例を実行すると、

x=100

x=200

と表示されます。(モジュールによって、同名のグローバル変数xが区別されています)


モジュールは、入れ子にすることができます(v18.00から)。

例えば、module MX { module MY { 命令 } } とすると、

命令の部分の グローバル変数名,ユーザ定義関数名,ラベル名 の前には

'MX#MY#' が付加されます。


例2.

module MX {

glb x=1000

module MY {

glb x=2000

}

}

text("x=".MX#x,0,0,0)

text("x=".MX#MY#x,0,20,0)

この例を実行すると、

x=1000

x=2000

と表示されます。(入れ子にしたモジュール内で、同名のグローバル変数xが区別されています)


(2018-8-5)