TOPS-20 で使うSNOBOL言語
2018年4月、歴史的に貴重なDEC社 TOPS-20でSNOBOL言語を使用しました。SNOBOL言語に便利な組み込み関数があります。このブログは、使用頻度の高い組み込み関数のメモです。
また、16進数/8進数/2進数を扱う基数変換の関数がありませんでしたので、自分で関数定義しました。
組み込み関数について
よく使用する組み込み関数を示します。
数値比較
整数値 i1とi2の比較が条件成立したとき、関数は成功してヌル文字を返します。不成立のときは、関数は失敗します。
組み込み関数 | 意味 |
---|---|
EQ(i1,i2) | 数値 (i1 = i2)の判定 |
NE(i1,i2) | 数値 (i1 ≠ i2)の判定 |
GE(i1,i2) | 数値 (i1 ≧ i2)の判定 |
GT(i1,i2) | 数値 (i1 > i2)の判定 |
LE(i1,i2) | 数値 (i1 ≦ i2)の判定 |
LT(i1,i2) | 数値 (i1 < i2)の判定 |
数値比較関数を使用して、ループを構成することができます。数値比較が偽になったとき、代入文を実行しません。
@TYPE FUNC1.SNO *------------------------------------------ C = 1 Z = 5 OUTPUT = " " "EQ|NE|LE|LT|GE|GT" LOOP CEQ = "x" CNE = "x" CLE = "x" CLT = "x" CGE = "x" CGT = "x" * CEQ = EQ(C,Z) "o" CNE = NE(C,Z) "o" CLE = LE(C,Z) "o" CLT = LT(C,Z) "o" CGE = GE(C,Z) "o" CGT = GT(C,Z) "o" OUTPUT = C " " CEQ " |" CNE " |" CLE " |" CLT " |" CGE " |" CGT * C = LT(C,9) C + 1 :S(LOOP) *------------------------------------ END @
実行します。
@SNOBOL FUNC1.SNO EQ|NE|LE|LT|GE|GT 1 x |o |o |o |x |x 2 x |o |o |o |x |x 3 x |o |o |o |x |x 4 x |o |o |o |x |x 5 o |x |o |x |o |x 6 x |o |x |x |o |o 7 x |o |x |x |o |o 8 x |o |x |x |o |o 9 x |o |x |x |o |o EXIT @
文字列比較
文字列 s1とs2の比較が条件成立したとき、関数は成功してヌル文字を返します。不成立のときは、関数は失敗します。
組み込み関数 | 意味 |
---|---|
IDENT(s1,s2) | 文字列 (s1 = s2)の判定 |
IDENT(s1) | 文字列 (s1 = NULL)の判定 |
DIFFER(s1,s2) | 文字列 (s1 ≠ s2)の判定 |
DIFFER(s1) | 文字列 (s1 ≠ NULL)の判定 |
文字列操作
組み込み関数 | 意味 |
---|---|
LPAD(s,i,c) | 文字列sの左端にcをパディング (文字列サイズ:i) |
RPAD(s,i,c) | 文字列sの右端にcをパディング (文字列サイズ:i) |
TRIM(s) | 文字列sの末尾の空白を削除 |
DUPL(s,i) | 文字列sをi回重複 |
REPLACE(s1,s2,s3) | 文字列s1を置換 s2(p)→s3(p) |
SIZE(s) | 文字列sの長さ |
文字列操作文字列操作プログラム例を示します。
@TYPE FUNC2.SNO *------------------------------------------ STR1 = "Z=X+Y " STR2 = TRIM(STR1) OUTPUT = "SIZE=" SIZE(STR1) " (" STR1 ")" OUTPUT = "SIZE=" SIZE(STR2) " (" STR2 ")" * STR3 = REPLACE(STR2 ,"XYZ" ,"123") OUTPUT = STR2 " --> " STR3 * C = 0 LOOP STR = DUPL("*",C) OUTPUT = LPAD(STR ,12 ,'.') "|" RPAD(STR ,12 ,'.') C = LT(C,10) C + 1 :S(LOOP) *------------------------------------ END
実行します。
@SNOBOL FUNC2.SNO SIZE=6 (Z=X+Y ) SIZE=5 (Z=X+Y) Z=X+Y --> 3=1+2 ............|............ ...........*|*........... ..........**|**.......... .........***|***......... ........****|****........ .......*****|*****....... ......******|******...... .....*******|*******..... ....********|********.... ...*********|*********... ..**********|**********.. EXIT @
ユーザー関数定義
ユーザー関数を定義することができます。
DEFINE(‘関数名(引数,…) ローカル変数,…’,エントリーラベル)
エントリーラベルを省略した場合は、エントリーは関数名と同じです。
ユーザー定義関数は、ラベル :(RETURN)で呼び出し元にリターンします。また、ラベル :(FRETURN)の場合は関数呼び出しが失敗します。従って、呼び出し元で F(ラベル)でエラー分岐することができます。
@TYPE FUNC3.SNO *------------------------------------------ * Function Define * DEFINE('TOUPPER(STR) UC,LC') DEFINE('CVSTR2BIN(STR,BASE) C,E,DIGIT,DGSET,N') DEFINE('CVBIN2HEX(BIN,N) Q,R') DEFINE('CVOCT2HEX(STRO) ') *================================================== :(START) *================================================== * Convert String Case Lower To Upper TOUPPER(STR) * TOUPPER TOUPPER = STR UC = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' LC = 'abcdefghijklmnopqrstuvwxyz' TOUPPER = REPLACE(STR,LC,UC) :(RETURN) *================================================== * Convert String To Binary CVSTR2BIN(STR,BASE) * CVSTR2BIN CVSTR2BIN = 0 C = 1 E = SIZE(STR) DGSET = SUBSTR("0123456789ABCDEF" ,1,BASE) CVS2B.LOOP DIGIT = SUBSTR(STR ,C ,1) DGSET BREAK(DIGIT) . N :F(FRETURN) CVSTR2BIN = CVSTR2BIN * BASE + SIZE(N) C = LT(C,E) C + 1 :S(CVS2B.LOOP) :(RETURN) *================================================== * Convert Binary To Hex CVBIN2HEX(BIN,N) * CVBIN2HEX CVBIN2HEX = "" CVB2H.LOOP Q = BIN / 16 R = REMDR(BIN ,16) BIN = Q "0123456789ABCDEF" LEN(R) LEN(1) . S CVBIN2HEX = S CVBIN2HEX N = GT(N,1) N - 1 :S(CVB2H.LOOP) :(RETURN) *================================================== * Convert STR(Oct) To STR(Hex) CVOCT2HEX(STRO) * CVOCT2HEX CVOCT2HEX = "" CVOCT2HEX = CVBIN2HEX(CVSTR2BIN(STRO,8),2) :(RETURN) *====================================================== START *------------------------------------ IDATA = "ABCabc_xyz_09" UDATA = TOUPPER(IDATA) OUTPUT = "TOUPPER : " "(" IDATA ") ---> (" UDATA ")" *------------------------------------ STR2 = "1010" BIN = CVSTR2BIN(STR2 ,2) OUTPUT = "CVSTR2BIN (2) : " "(" STR2 ") ---> (" BIN ")" * STR8 = "17" BIN = CVSTR2BIN(STR8 ,8) OUTPUT = "CVSTR2BIN (8) : " "(" STR8 ") ---> (" BIN ")" * STRH = "1F" BIN = CVSTR2BIN(STRH ,16) OUTPUT = "CVSTR2BIN (H) : " "(" STRH ") ---> (" BIN ")" *------------------------------------ BIN = 254 STR = CVBIN2HEX(BIN,4) OUTPUT = "CVBIN2HEX : " "(" BIN ") ---> (" STR ")" * BIN = 2049 STR = CVBIN2HEX(BIN,4) OUTPUT = "CVBIN2HEX : " "(" BIN ") ---> (" STR ")" *------------------------------------ STROCT = "10" HEX = CVOCT2HEX(STROCT) OUTPUT = "CVOCT2HEX : " "(" STROCT ") ---> (" HEX ")" * STROCT = "376" HEX = CVOCT2HEX(STROCT) OUTPUT = "CVOCT2HEX : " "(" STROCT ") ---> (" HEX ")" *------------------------------------ END @
- TOUPPER(STR) :英大文字変換
- CVSTR2BIN(STR,BASE):文字列STRを基数(BASE)で整数に変換
- CVBIN2HEX(BIN,N):整数をN桁16進数に変換
- CVOCT2HEX(STRO):8進数文字列を16進数2桁に変換
SNOBOL言語処理系は基本的にインタプリターなので、関数を呼び出す前に DEFINE()関数定義文を実行する必要があります。そこで、最初にDEFINE()関数を実行して関数定義内容のSNOBOL文をスキップします。
実行します。
@SNOBOL FUNC3.SNO TOUPPER : (ABCabc_xyz_09) ---> (ABCABC_XYZ_09) CVSTR2BIN (2) : (1010) ---> (10) CVSTR2BIN (8) : (17) ---> (15) CVSTR2BIN (H) : (1F) ---> (31) CVBIN2HEX : (254) ---> (00FE) CVBIN2HEX : (2049) ---> (0801) CVOCT2HEX : (10) ---> (08) CVOCT2HEX : (376) ---> (FE) EXIT
配列 : ARRAY()
配列は、ARRAY()組み込み関数で定義します。
変数 = ARRAY(要素数)
変数 = ARRAY(要素数 ,初期値)
配列の初期値は、省略可能です。
多次元配列を定義する場合は、次元をカンマで区切り指定します。(必ず文字列で指定します)
配列の要素へのアクセスは、「配列変数<○>」の形式で添え字を指定します。添え字の下限は1です(0ではありません)。添え字の上限と下限を指定することもできます。
C/C++言語を頻繁に使っていたので、配列の下限を0とすることに慣れていました。SNOBOL言語は、ALGOL言語やPASCAL言語のように添え字を任意で指定できるので、よりアルゴリズムに近い記述ができます。
定義方法 | アクセス | 備考 |
---|---|---|
D1=ARRAY(5) | D1<1> | 要素数5の1次元配列 |
D2=ARRAY(“3,3”) | D2<3,3> | 要素数3×3の2次元配列 |
D3=ARRAY(“-1:1,-1:1”) | D3<-1,1> | 要素数3×3の2次元配列 |
@TYPE DATA1.SNO *------------------------------------------ D1 = ARRAY(5) * C = 1 LOOP1 D1 = SUBSTR("ABCDEF",C,1) C = LE(C,5) C + 1 :S(LOOP1) * C = 0 LOOP2 OUTPUT = C " " D1 C = LE(C,5) C + 1 :S(LOOP2) *------------------------------------ D2 = ARRAY("3,3","x") D2<2,2> = "o" * C = 1 OUTPUT = "------------" OUTPUT = " 1 2 3" LOOP3 OUTPUT = LPAD(C,3,' ') " " D2<C,1> " " D2<C,2> " " D2<C,3> C = LE(C,3) C + 1 :S(LOOP3) *------------------------------------ D3 = ARRAY("-1:1,-1:1","o") D3<0,0> = "x" * C = -1 OUTPUT = "------------" OUTPUT = " -1 0 1" LOOP4 OUTPUT = LPAD(C,3,' ') " " D3<C,-1> " " D3<C,0> " " D3<C,1> C = LE(C,1) C + 1 :S(LOOP4) *------------------------------------ END
実行します。
@SNOBOL DATA1.SNO 1 A 2 B 3 C 4 D 5 E ------------ 1 2 3 1 x x x 2 x o x 3 x x x ------------ -1 0 1 -1 o o o 0 o x o 1 o o o EXIT
動的配列 : TABLE()
SNOBOL言語は、AWKスクリプトのように動的配列を使えます。動的配列を利用すると、よりアルゴリズムに近い記述ができて便利です。
動的配列は、TABLE()組み込み関数で定義します。
変数 = TABLE()
変数 = TABLE(初期要素数 ,追加要素数)
テーブルのサイズは固定されていません。新しい要素が追加されるたびに自動的に拡張されます。初期要素数と追加要素数は、省略可能です。
配列の要素へのアクセスは、「動的配列変数[○]」の形式で添え字を指定します。
テーブルの添え字は整数に限定されず、任意のSNOBOL4データ型(文字列など)が使用できます。テーブルに存在しない添え字を指定した場合は、NULL文字列のエントリーを追加します。
@TYPE DATA2.SNO *------------------------------------------ T = TABLE() * IX = "ABC" T[IX] = "AWK" T[IX] = T[IX] "_SNOBOL" OUTPUT = T["ABC"] "(" T["XYZ"] ")" *------------------------------------ END
実行します。
@SNOBOL DATA2.SNO AWK_SNOBOL() EXIT
演算子シノニム
SNOBOL言語は、演算子を定義することができます。これは優れた機能です。とても昔に作られた言語とは思えません。
OPSYN( 連結演算子 , 関数名 ,0) // 関数名 同義語
OPSYN( 連結演算子 , 関数名 ,1) // 単項演算子
OPSYN( 連結演算子 , 関数名 ,2) // 二項演算子
連結演算子は、1文字の文字列として指定します。
@TYPE OPSYN.SNO *------------------------------------------ OPSYN( "MOD" , "REMDR" ,0) OPSYN( "%" , "REMDR" ,2) N = 23; D = 2 OUTPUT = N " % " D " = " (N % D) OUTPUT = N " % " D " = " MOD(N ,D) N = 23; D = 16 OUTPUT = N " % " D " = " (N % D) END
剰余を算出する組み込み関数 REMDR()をMOD()で呼び出すことができるようにします。また、C言語のように % 演算子が使えるようにします。
「N % D」は、REMDR(N,D)を呼び出します。
実行します。
@snobol opsyn.sno 23 % 2 = 1 23 % 2 = 1 23 % 16 = 7 EXIT
まとめ
SNOBOL言語の組み込み関数について調べました。
これで、SNOBOL言語でプログラミングする基礎を修得しました。強力なパターンマッチング機能を使って、複雑な処理をSNOBOL言語で書いてみようと思います。
TOPS-20に関するブログ
歴史的に貴重なDEC社 TOPS-20を使うためにKLH10エミュレーターをLinuxにインストールして、TOPS-20を実行することができました。
TOPS-20に関するブログを以下に示します。