## ## WML Macros ## Copyright (c) 2000 Denis Barbier, All Rights Reserved. ## =head1 名前 WML Macros - 強力なWMLマクロを書く =head1 説明 このチュートリアルはWMLマクロを書くための手引である。巧妙なマクロを書く ためには、初心者が初めの雛型を書くための、役立つヒントが必要である。 このドキュメントを最高に活用するためには、WMLが私用する個々の段階のド キュメントを読んでおく事が望ましい。 以下の例文は次のようにコンパイルするものとする wml -q -p123 test.wml ほとんどの場合、F パスだけを通せるが、しかし下のラインはより 一般的である。 =head1 導入 =head2 定義 これらの定義はこの文章で使われるものである。私は詳細にはこだわらないので、 W3Cの定めるものと多少違うかもしれない。 =over 2 =item * I は大括弧で囲まれるテキストの部分である。例えば、 =item * I タグは I を開始するタグである(下記参照)。 左括弧、エレメント名、オプションである I (下記参照)、 そして右括弧から構成される。以下のこれらは全て開始タグである: =item * I タグは I の終了タグである(下記参照)。 左括弧、スラッシュ、エレメント名、そして右括弧から成る。 このタグは属性を持てない。 =item * I 要素は文章の基本的な単位であり、開始タグと終了タグの組から成る。 Click here =item * I ボディ要素は開始タグと終了タグの間に挟まれるテキストの部分である。 上の例では、C という名前の一つの要素がある。そしてそのボディは C である。 =item * I は要素をより柔軟に扱うためのパラメータである。 属性は、開始タグに置かなければならない。要素はどんな属性でも持てる、 そしてそれは一つ以上の空白、タブ、または改行により分割される。 要素によっては、必須の属性と、オプションの属性が定義されている。 Logo この C 要素は3つの属性を持つ =item * I は終了タグの無い要素である。 =item * I は、開始タグと終了タグを持つ。 =back =head2 ファーストコンタクト 基本的に全てのマクロ定義は Cdefine-tagE> で行なわれる。 これは普通の例である: Input: 1| 2| bar 3| 4| Output: 1| 2| 3| bar 4| この普通の例にはいくつかの興味深いポイントがある: =over 2 =item * 改行は保存され、同じ行が入力と出力に見られる。後で、ちょっと空白文字 について詳細に考えてみよう。 =item * タグ名は、大文字と小文字の区別をしない。 =back =head2 単純タグについて HTMLにおける単純タグは以下のような終了タグを持たない要素である。
しかし、XMLでは単純タグは次のどちらかの方法で書かれなければならない:


