DEC社 TOPS-20を使う方法 SNOBOL言語の組み込み関数 編

SNOBOL

SNOBOL

TOPS-20 で使うSNOBOL言語

2018年4月、歴史的に貴重なDEC社 TOPS-20でSNOBOL言語を使用しました。SNOBOL言語に便利な組み込み関数があります。このブログは、使用頻度の高い組み込み関数のメモです。

また、16進数/8進数/2進数を扱う基数変換の関数がありませんでしたので、自分で関数定義しました。

DEC社 TOPS-20を使う方法 SNOBOL言語使用編

 

組み込み関数について

よく使用する組み込み関数を示します。

数値比較

整数値 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に関するブログを以下に示します。

DEC社 TOPS-20を使う方法 KLH10エミュレーター導入編

DEC社 TOPS-20を使う方法 KLH10エミュレーター実行編

DEC社 TOPS-20を使う方法 TOPS-20コマンド使用編

DEC社 TOPS-20を使う方法 エミュレーター簡単起動/停止編

DEC社 TOPS-20を使う方法 TELNETでログイン編

DEC社 TOPS-20を使う方法 タイムゾーンの設定編

DEC社 TOPS-20を使う方法 ディレクトリのツリーウォーク編

DEC社 TOPS-20を使う方法 UNIX Toolsとbash使用編

DEC社 TOPS-20を使う方法 ファイルタイプ編

DEC社 TOPS-20を使う方法 FORTHシステム使用編

DEC社 TOPS-20を使う方法 KermitTeraTerm でファイル転送

DEC社 TOPS-20を使う方法 SNOBOL言語使用編