import "exp/datafmt"

datafmtパッケージは、文法指定方式または型駆動方式による任意データ構造のフォーマット出力を実装しています。データ構造のフォーマットは2つのフェーズから成ります。まず最初にパーサーはフォーマットの定義を読み込み、コンパイル済みフォーマットを生成します。そのあと、このコンパイル済みフォーマットを任意の値に繰り返し適用できるようになります。値にフォーマットを適用した結果は、フォーマット結果のバイトデータを格納した[]byte型の値、もしくはnilです。

フォーマットの定義は、パッケージ宣言とフォーマットルールのセットで構成されます。

Format      = [ Entry { ";" Entry } [ ";" ] ] .
Entry       = PackageDecl | FormatRule .

(フォーマット定義の構文はGo言語仕様で使用されるのと同じEBNF表記で示されます。ホワイトスペース、コメント、識別子、および文字列リテラルの文法はGoと同じです。)

パッケージ宣言はパッケージ名(例:’ast’)をパッケージのインポートパス(例:’go/ast’)にバインドします。各パッケージを使用する前に一度宣言しなければいけません。(型名については後述)

PackageDecl = PackageName ImportPath .
PackageName = identifier .
ImportPath  = string .

フォーマットルールは、ルール名をフォーマット式にバインドして定義します。ルール名は型名、もしくは特別な名前である’default’、’/'のいずれかです。型名は、事前宣言済みの型(例:’int’、 ‘float32′等)、パッケージ名付きのユーザ定義型(例:’ast.MapType’)、名前の無い複合型の構造を示す識別子(‘array’、 ‘chan’、 ‘func’、 ‘interface’、 ‘map’、’ptr’)のいずれかです。各ルールは一意の名前を持つ必要があり、宣言は任意の順番で行うことができます。

FormatRule  = RuleName "=" Expression .
RuleName    = TypeName | "default" | "/" .
TypeName    = [ PackageName "." ] identifier .

値をフォーマットするにあたり、値の型名はフォーマットルールを決めるために使われます(オーバーライドする仕組みは後述)。選ばれたルールが持つフォーマット式によって、どのように値がフォーマットされるかが決まります。各フォーマット式に値を適用した結果はバイトシーケンス、もしくはnilになります。

最も一般的な形式のフォーマット式は、選択肢のリストで、さらにその各選択肢はオペランドのシーケンスです。

Expression  = [ Sequence ] { "|" [ Sequence ] } .
Sequence    = Operand { Operand } .

選択肢リストのなかで最初に評価結果が非nilとなった値が、その選択肢リストのフォーマット式の評価結果となります。そのような選択肢が無いときはnilが評価結果となります。オペランドシーケンスの評価結果は、その各オペランドの評価結果を連ねたものです。シーケンス内のオペランドのどれかひとつの評価結果がnilとなるときは、オペランドシーケンス全体の評価結果もnilとなります。

オペランドは、以下の5種類です。

Operand     = Literal | Field | Group | Option | Repetition .

Literalは次の2つの置換によって評価を行います。最初に、パラメータとして渡されたカレントの値をfmt.Printfと同じように「%-書式」を使って展開します。次に、改行文字またはフォームフィード文字の直後に現在のインデント(後述)を挿入します。

Literal     = string .

下のテーブルは、文字列リテラルと、それを値42に対して適用した結果をそれぞれ示しています。

"foo"       foo
"%x"        2a
"x = %d"    x = 42
"%#x = %d"  0x2a = 42

Fieldオペランドはフィールド名であり、オプションの代替ルール名を伴います。フィールド名は識別子、もしくは特別な名前である@または*です。

Field       = FieldName [ ":" RuleName ] .
FieldName   = identifier | "@" | "*" .

フィールド名が識別子の場合、カレントの値は構造体である必要があり、フィールドが構造体内の名前と一致している必要があります。これと同じ検索ルールがGo言語でも使われています(例えば、匿名フィールドの名前は、パッケージ名を伴わない型名です)。フィールド名は構造体のフィールドの値を示します。フィールドが見つからないときは、フォーマットが中断され、エラーメッセージが返されます。(フィールドが見つからないときの評価結果をnilとするように変更する予定です。)