すなわちボディ無しの複合タグのようにするか、または開始タグの後にスラッ シュを加えるかのどちらかになる。はじめのものはWMLで動かず、その上HTML ブラウザを混乱させるかもしれないので、避けなければならない。そこで、 あなたはこの後置スラッシュを加えるかどうかを選ばなければならない。 WMLはスラッシュがあっても無くてもどちらでも動作する。 この文章では、新しいXHTML規格に従うために、常に後置スラッシュを使用す る。これは私には好ましい書き方だが、他人はこの後置スラッシュ無しでまた 続けるかもしれない。あなたは、どの文法に従いたいかを決めよう。 一方、HTMLブラウザはXHTML構文により混乱するかもしれないので、出力した テキストにはこの後置スラッシュを含まない。これは一見矛盾している。 しかし、このアプローチにより我々の入力ファイルは将来のXHTMLツールで処 理できるように準備ができている。そして、我々がXHTMLのためのページを生 産するためには、適切なフラグでWMLを実行するだけである。 =head1 新しいタグの定義 既知の要素が入力テキストから見つかる度に、取り除かれてテキストで置き 換えられる。その後、その中に他のマクロを含む場合にそなえ、この置き換 えたテキストはスキャンされる。 全てのユーザマクロは C 要素で定義される。その最初の属性は 定義されるマクロの名前である。そして、そのボディはこのマクロの代りに 挿入されるテキストである。 単純な例を見てみよう: Input: 1| http://www.engelschall.com/sw/wml/ 2| Output: 1| 2| http://www.engelschall.com/sw/wml/ 複合タグを定義する事は難しくない、そのためには C 要素 を追加する。 Input: 1| bar 2| baz Output: 1| 2| bar =head2 特別なテキスト いくつかの文字列は、マクロのカスタムのために特別な意味を持ち、特定の値と置きかえられる。 =over 2 =item %0 %1 ... 属性: C<%0> は、はじめの属性である。同様に C<%1>、という風に続く。 =item %name マクロ名 =item %attributes 空白で区切られた属性のリスト =item %body マクロのボディ部分(複合タグのみ) =item %# 引数の数 =item %% パーセント記号 =back Input: 1| 2| Macro name: %name 3| Number of arguments: %# 4| First argument: %0 5| Second argument: %1 6| All arguments: %attributes 7| Body macro: %body 8| 9| 10| And the body 11| goes here. 12| Output: 1| 2| 3| Macro name: foo 4| Number of arguments: 3 5| First argument: Here 6| Second argument: are 7| All arguments: Here are attributes 8| Body macro: 9| And the body 10| goes here. 11| 12| これらの特殊文字はまた修飾文字により変更されるかもしれない。それは、 パーセント記号の後に置かれる一文字以上の文字列である。修飾文字には 次のようなものがある: =over 2 =item U (展開しない) 文字は置きかえられるが、展開されない。(expansionについての章を見よ) =item A (配列) リストは空白のかわりに改行で区切られる。この修飾文字は C<%attributes> だけで働く。 Input: 1| 2| First argument: %A0 3| All arguments: %Aattributes 4| Body macro: %Abody 5| 6| 7| And the body 8| goes here. 9| Output: 1| 2| 3| First argument: Here 4| All arguments: Here 5| are 6| attributes 7| Body macro: 8| And the body 9| goes here. 10| 11| =back 注意点としては、マクロが読み込まれる時にこれらのシーケンスが置き換えら れ、その後に置き換えられたテキストが再びスキャンされる事である。これは 以下のような構造を書かないためにも、たいへん重要である。 %body /> 内側の C<%body> は、CifE> 要素で置きかえられる。 C<前に>スキャンされる。 それは予想できない結果を引きおこすかもしれない。次のようにするとまだ ましである。 "%body" /> しかし、C<%body>が二重引用符を含んでしまうと、トラブルの元となる。 そのため、引数のうち一つが特別なシーケンスを含んでいる時、決して CifE> (と派生物)をつかってはならない。その代りとしては > %body =head1 空白文字 これまでの例では、多量の使わない改行を表示してきた。これらを消去する 方法がある。一つの方法としては pass 1 で、バックスラッシュを行末に置くと、 行末の改行を捨てる。 Input: 1| \ 2| bar\ 3| \ 4| Output: 1| bar 別の手法としては、マクロ定義に C を加える。例えば、 1| 2| bar 3| 4| Output: 1| 1| bar 1行目の改行は C/define-tagE> の後の改行なので捨てられない。 この属性が使われると、全ての頭と終りの空白、角括弧の外側の改行は全て取り 除かれる。 =head1 属性つきのマクロ WMLの素晴しい機能として、任意の属性が扱える事がある。マクロで属性を 扱う方法はいくつもある。ここでは全てのWMLモジュールで使える標準的な 方法について扱う。 HTMLの文法では C は変数の割当てのみなので、属性は変数で 格納される。ローカル変数を扱うために、プッシュ/ポップのメカニズムが利用 される。次の例を見てみよう。 Input: 1| 2| 3| 4| 5| "" 6| /> 7|
8| 9| 10| 11| Output: 1| 2| http://www.w3.org/ CpreserveE> タグは、スタックの先頭に引数で指定された変数を プッシュし、そしてその変数をクリアする。この変数は Cset-var %attributesE>で割りあてられないと空白になる。 Cresstoreタグは、スタックの先頭の値をポップして、引数で指定さ れた変数にセットする。 HTMLのいくつかの属性では値が無いのが正当なものがある。 そういった属性は検出されるかもしれない Input: 1| #use wml::std::info 2| 3| 4| 5| 6| 7| 8| " "" > 9| " ""> 10| 11| 12| 13| 14| 15| Output: (only non-blank lines are printed) Test page 1 Test page 2 =head1 クォートとグループ化 で、引用符または二重引用符で囲むことにより、いくつかの言葉を含んでいる 属性を指定する事ができる。WMLは二重引用符だけを理解する。 1| \ 2| Number of arguments: %# 3| First argument: %0 4| \ 5| \ 6| \ Output: 1| Number of arguments: 3 2| First argument: Here 3| Number of arguments: 2 4| First argument: Here are =head1 拡張 この章では、全ての例は以下のコマンドラインで処理される wml -W2,-dat -q -p123 そして、C ではじまる全ての出力行は、このデバッグフラグにより生成 される。 この章は理解が難しいだろう。しかしこれらの概念がたまに(大部分はWMLチュー トリアルのためにマクロを書くとき)必要なだけなので、わからなくてもWMLを 使う事はできる。 標準では、タグがスキャンされる時、マクロは展開される。 Input: 1| %attributes\ 2| baz\ 3| Output: 1| trace: -1- 2| trace: -1- 3| trace: -2- 4| trace: -1- 5| name=baz まず、 CbarE> マクロが最初に処理される、そして CfooE> が処理される (ハイフンの間の行が、処理されるレベルを示す)。 実際にはWMLは C という名前を見付ける。これはマクロ名なので、属性が探索される。 属性がスキャンされると、それは CbarE> を見付ける。このマクロには属性 が無いので、CfooE> 属性のスキャンが終了すると、該当するテキストで置 き換えられる。 考察してみよう Input: 1| %attributes\ 2| baz\ 3| Output: 1| trace: -1- 2| trace: -1- 3| trace: -2- 4| trace: -1- > 5| trace: -1- 6| name=baz C 属性は、このマクロの属性がスキャンされると、展開 されないという事をWMLに告げる。すると、最初の4行は理解しやすくなる。 しかし、CfooE> の後で展開されなければ name= このテキストは再びスキャンされ、CbarE> は展開される。 この展開を禁ずるには、B<特別なテキスト>の章で説明される C 修飾文字を使う。 Input: 1| %Uattributes\ 2| baz\ 3| Output: 1| trace: -1- 2| trace: -1- 3| trace: -2- 4| trace: -1- > 5| name= =head1 MP4H と EPERL を混ぜる 準備をしてから、F と F を混ぜる方法を見てみよう。以下の章は ちょっとトリッキーである。手っ取りばやく使用するならば直接 B を見る事。 =head2 入れ子の ePerl マクロは動作しない このマクロを見てみよう: <: print "attrs:%attributes"; :> ぱっと見には、次のようになる。 attrs:%attributes しかし、マクロが入れ子にされると、何が起きるだろうか? Input: 1| /> Output: 1| attrs:attrs:0 きちんと動作する! その一方、 Input: 1| /> Output: 1| ePerl:Error: Perl parsing error (interpreter rc=255) 2| 3| ---- Contents of STDERR channel: --------- 4| Backslash found where operator expected at /tmp/wml.1183.tmp1.wml line 5| 10, near ""attrs:<: print attrs:0; print "\" 6| (Missing operator before \?) 7| syntax error at /tmp/wml.1183.tmp1.wml line 10, near ""attrs:<: print 8| attrs:0; print "\" 9| Execution of /tmp/wml.1151.tmp1.wml aborted due to compilation errors. 10| ------------------------------------------ 11| ** WML:Break: Error in Pass 3 (rc=74). ふぅ、なにか間違っているようだ。 pass 2 の出力はこうなる 1| <: print "attrs:<: print attrs:0; :>"; :> そして、ePerl コマンドはネストできないので、エラーとなる。 (もしpass 2の出力を見て理解できないなら、以前の章を読みなおしましょう) この例は単純なものであり、回避方法も単純である (Cshow-attr-okE> を代りに使えばよい)。しかし、こうやって追跡するのが難しい場合も多い。 例えば、WMLのモジュールで定義されたマクロを入れ子で使う時、ePerl コード があるかどうかはわからない。 =head2 まずは問題解決へ挑戦 問題は、この文章によれば ePerl コマンドが入れ子にできないという事 である。そこで、まずは入れ子のレベルを数え、そして外部の時だけ ePerl のデリミタを表示してみよう。 Input: 1| \ 2| 3| 4| 1 />> 5| <: %body :> 6| 7| 1 />> 8| %body 9| 10| 11| \ 12| \ 13| $a += 1; %body\ 14| \ 15| 16| <:= $a :> Output: 1| 2| 3 別の例を見てみよう(1-11行には変更無し) Input: 12| 13| 14| $string = q|%body|; $string =~ s|%0||g; print $string; 15| 16| \ 17| Hello this is a test Output: 1| Hllo this is a tst 以前定義したように、Cremove-letterE> タグを入れ子にすると、この ようになってしまう: Input: 17| \ 18| Hello this is a test\ 19| Output: 1| ePerl:Error: Perl parsing error (interpreter rc=255) 2| 3| ---- Contents of STDERR channel: --------- 4| Bareword found where operator expected at /tmp/wml.1198.tmp1.wml 5| line 10, near "q|$string = q|Hello" 6| syntax error at /tmp/wml.1198.tmp1.wml line 10, near "q|$string = 7| q|Hello this "syntax error at /tmp/wml.1198.tmp1.wml line 10, near ";|" 8| Execution of /tmp/wml.1198.tmp1.wml aborted due to compilation errors. 9| ------------------------------------------ 10| ** WML:Break: Error in Pass 3 (rc=74). わかりやすくするためにはじめの2 passだけを実行して、どのような入力が ePerl に送られるかを見てみよう: prompt$ wml -q -p12 qaz.wml <: $string = q|$string = q|Hello this is a test|; $string =~ s|e||g; print $string;|; $string =~ s|s||g; print $string; :> 期待としては ePerl のデリミタはセンテンスのまわりだけに置かれて、入れ子 にはならない。しかし、C<%body> という指示語が ePerl コードと置き換えら れたので、そうはならない。 これらの特別な言葉(C<%EdigitE>, C<%body>, C<%attributes>, ...) が使われると、置き換えテキストが ePerl コマンドを含まない事が保証でき ないため、ePerlのデリミタの範囲に含まれると、トラブルとなる。 =head2 wml::std::tags モジュールにより定義されるマクロ F(3) モジュールは、入れ子にされた ePerl コマンドを使うため の方法を提供する。前の例はこのように書けるだろう Input: 1| #use wml::std::tags 2| 3| 4| 5| %body 6| %0 7| $string =~ s|$letter||g; 8| 9| 10| \ 11| \ 12| Hello this is a test\ 13| Output: ...61 empty lines... 62| Hllo thi i a tt 63| 64| これが働く方法を解説しようとすると、この文章で扱う方法を越えてしまう。 ここでは F モジュールが提供するコマンドとその使用法に 集中しよう。下のリストの擬似perlコマンドは、マクロと同等の機能を持つ。 =over 2 =item このマクロは、入れ子のレベルにより全部異なる、Perlの変数に展開される。 $perl_var =item string この複合タグは、その内容を表示する。 print qq(string); =item この単純タグは、その属性を表示する。 print string; =item Cperl:varE>変数を表示する。 print $perl_var; =item value Perlの変数を割当てる。もし属性が無ければ、値は Cperl:varE> に割当てられる。 $variable = qq(value); =item value Perlの変数を割当てる。もし属性が無ければ、値は Cperl:varE> に割当てられる。 $variable = q(value); =back =head2 マクロの使用法 問題に解決法があるという事を知った今、その確実な方法を学びたいだろう。 それには二つの鉄則がある: =over 2 =item 1 Perl分の中で特殊文字 (C<%EdigitE>, C<%body>, C<%attributes>, ...) を決して使わない事。 =item 2 Perlの C 文とその派生物も決して使わない事。 =back まず、これを $var1 = qq|%body|; $var2 = q|%body|; こう置き換える %body %body そして次に print $string; print "\"$alt\""; こうする $alt =head2 例 Example 1: C の単純なバージョン 入れ子でないバージョン: <: { my $src = '%0'; my $lowsrc = $src; $lowsrc =~ s|\.([^.]+)$|.lowsrc.$1|; system("convert -monochrome $src $lowsrc"); print "lowsrc=\"$lowsrc\""; } :> 入れ子バージョン: { my $src; %0 my $lowsrc = $src; $lowsrc =~ s|\.([^.]+)$|.lowsrc.$1|; system("convert -monochrome $src $lowsrc"); lowsrc="$lowsrc" } 最初の変更(C<$src>への割当て)は、属性にePerlコマンドを許可する。そして、 第二の変更(出力結果)は、このマクロがePerlコマンドに現れるのを許可する。 見ての通り、これはかなり素直な方法である。WMLモジュールが書かれた方法 がわかるかもしれない。 これまでの例や定義では、出力は全て標準出力に対しておこなわれた。 しかし、時々ファイルハンドルに対して出力したい。C を例にしてみよう 入れ子でないバージョン: <: { my $options = qq|%attributes|; my $tmpfile = "/wml.table.$$.tmp"; local (*FP); open(FP, ">$tmpfile"); print FP "<" . "wwwtable $options>\n"; print FP <<'__XTABLE__EOT' %body __XTABLE__EOT ; print FP "<" . "/wwwtable>\n"; close(FP); open(FP, "$WML_LOC_LIBDIR/exec/wml_aux_freetable -w $tmpfile|"); local ($/) = undef; print ; close(FP); unlink("$tmpfile"); } :> 入れ子バージョン: { my $tmpfile = "/wml.table.$$.tmp"; my $options; %attributes; 1 />> local *FH_XTABLE; open(FH_XTABLE, ">$tmpfile"); %body # we cut here to change filehandle 1 />> print FH_XTABLE ; close(FH_XTABLE); open(FH_XTABLE_IN, "/exec/wml_aux_freetable -w $tmpfile |"); local ($/) = undef; # The asterisk below prevents expansion during pass 2 and is # removed after this pass. = <*FH_XTABLE_IN>; close(FH_XTABLE_IN); unlink("$tmpfile"); } ファイルハンドルは、Cタグの属性により定義される。以降の Cperl:printE> への呼出しは、全てファイルハンドルに対し出力される。 =head1 AUTHOR Denis Barbier barbier@imacs.polytechnique.org =cut ##EOF##