Dl は,Dレコードを操作するための言語である。 C,C++,Javaのような汎用のプログラミング言語ではない。 機能的にはawkが最も似ている。 ただしawkが行形式のテキストファイルを扱うのに対し, Dlは,Dレコードを扱う。 たとえば,入力ファイル中の特定のレコードに新しいフィールドを 加えたり,値を変えたり,フィールドを削除したりすることができる。
Dlはプログラミング言語としての一通りの機能は備えているが, 大きなプログラムを処理するものとして作られてはいない。 DedはDlのインタプリタで,コンパイラのように高速な処理はできない。 複雑な処理を行いたい場合にはperlやCなどでプログラムを書く方をすすめる。 Dlの典型的な使用法として想定されているのは,たとえば次のようなものである:
Ded IF txtlang == jpn OR txtlang == kor OR txtlang == chi THEN area = ea FI input-file.d
このコマンドは,"txtlang"フィールドの値が"jpn","kor"または"chi" のレコードについて,"area"フィールドの値を"ea"とする(追加または置き換え)。
Dコマンドのいくつかは,Dlで書くこともできる。たとえば:
Dtie -t ":" a,b c
は次のように書いても同じ結果となる。
Ded FIELD c = FIELD a . CONST ":" . FIELD b ";" FIELD a = FIELD b = "{" "}"
この二つの方法の本質的な違いは,DコマンドがDファイルの基本ファイル演算 を提供するのに対し,Dlは汎用のDレコード操作機能を提供する, という点にある。 もっと実際的な違いとしてはスピードがある。Dコマンドは特定のファイル演算 を高速化するための専用コードを書いてある。 これに対し,DedはDlを1ステップごとに解釈実行していくわけで 当然遅くなる。 適当なDコマンドが提供されている演算に対してはそのDコマンドを使用すること を推奨する。
Dlプログラムはふつうコマンド引数の並びとして書かれる。 これはUNIXのsedと似ているが,-eオプションは使わず, プログラムを直接コマンド引数として書いていく。 このほかに,Dlプログラムをテキストファイルに作って引き渡す方法もある。 詳細は下記記法の節参照。
Dlは,非常に単純化された文法を持っている。 多くのコンピュータ言語と異なり,Dlには「文」がなく, 「式」のみである。制御構造の"if"や"while"も Dlの「演算子」である。 さらに";"でさえ,C言語の","に似た演算子である。 式は「評価」される。これはDlのその式の部分を「実行」する と同義語である。
また,Dlはサブルーチンやマクロ機能を持たない。 このため,大規模で複雑なプログラムを書くのに適していない。
Dレコードではどんなフィールドもリピーティングフィールドとなりうる。 したがって,Dlのどの定数も変数も配列である。 Dlではどんな演算子もそれぞれの方法で配列に対して定義されている。 たとえば"+"(加算)はオペランドの要素の数によって異なる演算を行う。 Perl言語はスカラーと配列との2つのコンテキストで演算子の意味を変えているが, Dlは配列コンテキストのみしかない。
Dlのインタプリタには2つのコマンドがある。 DedはDlのフル処理系であるのに対し, Dselectは演算子に制限があり,出力レコードに変更を加えるような操作はできない。
Dedでは与えられたDlプログラムが,各入力Dレコードに対して 評価(実行)され,その後のカレントレコードが (フィールド数がゼロ:すなわちレコードが削除されたのでない限り) 標準出力に書き出される。 なお,outputによって途中で明示的にカレントレコードを出力することもできる。 ただしこの場合でも,Dedはプログラムの評価後書き出しを行なう。 書き出し後,Dedは次のレコードを読み込んで新しいサイクルに入り, 入力ファイルの終わりまでこれを繰り返す。
Dselectでは, 与えられたDlプログラムが各入力Dレコードに対して評価され, 結果の値が真であれば(ブール値評価参照) 入力レコードが標準出力に書き出され,そうでなければ書き出されない。 フィールドに対する代入や,強制出力はDselectコマンドでは許されない。 したがって,入力レコードが変更されたり増えたりすることはない。
Dlのプログラムはコマンド引数あるいは-fオプションで指定した ソースファイルから入力される。 Dlプログラムはワードからなる。 Dl演算子,定数,フィールド名,変数名およびその他のDl予約語は すべてワードとして記される。 Dlでは括弧もワードとして記される。 ワードは任意の長さの文字の列である。 入力をどのようにワードとして認識するかはコマンド引数からのプログラムと, ソースファイルからのプログラムで若干の違いがある。
コマンド引数からプログラムを入力する場合,各コマンド引数がDlのワードとなる。 ソースファイルから入力する場合, 空白文字(SPACE,TAB,改行,IDEOGRAPHIC SPACE=全角スペース,など)でワードを分ける。 ソースファイルのEND OF FILEは,改行文字として扱う。 空白文字以外では,REVERSE SOLIDUS(\),QUOTATION MARK(") およびAPOSTROPHE(')は,特殊な意味を持つ。 次に記すクォーティング方法はUNIXのshの仕様に基づくものである。
REVERSE SOLIDUS(\)を行の終わりにおくと行継続マークとなる。 行継続マークとそれに続く改行はワードから取り除かれる。 ただし,APOSTROPHE(')中ではこの限りでない。 上記以外のREVERSE SOLIDUS はAPOSROPHEやQUOTATION MARKの中におかない限り エスケープ文字となり,エスケープ文字自身はワードから取り除かれるが, 続く1文字が強制的にワードの一部となる。 エスケープ文字は通常,空白文字やQUOTATION MARK,APOSTROPHEまたは REVERSE SOLIDUSをワードの中に入れるために使われる。
QUOTATION MARK(")も特殊文字をDlのワードに含めるために 使われるいまひとつの方法である。 DlのパーザがQUOTATION MARKにであうと,その文字はワードに含まれないが, 対応するQUOTATION MARKの前までの文字がワードに取り込まれる。 QUOTATION MARKの中では上記の例外が2つある。 REVERSE SOLIDUS,QUOTATION MARKの組(\")は1文字の QUOTATION MARKとなる。これはQUOTATION MARKの中でQUOTATION MARKを使うためにある。 もう一つの例外は,改行前のREVERSE SOLIDUSで行継続マークとなる。 REVERSE SOLIDUSも改行もワードには取り込まれない。 QUOTATION MARK中のほかのREVERSE SOLIDUSは通常の文字としてワードの一部となる。
APOSTROPHE(')もクォーティングを行う。 これはQUOTATION MARKより強く,DlパーザがAPOSTROPHEにであうと, 次のAPOSTROPHEの前までの文字がワードに取り込まれる。 これには例外がなくREVERSE SOLIDUSも,改行文字も通常の文字としてワードに取り込まれる。 APOSTROPHEをワードの中で使うには前述のREVERSE SOLIDUSによるエスケープか, QUOTATION MARKによるクォーティングをしなければならない。
Dlソースファイルでのクォーティングされたワード例
one\ word | one word |
"one word" | one word |
'one word' | one word |
one" "word | one word |
o\ n" "e' '' '"w o ""r d" | o n e w o r d |
\o\n\e\ \w\o\r\d | one word |
one\ word |
oneword |
\\\"\' | \"' |
"\"\\'\"" | "\'" |
"one\ word" |
oneword |
'"\"' | "\" |
'one word' |
one word |
次のワードはDlの中で予約語である。
! != !~ $& $' $. $n $? $` % && ( ) * ** + , - -- . .. / ; < <= <> = == =~ > >= @_ [ ] { || ABS AND ATAN AVG BY CAPS CAT CODESET CONST COS COUNT CURREC DIVIDEDBY DO DONE ELIF ELSE EPILOGUE EQ EXISTS EXIT EXP FI FIELD FIELDS FILENAME FNR FOR GE GREP GT IF IN INCL INT JOIN LE LENGTH LIKE LOCALE LOG LOG10 LT MATCH MATCHn MAX MIN MINUS MOD NE NOT NR NUM OR OUTPUT PLUS POSTMATCH POWER PREMATCH QX REC# S SG SIN SPLIT SPRINTF SQRT SSCANF STATIC STATUS STR SUBST SUBSTG SUBSTR SUM TAN THEN TIMES TOLOWER TOUPPER UNLIKE VAR WHILE
次の語は,限られた条件の下で予約語となる:
Dlのワードはすべて大小文字の区別がある。 ("IF"は予約語であるが,"if"や"If"は予約語ではない)。
コメントはワード/*で始まり,ワード*/で終わる。 C言語のコメントと違い,/*も*/も空白文字で 区切らなければならない。 /*COMMENT*/はコメントにならない (予約語ではない1語となる)が, /* COMMENT */はコメントとなる。
Dlのトークンには,フィールド名,, 定数,変数,静的変数, 特殊変数,演算子,括弧 またはエンドトークンがある。 各トークンは1ワードまたは予約語で始まる2つ以上のワードからなる。
次にDlの文法(簡略化したもの)を示す。
フィールド名 |
定数 |
変数と静的変数 |
特殊変数 |
括弧 |
エンドトークン |
予約語FIELDに続く1語がフィールド名トークンである。 たとえば:
FIELD a
は,フィールド名"a"のトークンである。 同様に,
FIELD FIELD
はフィールド名"FIELD"のトークンである。 この場合,2番目のワードFIELDは予約語ではなくフィールド名であり, 最初のワードFIELDが予約語である。
特殊な場合として,プログラムの先頭あるいは次のトークンの次に ある1ワードで,予約語でないものは,フィールド名トークンと解釈する。
(
; , && || !
IF THEN ELIF ELSE WHILE DO
ABS AND ATAN AVG CAPS CAT COS COUNT EXISTS EXP INT LENGTH LOG LOG10 MAX MIN NOT OR SIN SQRT SUM TAN TOLOWER TOUPPER
次の例で,2番目のワード"a"は,トークンEXISTSの次にあるため フィールド名トークンと解釈される。
EXISTS a
フィールド名トークンを評価すると, カレントレコードでそのフィールド名を持つフィールドの値となる。 同じフィールド名のフィールドが2つ以上あると,値は配列となる。 カレントレコードにその名前のフィールドがないと値は空値となる。
フィールド名トークンの値は文字列である。 しかし,たとえば比較演算をする場合数値として扱いたい場合も あるだろう。数値限定子をつけることにより フィールド名トークンを評価した結果を数値として取り出せる。 フィールド名の後ろにCOLON(:)と文字"n"をつけると 数値限定子となる。 たとえば:
FIELD seq
は文字列として評価されるので"10"は"9"より小さくなる。 しかし,
FIELD seq:n
は数値として評価されるため,"10"は"9"より大きくなる。
定数の形式には2通りある。 1つは予約語CONSTを使用する。 予約語CONSTに続く1語が定数トークンとなる。 配列値を表すには,最初の定数に続いてCONST と値とを繰り返す。
CONST a
CONST a CONST b
もう一つの方法は文字BRACEを使う。 LEFT BRACE({)1文字のワードの後ろで RIGHT BRACE(})1文字のワードの前にあるワードは 全体で定数トークンをなす。 このBRACEの中ではDlの予約語は, その意味を失って,定数値となる。
{ a }
{ a b }
このBRACEの中ではLEFT BRACEも単に定数となる。 次の例:
{ { } }
は,4番目の語でシンタクスエラーとなる。 3番目の語で定数トークンが終わっているためである。
BRACEによる定数記法では,RIGHT BRACEの1文字だけからなる 定数を表記することはできない。 次のようにCONSTによる記法を使用すること。
CONST }
空値を表記するには,次のように書く。
{ }
CONSTによる記法では空値を表記する方法はない。
特殊な場合として,次のトークンに続く1ワードで,予約語でないものは 定数トークンとして解釈する。
!= !~ % * ** + - . .. /
< <= <> = == =~ > >= [
BY DIVEDBY EQ GE GREP GT IN INCL JOIN
LE LIKE LT MINUS MOD NE NUM PLUS POWER
QX S SG SPLIT STR SUBST SUBSTG SUBSTR TIMES UNLIKE
たとえば,
FIELD 1 == 1
この例では,2番目のワード"1"はフィールド名で4番目のワードは 定数である。
定数は,そのままで文字列値となる。 たとえば演算子"+"の前後のように数値が必要な場合, Dlインタプリタはオペランドを自動的に変換する。 定数を直接数値として評価する方法はない。 ただし,NUM演算子により変換することができる。
変数トークンは予約語VARとそれに続く1ワードからなる。 同様に,静的変数トークンは予約語STATICとそれに続く1ワードからなる。 FORトークンの直後に限り予約語VARを省略することができる。 予約語STATICを省略することはできない。
変数および静的変数は,値を保持することができる。 この2つの異なりは,その寿命にある。 変数の寿命は,プログラムの1実行サイクルで, 新しいレコードが入力ファイルから読み込まれて, プログラムを実行する前に変数はすべて初期化される。 これに対し静的変数は,Dコマンドの実行期間である。 この意味で静的変数が,一般的な「変数」に近く, 変数は,ループのインデクスなどのローカルなものである。 (FOR演算子は,変数をインデクスとして使う。)
変数および静的変数のスコープは常にプログラム全体である。
変数または静的変数を評価すると, そこに最後に代入された値となる。 値を1度も代入していない変数または静的変数 を評価すると空値(要素数0の配列)となる。
構文的には特殊変数は予約語1語からなる。 意味的には,いくつかはperlの予約変数のようであり, いくつかはプログラミング言語の文に対応する。
特殊変数を評価すると,プログラムの実行環境に関する 値を返したり,プログラムのある機能を実行したりする。 この意味で「特殊変数」という名は誤解を生みやすいものもある。 しかし,構文上の単純さを確保するためこれらはすべて 特殊変数トークンのもとにまとめられている。
これらの変数は,FIELDSをのぞけば値を代入できない。 FIELDSは,カレントレコード全体を表し, 代入演算によって 値を変えることもできる。
個別の特殊変数に関しては, 演算子および関連特殊変数の節に記述する。
LEFT PARENTHESIS (()およびRIGHT PARENTHESIS ()) は,通常のプログラミング言語と同様に, 演算の順序を変えるために用いる。
ちなみに,CURLEY BRACKETS ({ })は, 定数を示して,式のグルーピングには関係しない。 また,SQUARE BRACKETS ([ ])は, 添え字演算である。
ワード"--"は,明示的にプログラムの終わりを示すのに使われる。 通常,このトークンを使う必要はない。 Dlのパーザは,トークンとして認識できないワード が出てくると自動的にエンドトークンを挿入するためである。 エンドトークンを必要とするのは, 入力ファイル名(の最初のもの)がDlの予約語と 一致する場合だけである。 たとえば:
COUNT a LT 2 -- LT
この例で,3番目の引数LTは,Dlの 予約語で,比較演算子"less than"である。 これに対し6番目の引数LTは,入力ファイル名である。 この場合,エンドトークンが必須となる。 これがないと,ファイル名が演算子と解釈されてシンタクスエラーをおこす。 (もし入力ファイル名が小文字の"lt"であれば エンドトークンは必要ない。)
Dlのプログラムには文がなく式のみであるため, プログラムは常に「評価」される。これは「実行される」と同義である。 この点ではLISPに似ているともいえよう。
各構文要素(フィールド名,変数等)の評価については, 構文要素 の各節に記述されている。 また,演算子を含む式が評価されるとき, 演算子および関連特殊変数 の各節に記述される演算が実行される。
Dlの演算結果は,文字列値または数値の配列となる。 これをブール値として評価する場合の規則は次の通り。
この規則では奇妙なことも起こりうる。 たとえば,数値{ 0 }は,FALSEであるが, 文字列値{ 0 }は,TRUEとなる(規則1)。 しかしながら,多くの場合この規則は実用的に働く。
このブール値評価規則は,演算子がブール値を必要とする場合一般に 適用される。 たとえば,論理AND演算子はその左辺をブール値として評価する必要があり, 左辺にこの規則を適用する。
以下の例では,FIELD,CONST,VAR は可能な場合,省略している。 これは,Dlパーザの機能を示すためにそうしているだけで, そのような使用を推奨しているわけではない。 予約語の省略は,しばしば気がつきにくいエラーのもととなるため, むしろなるべく,省略に頼らない書き方が勧められる。
フィールド"lang"の値が"jpn"か?
lang == jpn
フィールド"yr"の値が数値2003より小か?
yr:n LT 2003
各入力Dレコードは1つだけのフィールド"l"を持っていて, その値はあるテキストファイルの1行となっている。 このテキストファイルで,パラグラフは空白行で区切られている。 次の例は,この各レコードにパラグラフの1連番号をフィールド"P" として追加する。
IF NR == 1 THEN STATIC P = 1 FI ;
IF l =~ " *" THEN STATIC P = STATIC P + 1 FI ;
FIELD P = STATIC P
Dtie -t / y,m,d ymdと同じことをDedで行う。 (ただし,フィールド"y","m","d"は同数の要素を持っていること。)
ymd = FIELD y . CONST / . FIELD m . CONST / . FIELD d ;
y = FIELD m = FIELD d = { }
perlのgrep同様の演算。 値が空文字列のフィールドを除去。
FOR i IN COUNT FIELDS - 1 .. 0 DO
IF FIELDS [ VAR i ] LIKE "^[^:]*:$" THEN
FIELDS [ VAR i ] = { }
FI
DONE
上例と似ているが,添え字自体が配列を利用した例。 Dproj fooと同じことをDedで行う。
FOR i IN 0 .. COUNT FIELDS - 1 DO
IF FIELDS [ VAR i ] =~ ^foo: THEN
VAR f = VAR f , VAR i
FI
DONE ;
FIELDS = FIELDS [ VAR f ]
MIYAZAWA Akira