プログラミング言語 TL/1 について、予約語と同じ名前の変数 (または関数や手続き) を宣言して使うことが可能であるということは以前に紹介した。 その規則には仕様書からは読み取れない若干の曖昧さがあるのではないかという意見を述べていたところ、当時の実際の処理系 (TL/1-FM) で様々な例を試した結果をコメントで貰ったのでひとつひとつ検証していこうと思う。
うまく動く例1
VAR VAR ARRAY ARRAY[2] BEGIN ARRAY[1]:=123 ARRAY[2]:=234 VAR:=ARRAY[1]+ARRAY[2] WRITE(0:VAR,CRLF) END
これについては基本的な動作であり、特に考察が必要な要素はない。 ARRAY という名前の配列が宣言された時点でそれ以後は ARRAY は配列名として解釈される。
うまく動く例2
VAR VAR ARRAY VAR[2] BEGIN VAR[1]:=123 VAR[2]:=234 VAR[0]:=VAR[1]+VAR[2] WRITE(0:VAR[0],CRLF) END
名前 VAR
は単変数と配列の両方で宣言しているが、このとき単変数ではなく配列として機能する。 これも仕様通りの動作で、特に疑問なことはない。
以前に取り上げているが、複数の意味で宣言されているときにどれが優先されるかは仕様に明記されていて、単変数よりは配列が優先される。
コンパイルエラーの例1
VAR VAR ARRAY VAR[2] BEGIN VAR[1]:=123 VAR[2]:=234 VAR:=VAR[1]+VAR[2] WRITE(0:VAR,CRLF) END
エラーの内容
VAR VAR ARRAY VAR[2] BEGIN VAR[1]:=123 VAR[2]:=234 VAR:= Error : in 60
VAR
を配列ではなく単変数で扱おうとしている個所でエラーになっている。 上で述べたように、単変数より配列としての解釈が優先されるので仕様通りであり、不自然なところはない。
コンパイルエラーの例2
VAR ARRAY ARRAY FOO[2] BEGIN FOO[1]:=123 FOO[2]:=234 ARRAY:=FOO[1]+FOO[2] WRITE(0:ARRAY,CRLF) END
エラーの内容
VAR ARRAY ARRAY FOO[ Error FOO in 20
これについては仕様からは読み取れなかった挙動である。 宣言がいつから有効になるのかという点について、個人的には BEGIN の直後からとするのが曖昧さが少ないと考えていたのだが、実際には宣言の直後からだったようだ。
コンパイルエラーの例3
VAR ARRAY ARRAY:=123 BEGIN FOO[1]:=123 FOO[2]:=234 ARRAY:=FOO[1]+FOO[2] WRITE(0:ARRAY,CRLF) END
エラーの内容
VAR ARRAY ARRAY:=123 BEGIN Error ARRAY in 30
これについては興味深い。 ARRAY:=123
に相当するオブジェクトコードは正しく生成されているそうなので、そこまでは正しいプログラムだとしてコンパイラは許容しているということになる。 おそらくは、複文 (仕様書にない用語だがBEGIN
/END
、またはその他の文括弧で囲んだ複数の文を便宜上こう呼ぶことにする) の扱いについて仕様に書かれていない解釈が存在するのだと思う。
複文は複数の文をまとめたひとつの文であるので、文が現れることが出来る個所にはどこにでも現れることが出来る。 しかし、主プログラムや副プログラムの本体部分はたとえ文がひとつであろうとも BEGIN
/END
で囲まなければならず、他の文括弧を用いることもできない。 それが仕様書から読み取れる仕様である。
しかし、実際の挙動は主プログラムや副プログラムの本体部分は単に「文」であればなんでも良いのではないか。 他の文括弧を使った複文でも通りそうな気がする。
引数なし関数呼び出しの次の行の解釈でうまく動く例1
FUNC FOO VAR X BEGIN X:=FOO (X) WRITE(0:X) END FOO BEGIN RETURN 2 END
実行結果
2
TL/1 は行指向ではない (改行は単に無視される) ので、関数名と引数の間に改行を入れても関係なく引数として渡されると解釈すべきで、定義より多く引数を渡す分には変数が参照されることはないのだから問題なく動作するのだろう。
引数なし関数呼び出しの次の行の解釈
FUNC FOO VAR X BEGIN X:=FOO {X:=1} WRITE(0:X) END FOO BEGIN RETURN 2 END
実行結果
1
{
}
のかわりに (
)
を文括弧として使った場合
FUNC FOO VAR X BEGIN X:=FOO (X:=1) WRITE(0:X) END FOO BEGIN RETURN 2 END
エラーの内容
FUNC FOO VAR X BEGIN X:=FOO (X:= Error : in 50
関数名の直後の開き丸括弧は常に引数の開始であると解釈するようだ。 当時のコンピュータの能力を考えると少ない先読みでトークンの意味を確定しようとする設計は合理的と言える。 おそらく TL/1 の文法を分類するなら LL(1) の範疇に収まるのではないかと思う。 一方で、そもそも丸括弧を式括弧にも文括弧にも引数にも用いようとしているのが解釈し難さの原因でもあるので最初から分けておくべきだったとも思う。
引数付関数呼び出しを2行に分けて記述してうまく(?)動く例
FUNC FOO VAR X BEGIN X:=FOO (1) WRITE(0:X) END FOO VAR Y BEGIN RETURN 2+Y END
実行結果
3
これは少しばかり奇妙な挙動だと思う。 スタックレイアウトが想像できない。
Document ID: 6ed36823564fc58efd50e2fd7816e296