特別な名前である’@'はカレント値です。

特別な名前である’*'は、カレント値の型を示します。

array, slice types   array, slice 要素 ({}内のみ、下を参照)
interfaces           インタフェースに格納されている値
pointers             ポインタによって示される値

(実装上の制約:チャネル、関数、マップ型はリフレクションがサポートしていないため、サポートされていません。)

フィールドは以下の通りに評価されます。フィールド値がnilであるか、もしくは配列、スライスの要素が存在しないときは、評価結果はnilとなります(配列/スライスの要素の詳細については後述)。値がnilではないフィールド値は、型の名前および選択されたルール名と一致するルールを使用して再帰的にフォーマットされます。

以下の例は、構造体’myPackage.Point’の完全なフォーマット定義です。先にパッケージを実装します。

package myPackage  // myDir/myPackageディレクトリに置きます
type Point struct {
	name string;
	x, y int;
}

フォーマット定義です。

myPackage "myDir/myPackage";
int = "%d";
hexInt = "0x%x";
string = "---%s---";
myPackage.Point = name "{" x ", " y:hexInt "}";

上のフォーマット定義をmyPackage.Point{“foo”, 3, 15}に適用した結果です。

---foo---{3, 0xf}

さらにオペランドはグループ化したり、オプションを取ったり、式を繰り返したりすることができます。グループ化式(group)は、さらに複雑な式(body)をグループ化し、単一オペランドの代わりとして使用することができます。

Group       = "(" [ Indentation ">>" ] Body ")" .
Indentation = Expression .
Body        = Expression .

グループのbodyの前に、’>>’を伴なうインデント式を持つことができます。インデント式の評価には、他の式や評価結果のようにカレント値が適用されます。これがnilでないときはbodyの評価中(フォーマット状態については後述)に、その値がカレントインデントに追加されます。

オプション式(option)は、角括弧’[]‘で囲まれます。

Option      = "[" Body "]" .

オプションはbodyを評価しますが、通常と異なる点は評価結果がnilのときは、nilの代わりに空の[]byteを評価結果として使用します。このようにオプションを使用することで、式をnilオペランドから保護することができます。

繰り返し表現(repetition)は、波括弧’{}’で囲まれます。

Repetition  = "{" Body [ "/" Separator ] "}" .
Separator   = Expression .

繰り返し式は、以下の通りに評価されます。bodyを評価結果がnilとなるまで繰り返し評価し、その評価結果を連結した値が繰り返し式の評価結果となります。繰り返しの評価結果は、空の連結になることはありますがnilとはなりません。bodyの評価中、暗黙的なインデックス値が提供されます。そのインデックスは配列やスライスの要素を指定するために使用されます。対応する要素が存在しないときは、その要素の評価結果はnilとなり、繰り返しが終了します。

繰り返しのbodyの後に’/'とセパレータ式(separator)が指定されることがあります。セパレータ式が指定されているときは、bodyの繰り返しの合間に評価されます。

以下の例は、無名型のスライスをフォーマットするための完全なフォーマット定義です。

int = "%b";
array = { * / ", " };  // 配列は、スライスの型名

‘[]int{2, 3, 5, 7}’の結果は、下となります。

10, 11, 101, 111

デフォルトルール:フォーマットルール名’default’が指定されているときに、他のルールが見つからなければ、このルールがフォーマットに使用されます。下は、共通のデフォルトルールです。

default = "%v"

各基本型でそれぞれルールを指定する必要がないように、デフォルトのフォーマットが提供されています。

グローバルセパレータルール:フォーマットルール名’/'が指定されているときは、リテラル間にカレント値を使って評価されます。このセパレータ式の評価結果がnilとなるときは無視されます。

例えば、グローバルセパレータルールで、値のシーケンスをコンマで区切るときのルールは下となります。

default = "%v";
/ = ", ";

パッケージファイル

datafmt.go parser.go

Environment型

