BASICの言語仕様の範囲では実行できない仕事が,外部プログラムを利用することで実行可能となることがあります。
Full BASICは,プログラムは実行開始時点で翻訳が終了していることが前提となっているので,実行中のプログラムが自分自身を書き換えて実行することは不可能です。
(仮称)十進BASICでは,プログラムをファイルとして書き出し,CHAIN文やEXECUTE文で実行することで,この束縛から逃れることができます。
例
関数f(x)をBASICの文法で入力すると,そのグラフを描く。(ver 5.4.0以降で実行してください)
100 REM xの数式を入力すると,-8≦x≦8の範囲でグラフを描く 110 DATA "SET POINT STYLE 1" 120 DATA "SET WINDOW -8,8,-8,8" 130 DATA "DRAW GRID" 140 DATA "FOR x=-8 TO 8 STEP 0.0004" 150 DATA " WHEN EXCEPTION IN" 160 DATA " PLOT POINTS:x,f(x)" 170 DATA " USE" 180 DATA " END WHEN" 190 DATA "NEXT x" 200 DATA "FILE DELETE ""temp.bas""" ! 後始末 210 DATA "END" 220 INPUT PROMPT "f(x)=":s$ ! 関数の式を入力 230 OPEN #1:NAME "temp.bas" ! BASICプログラムを書き出す 240 ERASE #1 250 PRINT #1:"DEF f(x)=";s$ 260 DO 270 READ IF MISSING THEN EXIT DO: s$ 280 PRINT #1:s$ 290 LOOP 300 CLOSE #1 310 CHAIN "temp.bas" ! 書き出したプログラムを実行 320 END
temp.bas削除がtemp.bas自身の仕事になっているので,temp.basに文法誤りがあって実行できないとtemp.basファイルが残ってしまいます。そのため,文法誤りが出たときは,文法誤りがなくなるまで実行する必要があります。
それが問題になる場合は,CHAIN文の代わりにEXECUTE文を用い,EXECUTE文の次にFILE DELETEを書いてください。
100 REM xの数式を入力すると,-8≦x≦8の範囲でグラフを描く 110 DATA "SET POINT STYLE 1" 120 DATA "SET WINDOW -8,8,-8,8" 130 DATA "DRAW GRID" 140 DATA "FOR x=-8 TO 8 STEP 0.0004" 150 DATA " WHEN EXCEPTION IN" 160 DATA " PLOT POINTS:x,f(x)" 170 DATA " USE" 180 DATA " END WHEN" 190 DATA "NEXT x" 200 DATA "GSAVE ""TEMP.BMP"",""4bit""" ! 結果をファイルに保存して終了 210 DATA "END" 220 INPUT PROMPT "f(x)=":s$ ! 関数の式を入力 230 OPEN #1:NAME "temp.bas" ! BASICプログラムを書き出す 240 ERASE #1 250 PRINT #1:"DEF f(x)=";s$ 260 DO 270 READ IF MISSING THEN EXIT DO: s$ 280 PRINT #1:s$ 290 LOOP 300 CLOSE #1 310 EXECUTE "temp.bas" ! 書き出したプログラムを実行 320 FILE DELETE "temp.bas" ! 後始末 330 WHEN EXCEPTION IN 340 GLOAD "TEMP.BMP" ! 結果を受け取る 350 FILE DELETE "TEMP.BMP" ! 後始末 360 USE 370 END WHEN 380 END
実用的には,書き出すプログラムはDATA文にではなく,別のファイルに入れておく形にするのが合理的です。
EXECUTE文は,EXEファイルを起動する目的にも使えます。そのEXEファイルの起動時パラメータは,EXECUTE文のWITH句で指定できます。
例
temp.txtを書き出してメモ帳で開く
10 DATA "Hello" 20 OPEN #1:NAME "A:temp.txt" 30 ERASE #1 40 READ s$ 50 PRINT #1:s$ 60 CLOSE #1 70 EXECUTE "NOTEPAD.EXE" WITH ("A:temp.txt") 80 END
また,拡張子.txtをメモ帳に関連付けてあれば,70行を
70 EXECUTE "A:temp.txt"
としても,メモ帳が開きます。
一般に,ファイル名を引数としてとることのできるプログラムであれば,この方法で処理を委ねることができます。
たとえば,(仮称)十進BASICではできないことが別のBASICではできるとしたら,そのBASICの文法で書かれたプログラムをファイルとして生成して,それを引数としてそのBASICをEXECUTE文で起動すればよいでしょう(そのBASICがそういうプログラムの引き渡し方を受け付けてくれればの話ですが)。
また,あて先と電文と納めたファイルのファイル名を引数として起動すればメールを送れるプログラムが存在すれば,BASICのプログラムでメールを送れることになります。
プログラムのはじめに
OPTION CHARACTER BYTE
を書くと,“文字=バイト”になるので,
nが0から255までの数値(整数)であるとき,
PRINT #1:chr$(n);
を実行すると,nがバイトとしてファイルに書き出されます。(文末にセミコロンが必要なことに注意)
つまり,ファイルのフォーマットが分かれば,任意のバイナリファイルが生成できることになります。
たとえば,音声ファイルのフォーマットが分かれば,それを合成し書き出して,play文で呼び出せば希望する音が出せることになります。一般に,処理を任せたいと考えるプログラムが,ファイル名を引数として起動するとそのファイルを読み込んで処理するようになっていれば,そのプログラムが要求するフォーマットでファイルを合成して書き出すことで,希望する処理が実行できます。また,実際に実行する人はいないと思いますが,EXEファイルのフォーマットが分かれば,EXEファイルを合成して,それをEXECUTE文で実行することも理論的には可能です。
なお,OPTION CHARACTER BYTE の有効範囲はプログラム単位です。外部副プログラムや外部関数定義を利用する場合には,主プログラムだけではなく,それらにも同様に書いておく必要があります。
WAVファイルフォーマットに関する情報は,
WAV ファイルフォーマット があります。
十進BASIC掲示板に実際の作例が掲載されました。たかしさんからの情報ですが,実際の作者は別の方のようです。
なお,プログラムのはじめに
OPTION CHARACTER BYTE
を書いておけば,オプションメニューでの前操作は不要です。
REM testwave.bas 十進BASICによる音声ファイル生成プログラム REM (L:1KHz -10dB 正弦波、R: 振幅 -10dB {パワーは-11.76dB} 白色雑音) REM chr$関数の引数として全ての1バイト整数が許される様に、オプションメニューの REM 「互換性」「動作」で「文字列処理の単位」を「バイト」に設定して実行すること LET d=10 !継続時間(秒) LET f0=1000 !正弦波周波数(Hz) LET db=-10 !生成波形の最大値(±32767を0dBとする) LET fs=44100 !標本化周波数(Hz) LET bps=fs*4 !1秒当りのデータ量(ステレオ16ビット量子化) LET dsize=d*fs*4 !オーディオデータサイズ LET fsize=dsize+36 !ファイルサイズ(先頭8Bを除く) LET fmtsize=16 !フォーマットサイズ LET channel=2^17+1 !ステレオPCMデータの指定 LET reso=2^20+4 !16ビットの指定 LET a=10^(db/20) OPEN #6 : NAME "test.wav" !ファイルを開き、この名前のファイルが ERASE #6 !既に存在していた場合には上書きを指定 LET t0=TIME PRINT #6 : "RIFF"; !以下、wavファイルのヘッダーを作成 CALL out4(fsize) ! PRINT #6 : "WAVEfmt "; ! CALL out4(fmtsize) ! CALL out4(channel) ! CALL out4(fs) ! CALL out4(bps) ! CALL out4(reso) ! PRINT #6 : "data"; ! CALL out4(dsize) !ここまでがヘッダー用の出力 LET al=a*32767 !左チャネル係数 LET ar=a*65535 !右チャネル係数 LET k=f0/fs*PI*2 !引数の刻み LET audio$="" !オーディオデータバッファを初期化 LET count=0 !カウンタをリセット FOR i=1 TO d*fs LET lch=INT(SIN(i*k)*al+0.5) !左チャネルは1kHz正弦波 IF lch<0 THEN LET lch=lch+65536 !負の数は補数表現 LET rch=INT((RND-0.5)*ar+0.5) !右チャネルは白色雑音 IF rch<0 THEN LET rch=rch+65536 !負の数は補数表現 LET audio$=audio$&CHR$(MOD(lch,256))&CHR$(INT(lch/256))&CHR$(MOD(rch,256))&CHR$(INT(rch/256)) LET count=count+1 IF count=64 THEN !バッファデータが所定の長さ(64サンプル256Bがほぼ最適)に達したら PRINT #6 : audio$; !データをファイルに出力して LET audio$="" !バッファを初期化し LET count=0 !カウンタをリセット END IF NEXT i PRINT #6 : audio$; !バッファに残ったデータを出力して CLOSE #6 !ファイルを閉じる PRINT "elapsed time = ";TIME-t0;"seconds" SUB out4(i4) !4バイト整数の出力(little endian) LET j4=i4 FOR m=0 TO 3 PRINT #6 : CHR$(MOD(j4,256)); LET j4=INT(j4/256) NEXT m END SUB END
なお,Windows版十進BASICでは,この後,
PLAYSOUND "test.wav" END
を実行すれば,音が聞けます。
ファイル名を引数として指定して起動したとき,それをどう処理するかは,相手のEXEファイル次第です。
<参照>ver 5.4.0でのCHAIN文,EXECUTE文の動作の変更