|
Vino言語(1/4) (2/4) (3/4) (4/4)
Vino 言語仕様の予定 ver. 0.02 2002/11/20
…と、ここまで書いて力尽きた…。ごめんなさい。以下、工事中です。^^;
break, continue の話と、ライブラリの話と、例外の話がちょっぴりです。
------------------------------------------------------------
◆ vector[] の each と break の組み合わせ。
label A -- ラベル名称 A
{
tbl : 'a', 'b', 'c', ;
tbl.each(
func {
-- sys.put( in[0].type_name() ) ; -- string が表示される
in[0] == 'b' ? break A ; -- ここから
sys.put( "{in[0]}\n" ) ;
}
) ;
} ; -- ここへ飛ぶ(Aを抜ける)
-- a のみが表示される
◆
( 'a', 'b', 'c', ).each(
func {
-- sys.put( in[0].type_name() ) ; -- string が表示される
in[0] == 'b' ? break each ; -- ここから
sys.put( "{in[0]}\n" ) ;
}
) ; -- ここへ飛ぶ(each の外へ飛ぶ)
-- a のみが表示される
◆ continue文
C/C++言語のcontinueと同じ動きをする。
label のスコープを繰り返すか、最も内側のループを繰り返す。
comment_rex : Rex( '^#' ) ;
sys.each( -- 標準入力
func {
-- sys.put( in[0].type_name() ) ; -- string が表示される
comment_rex.match( in[0] ) ? continue each ; -- コメント # は除外
sys.put( in[0] ) ;
}
) ;
◆ vector[] の each と continue の組み合わせ。
label A -- ラベル名称 A -- ここへ飛ぶ
{
tbl : 'a', 'b', 'c', ;
tbl.each(
func {
-- sys.put( in[0].type_name() ) ; -- string が表示される
in[0] == 'b' ? continue A ; -- ここから
sys.put( "{in[0]}\n" ) ;
}
) ;
} ;
-- a a a a a ... が表示される(無限ループだねー)
◆
tbl : 'a', 'b', 'c', ;
tbl.each( -- ここへ飛ぶ(次の each へ)
func {
-- sys.put( in[0].type_name() ) ; -- string が表示される
in[0] == 'b' ? continue each ; -- ここから
sys.put( "{in[0]}\n" ) ;
}
) ;
-- a c が表示される
●4章 ライブラリ
class File, class FileBinary, class FileStatus
ファイル入出力(_FileIn, _FileOut, _FileAdd)
interface _StreamIn
{
out func each
{
in:func{ in:string, },
}
}
interface _StreamIn
{
out func put
{
in:string,
}
}
interface _FileIn
{
out func read_open
{
in:func{ in:_StreamIn, },
}
}
interface _FileOut
{
out func write_open
{
in:func{ in:_StreamOut, },
} ;
} ;
interface _FileAdd
{
out func add_open
{
in:func{ in:_StreamOut, },
} ;
} ;
class File
{
in_filename : string in[0] ;
use _FileOut ;
use _FileIn ;
use _FileAdd ;
} ;
class FileBinary
{
in_filename : string in[0] ;
use _FileOut ;
use _FileIn ;
use _FileAdd ;
} ;
◆ _StreamIn の each と break の組み合わせ。
break は C/C++言語の break とほぼ同じ動きをする。
label のスコープを抜けるか、最も内側のスコープを抜ける。
File( 'file.txt' ).read_open(
func {
-- sys.put( in[0].type_name() ) ; -- interface:_StreamIn が表示される
fp : in[0] ;
fp.each(
func {
-- sys.put( in[0].type_name() ) ; -- string が表示される
in[0] == "END\n" ? break each ; -- ここから
sys.put( "{in[0]}\n" ) ;
}
) ; -- ここへ飛ぶ
} ) ;
◆ ファイル読み込み
fp : File( 'file.txt' ) ;
-- sys.put( fp.type_name() ) ; -- class:File{in:string,} が表示される
fp.read_open(
func {
-- sys.put( in[0].type_name() ) ; -- interface:_StreamIn{} が表示される
in[0].each(
func {
-- sys.put( in[0].type_name() ) ; -- string が表示される
line : in[0] ;
sys.put( line ) ; -- 一行ずつ表示
}
) ;
}
) ;
上のコードを簡単に書くと
File( 'datafile.txt' ).read_open
{
-- sys.put( in[0].type_name() ) ; -- _StreamIn が表示される
in[0].each
{
-- sys.put( in[0].type_name() ) ; -- string が表示される
sys.put( in[0] ) ;
} ;
} ;
もっと簡単に書くと
File( 'datafile.txt' ).read_open().each{ sys.put( in ) ; } ;
では、なにも省略せずにすべて書いてみると…
fp : File( 'file.txt' ) ;
func read_open_callback
{
input : _StreamIn in[0] ;
func input_each_callback
{
one_line : string in[0] ;
sys.put( one_line ) ; -- 一行ずつ表示
} ;
input.each( input_each_callback ) ;
} ;
fp.read_open( read_open_callback ) ;
次のようにファイルの各行を配列に一括して読み込むこともできます。
tbl $ list[string] ;
File( 'datafile.txt' ).read_open().each{ tbl <<= in[0] ; } ;
sys.put( tbl.str() ;
また、次のようにすると、コマンドラインで指定したファイルをひとつづつオープンし、
それを順次読み込むことができます。
sys.arg.each(
func {
-- sys.put( in[0].type_name() ; -- string が表示される
File( in[0] ).read_open().each(
func { sys.put( in[0] ) ; }
) ;
}
) ;
これは、だいたい、次のような処理と同等になります。
sys.arg.size.each(
func {
-- sys.put( in[0].type_name() ; -- int が表示される
File( sys.arg[in[0]] ).read_open().each(
func { sys.put( in[0] ) ; }
) ;
}
) ;
◆ ファイル書き込み
ファイルに何かを書き込むには次のようにします。
File( 'datafile.txt' ).write_open(
func {
-- sys.put( in[0].type_name() ; -- _StreamOut が表示される
in[0] <<= "こんにちは\n" ;
}
) ;
では、なにも省略せずに書いてみます。
fw : File( 'datafile.txt' ) ;
func write_ok_callback
{
strm : _StreamOut in[0] ;
strm.push( "こんにちは\n" ) ;
} ;
fp.write_open( write_ok_callback ) ;
ファイルに追加書き込みするには次のようにします。
File( 'datafile.txt' ).add_open
{
-- sys.put( in[0].type_name() ; -- _StreamOut が表示される
in[0] <<= "こんにちは\n" ;
}
◆ 標準入出力
File() を用いて作成する他に、次の3つのハンドルがあらかじめオープンされています。
sys -- 標準入力 interface:_StreamIn ;
sys -- 標準出力 interface:_StreamOut ;
sys.stderr -- 標準エラー出力 interface:_StreamOut ;
sys : class System
{
use _StreamIn ;
use _StreamOut ;
out stderr : _StreamOut ;
}
標準入力から各行を読み込むには次のようにします。
sys.each{ sys <<= in[0] ; } ;
◆ バイナリ読み込み
例えば次のようなコードを用いる事により、ファイルをバイナリデータとして読み込む事ができます。
buff $ vector[ byte ] ; -- byte や bit もいるのかな?
FileBinary( 'binary.dat' ).read_open(
func {
fp : _StreamIn in[0] ;
in.each(
func {
buff <<= in[0] ;
}
) ;
}
) ;
buff.each
{
sys.put( "{02x:in[0]}" ) ;
} ;
●以下、おまけ(Pipe, Call, Rex)
◆ 外部コマンド実行
出力結果を外部コマンドに渡すこともできます。
次の例ではsys.put文の出力をnkfコマンドでJISコードに変換して出力しています。
Pipe( '/usr/local/bin/nkf -j' ).write_open
{
-- sys.put( in[0].type_name() ; -- _StreamOut が表示される
in[0] <<= "こんにちは\n" ;
} ;
また、外部コマンドの出力結果を読み取ることもできます。
次の例では、file.txt の中身を nkf コマンドでEUCに変換したデータを読み込みます。
Pipe( '/usr/local/bin/nkf -e file.txt' ).read_open().each(
func {
-- sys.put( in[0].type_name() ; -- string が表示される
sys.put( in[0] ) ;
}
) ;
単に外部コマンドを実行するだけであれば、次のようにします。
rtn_code : Call( '/usr/local/bin/nkf -e file.txt' ) ;
◆ 環境変数(sys.env)
sys.envという特別な連想配列変数を用いて環境変数の値を読み取ったり設定したりすることができます。
sys.env.type_name() -- 型は map[string..string]
sys.put( sys.env['PATH'] ; -- 環境変数PATHの値を表示する。
sys.env['TZ'] = "JST-9" ; -- 環境変数TZに値を設定する。
◆ コマンド引数(sys.arg)
コマンドラインからの引数を得るために、次の変数が使用できます。
sys.arg[0] -- 引数の配列(0番目はプログラム名称)
sys.arg.size -- 引数の個数
sys.arg[1] -- 最初の引数
sys.arg.type_name() -- 型は vector[string]
◆ シグナル(sys.sig)
シグナルはプログラムに送信される非同期のメッセージです。
例えば、強請終了を明示された時はSIGTERMシグナルが、
プロセス間の通信が切断された時はSIGPIPEメッセージが、
設定しておいた時刻になったらSIGALRMシグナルが、
プログラムに対して送信されます。
sys.sig['TERM'] = func { sys.put( 'sigterm!!' ); } ;
sys.sig.type_name() -- 型は map[string..func]
と宣言することにより、そのプログラムに SIGTERMシグナルが送信された時に、
sys.put( 'sigterm!!' ) ;
が実行されるようになります。
◆ 正規表現 Regular Expression (Rex)
-- 正規表現文字列 'ABCDEFG' が文字列'EFG'を含んでいたら
r : Rex( 'EFG' ) ;
r.match(
'ABCDEFG',
func {
-- sys.put( in.type_name() ; -- vector[string] が表示される
sys.put( "含んでいる.{in[0]}\n" ) ; -- ABCDEFG を表示
}
) ;
-- 文字列 'ABCDEFG' が文字列'EFG'を含んでいたら
Rex( 'EFG' ).match( 'ABCDEFG', { sys.put( "含んでいる.{in[0]}\n" ) ; } ) ;
上記の rex は「変数 xx の中に、EFGという文字列が含まれていたら」という意味になる。
正規表現の EFG の箇所には次のような表記を用いることができる。
A Aという文字
ABC ABCという文字列
A+ 1個以上連続したA(A, AA, AAA, ...)
A* 0個以上連続したA( , A, AA, AAA, ...)
. 1つの任意文字(A, B, C, ...) (\nを除く)
? 0または1つの任意文字( , A, B, C, ...)
^ABC ABCで始まっていたら
ABC$ ABCで終わっていたら
[ABC] A,B,Cのいずれか1文字
[A-Z] A〜Zまでのいずれか1文字
[A-Za-z0-9] A〜Z, a〜z, 0-9までのいずれか1文字
[^ABC] A,B.C以外の文字
[^A-Z] A〜Z以外の文字
A|B|C AまたはBまたはC
\nなど エスケープシーケンス文字の\nなど
(ABC) ABCという文字列。カッコ部分は後で参照可能です。
これらを組み合わせてたとえば「Vinoの変数名に用いることができる文字」は次のように表わされる。
^[A-Za-z_][A-Za-z0-9_]*$
正規表現中では下記の文字は特殊な文字(メタ文字)として扱われるので、
これらの文字を表わすには \+, \*, \?, ...のように表す。
+ * ? . ( ) [ ] { } | \
括弧(...)で囲んだ部分は、正規表現内では in[1], in[2], in[3], ...として参照できます。
1, 2, 3, ...は、正規表現中で括弧が現れる順番を示す。
^(...)\1 最初の3文字がもう一度繰り返す(ABCABCなど)
また、正規表現の外ではマッチの結果を次のように参照できる。
in[0], in[1], in[2], ... ( )の部分にマッチした文字列
in.last 最後にマッチした( )部分
in マッチした部分の文字列
in.head マッチした部分から前側の文字列
in.tail マッチした部分から後側の文字列
たとえば、次のように使用することができる。
Rex( '^([0-9][0-9]):([0-9][0-9]):([0-9][0-9])$' ).match(
time,
func {
hour : in[0] ;
min : in[1] ;
sec : in[2] ;
...
}
) ;
----
例外処理をどうするかという大きな問題が残っていましたね…。
うーん。
基本的に try-catch 構文は使うつもりが無いのです。
イメージとしては、sys と同じように
exception というグローバルなインスタンスがあって、
そこに
exception.throw( 'error' ) ; -- エラー
とこんな感じで投げると強制終了すると。
で、強制終了してほしくない場合は、あらかじめ
{
exception.catch( 'error', { ... } ) ;
... -- ここで起きた throw 'error' を拾う。
} ; -- で catch されたら引数の関数が実行された後、強制終了は免れて、
-- このスコープの外に飛んでくる。
としたいのだが…そんなことがライブラリでできるのか?ってことだよね。
もう少し良い案がないか考え中…。
{
exception 'ErrorMaggage' ; -- break 構文と同じように書く
...
catch -- dtor (デストラクタ)の構文と同じように書く
{
message : string in[0] ;
stack_trace : vector[string] in[1] ;
-- exception が実行されると catch まで飛ぶ
sys.put( message )
; -- ErrorMasage が表示される
}
...
exception 'ErrorMaggage' ; -- catch の後ろでも書ける
} ;
|
1章に戻ります。
Vino言語(1/4) (2/4) (3/4) (4/4)
last update 2003/03/22
since 2002/09/16
|
| やまざきのおすすめエレクトロニクス |
|
| やまざきのおすすめ本 |
|
| やまざきのおすすめDVD |
|
| やまざきのおすすめCD |
|
|