特定用途向けのEnvironmentがFormat.Applyに提供されることがあります。このEnvironmentは、カスタムフォーマッタ内からState.Env()を使って取得、利用されます。Environmentは、Copyメソッドを実装する必要があります。このCopyメソッドは、レシーバの完全なコピーを返さなければなりません。これは、フォーマッタがEnvironmentの保存と復元を行うために必要です。(式が見つからないとき)

Environmentがフォーマット中に変更(これはカスタムフォーマッタからの制御)されないのであれば、Copyメソッドは単にレシーバの値を返すだけの非常に軽い処理です。

type Environment interface {
    Copy() Environment
}

Format型

Formatは、フォーマット定義を解析した結果です。Formatは、値をフォーマットするために繰り返し適用されます。

type Format map[string]expr

Parse関数

func Parse(fset *token.FileSet, filename string, src []byte, fmap FormatterMap) (Format, os.Error)

Parseは、ソースsrcから一組のフォーマットを解析します。カスタムフォーマッタは、フォーマッタ関数を格納したマップを通して提供されます。エラーがなければ、戻り値としてFormatとnilが返ります。エラーがあったときはFormatはnilで、エラーとして空ではないErrorListが返ります。

(Format) Eval関数

func (f Format) Eval(env Environment, args ...interface{}) ([]byte, os.Error)

Evalは、各引数をフォーマットfによってフォーマットし、結果の[]byteとos.Errorを返します。エラーが発生すると、返される[]byteには部分的なフォーマット結果が格納されます。環境envは、Stateのパラメータを経由して、有効なカスタムフォーマッタに渡されます。

(Format) Fprint関数

func (f Format) Fprint(w io.Writer, env Environment, args ...interface{}) (int, os.Error)

Fprintは、各引数をフォーマットfによってフォーマットし、wに書き込みます。書き込んだ合計バイト数と、エラーのときはos.Errorを返します。

(Format) Print関数

func (f Format) Print(args ...interface{}) (int, os.Error)

Printは、各引数をフォーマットfによってフォーマットし、標準出力に書き込みます。書き込んだ合計バイト数と、エラーのときはos.Errorを返します。

(Format) Sprint関数

func (f Format) Sprint(args ...interface{}) string

Sprint は、各引数をフォーマットfによってフォーマットし、結果の文字列を返します。フォーマット中にエラーが発生すると、返される文字列には部分的なフォーマット結果とエラーメッセージが含まれます。

Formatter型

カスタムフォーマッタは、Formatter関数型を実装して作成します。フォーマッタ関数を実行する際、パラメータとして現在のフォーマット状態(State)、フォーマット対象の値、フォーマッタをインストールしたときに指定したルール名(同一のフォーマッタ関数を他の名前で登録することも可能)を渡します。フォーマッタは、フォーマット処理もしくはState.Writeを使って出力を行うために現在のフォーマット状態にアクセスします。

フォーマッタの戻り値は論理値で、実行結果が非nil値のときtrueを、nil値のときfalseを返します。

type Formatter func(state *State, value interface{}, ruleName string) bool

FormatterMap型

FormatterMapは、カスタムフォーマッタのセットです。これは、ルール名とフォーマッタ関数とのマッピングです。

type FormatterMap map[string]Formatter

State型

Stateは、フォーマットの現在の状態を表します。カスタムフォーマッタへの引数として渡されます。

type State struct {
    // contains unexported fields
}

(*State) Env関数

func (s *State) Env() interface{}

Envは、Format.Applyに渡された環境を返します。

(*State) LinePos関数

func (s *State) LinePos() token.Position

LinePosは、このStateの出力バッファ内の現在行の先頭位置を返します。

(*State) Pos関数

func (s *State) Pos() token.Position

Posは、出力バッファ内の、次に書き込みを行う位置を返します。行番号は1から開始します。

(*State) Write関数

func (s *State) Write(data []byte) (int, os.Error)

Writeは、出力バッファにデータを書き出します。各改行またはフォームフィード文字の後にはインデント文字列が挿入されます。このメソッドはエラーを返せません。