(元記事は http://www.cs.bu.edu/~hwxi/ATS/DOCUMENT/atsdocman.html です)
atsdoc コマンドは ATS1 のユーティリティで、texting function calls を含むテキストファイルをそれらの呼び出しをその返り値で表わされる文字列で置換します。このユーティリティは ATS の基本的なプログラミングをできる人に向けて作られています。
texting function calls (TFC's) の構文は次のようになります:
funarg ::= DSTRING | SSTRING | INTEGER | ID | funcall funarglst ::= /*empty*/ | funarg | funarg "," funarglst funcall ::= "#" ID {funres} "(" funarglst ")" | "#" ID {funres} "{" funarglst "}" funres ::= "[" ID "]"このとき DSTRING と SSTRING はダブルクオートとシングルクオートの文字列をそれぞれ表わします。 また INTEGER は基数10の整数を、ID は ATS における有効な識別子を表わします。 例えば、次のテキストは構文として有効な TFC's です:
#fact(10) #timestamp[NOW]() #foo("#Hello("world")") #foo("#Hello('world')") #bar(#version(), 100) #foolst{itm1, itm2, itm3}
TFC's を含むテキストを表わすのに atext という単語を使うことにしましょう。ファイル foo.atxt の内容が次のようだとします:
Author: #author() Time of the last modification: #timestamp[NOW]()
もし次のコマンドを実行したなら:
atsdoc --outcode foo_atxt.dats --outdata foo_atxt.txt -i foo.atxtすると2つのファイル foo_atxt.dats と foo_atxt.txt が生成されます。また、次のコマンドを実行しても同じ結果が得られます:
atsdoc --outcode foo_atxt.dats -i foo.atxt > foo_atxt.txt
foo_atxt.dats の中は次のようになるでしょう:
(* foo.atxt: 10(line=1, offs=10) -- 18(line=1, offs=18) *) val __tok1 = author() val () = theAtextMap_insert_str ("__tok1", __tok1) (* foo.atxt: 51(line=2, offs=33) -- 67(line=2, offs=49) *) val NOW = timestamp() val () = theAtextMap_insert_str ("NOW", NOW)識別子 __tok1 の名前は自動的に生成されており、識別子 NOW の名前は入力から取ることに注意してください。foo_atxt.dats コメントで書かれている位置情報は主にデバッグに使います。
foo_atxt.txt の中身は次のようになるでしょう:
Author: #__tok1$ Time of the last modification: #NOW$foo_atxt.txt におけるそれぞれのトークンは、識別子を '#' と '$' で囲まれたものであることに注意してください。
この後の工程では foo_atxt.dats を、foo_atxt.txt 中のトークンをなんらかのテキストで置換されたテキストファイルを生成する実行可能バイナリへコンパイルすることになります。けれども、foo_atxt.dats には main 関数がありません。また、関数 author と timestamp も有効ではありません。foo.atxt に適切な ATS ソースコードを組み込むことで、これらの問題を解決できます。
次のような内容のファイル foo2.atxt を考えてみましょう:
%{ // dynload "libatsdoc/dynloadall.dats" // staload "libatsdoc/SATS/libatsdoc_atext.sats" // %} %{ fn author () = atext_strcst"John Doe" %} %{ staload UN = "prelude/SATS/unsafe.sats" staload TIME = "libc/SATS/time.sats" fn timestamp (): atext = let var time = $TIME.time_get () val (fpf | x) = $TIME.ctime (time) val x1 = sprintf ("%s", @($UN.castvwtp1(x))) prval () = fpf (x) val x1 = string_of_strptr (x1) in atext_strcst (x1) end // end of [val] %} Author: #author() Time of the last modification: #timestamp[NOW]() %{ implement main () = fprint_filsub (stdout_ref, "foo2_atxt.txt") %}次のコマンドラインを実行すると、特別な記号 '%{' と '%}' で囲まれたテキストは foo2_atxt.dats 中にコピーされます:
atsdoc -do foo2_atxt.dats -i foo2.atxt > foo2_atxt.txt関数 fprint_filsub は呼び出されると、foo2_atxt.txt 中のトークンをテキストで置換します。
これで foo2_atxt.dats を foo2 にコンパイルして、foo2 の実行結果を foo2.output にダンプすることができるようになります:
atscc -o foo2 foo2_atxt.dats -latsdoc ./foo2 > foo2.output予想通り、foo2.output の内容は次のようになるはずです:
Author: John Doe Time of the last modification: Wed Aug 24 20:31:59 2011
上記で示した関数 author と timestamp は文字列を返していません。その代わりに、libatsdoc/SATS/libatsdoc_atext.sats で宣言された atext 型の値を返します:
datatype atext = // | ATEXTnil of () // empty text // | ATEXTstrcst of string // string constants | ATEXTstrsub of string // strings containing marked tokens // | ATEXTapptxt2 of (atext, atext) // text concatenation | ATEXTappstr2 of (string, string) // string concatenation // | ATEXTapptxt3 of (atext, atext, atext) // text concatenation | ATEXTappstr3 of (string, string, string) // string concatenation // | ATEXTconcatxt of atextlst // text concatenation | ATEXTconcatxtsep of (atextlst, atext(*sep*)) // text concatenation with separator // end of [atext] where atextlst = List (atext) and stringlst = List (string)ATEXTstrsub を除けば、データ型 atext に関連するデータコンストラクタの意味を理解するのは容易でしょう。この ATEXTstrsub はその文字列型の引数が、2つの文字 '#' と '$' で囲まれた識別子で囲まれたトークンを含む可能性があることを示しています。なんらかの文字列 str を ATEXTstrsub(str) の形の値で文字列化する時、str 内のトークンをそれが表現する文字列で置換する必要があります。より詳しくは libatsdoc/DATS/libatsdoc_atext.dats 内にある fprint_strsub 関数の実装を見てください。
上記で示した例を含むファイル群は オンライン から入手できます。この HTML ファイルを生成する atext ファイルも オンライン から入手できます。