# -*- coding: utf-8 -*-

=encoding utf-8

=head1 NAME

YATT::Lite::XHF::Syntax(ja) - Extended Header Fields (XHF) フォーマットの構文定義

=for code perl

=head1 SYNOPSIS

  require YATT::Lite::XHF;

  my $parser = YATT::Lite::XHF->new(string => <<'END');
  # Taken from http://docs.ansible.com/YAMLSyntax.html#yaml-basics
  name: Example Developer
  job: Developer
  skill: Elite
  employed: 1
  foods[
  - Apple
  - Orange
  - Strawberry
  - Mango
  ]
  languages{
  ruby: Elite
  python: Elite
  dotnet: Lame
  }

  name: hkoba
  languages{
  yatt: Elite?
  }
  END
  
  # read() は(\n\n+ で区切られた)一パラグラフ分のレコードを返します,
  # リストコンテキストでは, 戻り値はフラットなリストです。
  # 通常、それは キー: 値 組のリストですが、それ以外のものを含めることも出来ます。
  # スカラコンテキストで呼び出した場合は hash が返ります
  while (my %hash = $parser->read) {
    print Dumper(\%hash), "\n";
  }

=head1 DESCRIPTION

Extended Header Fields (B<XHF>) フォーマットとは
hkoba が独自に定義した俺々フォーマットで、
L<電子メールのヘッダ|http://tools.ietf.org/html/rfc2822#section-2.2>
や L<HTTP の ヘッダ|http://tools.ietf.org/html/rfc2616#section-4.2>
をベースに、 I<入れ子データ構造> のための拡張を加えたものです。
XHF 形式のファイルや文字列を load/parse するには L<YATT::Lite::XHF>
を使って下さい。

Note: 元々 XHF は unit test のテスト入力・出力ペアを簡単に書くために
設計されました。シリアライザ (L<YATT::Lite::XHF::Dumper>) も用意してありますが、
決して完全な(perl の任意のデータ構造をシリアライズすることを目指した)ものでは
ありません。そのような用途には L<YAML> 一族や, L<Storable> などを用いて下さい。

=head2 最小限のエスケープルール
X<minimum-escaping>

最も単純な用途では、 YAML と XHF はとても良く似て見えます。例えば、
C<< {foo => 1, bar => 2} >> というデータを表現する時、YAML と
XHF は同じように書けます。

  foo: 1
  bar: 2

しかし、少し複雑なデータ構造になると、段々違いが見えてきます。
例えば C<< {x => [1, 2, "3, 4"], y => 5} >>、

B<XHF> では以下のように書けます:

  {
  x[
  - 1
  - 2
  - 3, 4
  ]
  y: 5
  }


同じものを B<YAML> ではこう書くでしょう：

  ---
  x:
    - 1
    - 2
    - '3, 4'
  y: 5

両者の違いは:

=over 4

=item * XHF は B<括弧> C< {} [] > を用い、YAML は B<字下げ> を使って構造を表す。

=item * XHF では C<3, 4> はそのまま表記できるが、YAML では B<エスケープして> C<'3, 4'> と書かねばならない。

=back

=head3 複数行文字列と、"書いたまま"モード
X<multi-line-text>

XHF の C<キー：値> ペアの B<値> 部分では、エスケープする必要がある文字は
改行文字 C<\n> (と、値の前後の空白文字) だけです。
つまり、値部分には全く「構文」がありません。ですので、
値部分を書く時に覚えなければならないことは、実質的に無いも同然なのです。

=over 4

=item 値の中に改行を含めたい場合
X<newline-escaping> X<line-continuation>

単純に、全ての改行 C<"\n"> を改行+SPACE C<"\n "> で置換して下さい。
つまり C<< s/\n/\n /g >> するだけです。

例： C<< { foo => "1\n2\n\n3", bar => 4 } >> は以下のように書けます:

   foo: 1
    2
    
    3
   bar: 4

=item 先頭・末尾にスペース/タブ/改行を残したい場合は？
X<verbatim-text>

キーと値の区切りを、 C<": "> の代わりに C<":\n"> で書くだけです。
後は他と同様、値の中の C<"\n"> をエスケープするだけで構いません。

例： C<< { foo => "  x  ", bar => "\n\ny\n\n" } >> は以下のように書けます:

  foo:
    x  
  bar:
   
   
   y
   
   


=back

=head2 キーと値を個別のアイテムとして書ける
X<pair-by-two-items>

XHF のキー部分には、書ける文字に強い制限が有ります(値部分と違って).
具体的には C<[[:alnum:]]>, C<"-">, C<".">, C<"/"> そして
幾つかの追加の文字(詳細は L</BNF> の I<field-name> の定義を参照) です。

しかしながら、 C<キー: 値> と書く代わりに、C<- キー>, C<- 値> と２つのアイテムを
連続して書くことで、同じ意味を表すことが出来ます。この２つの書き方は、
どこでも入れ替えて使うことが出来ます。
ですので、もし面倒なら、キー部分を全部 C<- キー> 表記で書いて、
(値と同じく)改行 C<\n> だけエスケープする、でも構いません。

   # 例えば、以下のブロック：

   foo: 1
   bar: 2

   # 上記は、以下と同じ結果になります

   - foo
   - 1
   - bar
   - 2

例： C<< { "foo bar" => "baz" } >> は以下のように書けます：

  {
  - foo bar
  - baz
  }

別の例： C<< { "\n  foo\nbar  \n" => "baz" } >> は以下のように書けます。

  {
  -
  
     foo
   bar
  
  - baz
  }

入れ子になったデータ構造でも、同じ法則が適用できます。

   foo{
   x: 1
   y: 2
   }
   baz[
   - z
   ]

   # can be written instead as following:

   - foo
   {
   x: 1
   y: 2
   }
   - baz
   [
   - z
   ]

   # or even like following:

   - foo
   {
   - x
   - 1
   - y
   - 2
   }
   - baz
   [
   - z
   ]

C<キー: 値> 記法は配列の中でも使えます：

  [
  foo: 1
  bar: 2
  ]

  # above is equal to following

  [
  - foo
  - 1
  - bar
  - 2
  ]

=head2 リストを格納するコンテナの型は外側が決める
X<container-agnostic>

もう一つの重要な違いは、コンテナの型の選択方法です。
XHF では キー・値を区切る区切り文字は、"値部分の型" を決めるものです。
これは区切り文字が外側のコンテナの型を決める YAML とは大きく違います。

XHF では、以下のブロック：

  foo: 1
  bar: 2

は、C<< ( foo => 1, bar => 2 ) >> つまり４つの要素からなるフラットなリスト、を表します。このリストをどんなコンテナに格納するのかは、呼び出し側が自由に決めて構いません。
例えば辞書に格納したいなら:

  my %dict = $parser->read;

配列に格納したいなら:

  my @array = $parser->read;

なお、もしスカラコンテキストで呼び出した場合は、辞書が作られます
(もし奇数個しか要素がないなら、エラーになります).

  my $dict = $parser->read;

これに対して YAML では C<:> は常に I<map(dictionary)> を意味します。
ですので、上記は C<< +{ foo => 1, bar => 2 } >> を返すでしょう。

=head3 順序付き、キー・値ペアのリスト、重複有り
X<ordered-kv-pairs>

以上のように XHF のブロックの一番外側はフラットなリストを表しているので、
そこには (Perl の HASH データ構造から来る) 制限は適用されません。
従って、Perl の HASH では許されないような、キーの順序や、キーの重複を
表現することも、以下のように一応可能です：


  foo: 1
  foo: 2
  foo: 3
  bar: x
  bar: y

上記を以下のスクリプトで読むと

  my @array = $parser->read;

C<@array> には C<< (foo => 1, foo => 2, foo => 3, bar => 'x', bar => 'y') >>
が格納されるでしょう。

この機能は、ある種のテストデータを表現するために、時に大いに役立ちます。
(例えば HTTP のクエリ引数や、
Email ヘッダの "Received" フィールド). 例えば上記の XHF が表すデータと
等価な http クエリパラメータを作る html form は、以下のように書けるでしょう：

=for code html

  <input type="checkbox" name="foo" value="1">
  <input type="checkbox" name="foo" value="2">
  <input type="checkbox" name="foo" value="3">
  <input type="checkbox" name="bar" value="x">
  <input type="checkbox" name="bar" value="y">


=for code perl

Note: 現在のところ、入れ子要素は perl の普通の HASH と ARRAY に変換されます。
ですので、上記の順序・重複保護が有効に働くのは、一番外側のみです。

=head2 パラグラフのストリーム (コメント読み飛ばし付き)
X<paragraph-stream>

XHF の入力ストリームは連続空行 C<"\n\n+"> で区切られます
(これは Email ヘッダや HTTP ヘッダと同様です)。
これは Perl の(懐かしの) パラグラフモードに基づく複数行レコードのフォーマットが
ベースになっています。Perl でパラグラフモードを扱うためのマニュアルは
L<perl -00|perlrun/-0> や L<Setting $RS to ""|perlvar/$RS> を参照して下さい。

Note: XHF では "コメントのみを含む" ブロックは自動的に読み捨てられます。例えば：

  foo: 1
  bar: 2

  # どや！ ここにコメントだけのブロックがおんねん！


  baz: 3
  qux: 4

上記を以下のスクリプトで読むとします：

  my @records;
  push @records, $_ while $_ = $parser->read;

すると C<@records> には２つの要素
C<< ({foo => 1, bar => 2}, {baz => 3, qux => 4}) >> だけが入ります。

=head3 メタ要素を入れたいときは?
X<howto-skip_comment>

ごく稀に、一つのストリームに、メタ情報的なものを含めたり、含めなかったり！
したくなる時が有ります。そんな時は、ストリームの先頭で、一回だけ
C<< read(skip_comment => 0) >> のように、
"コメントのみブロック"の読み捨て機能をオフにして読み込む、というハックが使えます：


  # これがメタ情報. 下の test => 1 を有効にするには、次の行頭の "# " を削って下さい
  # test: 1


  # This is body1
  foo: 1
  bar: 2

  # This is body2
  foo: 3
  bar: 4


スクリプトはこんな感じです。

  if (my @meta = $parser->read(skip_comment => 0)) {
    # process metainfo. You may get (test => 1).
  }
  while (my @content = $parser->read) {
    # process body1, body2, ...
  }

=head1 複雑な例 (YAML との対比つき)
X<complex-example>

Here is a more dense example B<in XHF>:

  name: hkoba
  # (1) You can write a comment line here, starting with '#'.
  job: Programming Language Designer (self-described;-)
  skill: Random
  employed: 0
  foods[
  - Sushi
  #(2) here too. You don't need space after '#'. This will be good for '#!'
  - Tonkatsu
  - Curry and Rice
  [
  - More nested elements
  ]
  ]
  favorites[
  # (3) here also.
  {
  title: Chaika - The Coffin Princess
  # (4) ditto.
  heroine: Chaika Trabant
  }
  {
  title: Witch Craft Works
  heroine: Ayaka Kagari
  # (5) You can use leading "-" for hash key/value too (so that include any chars)
  - Witch, Witch!
  - Tower and Workshop!
  }
  # (6) You can put NULL(undef) like below. (equal space sharp+keyword)
  = #null
  ]

Above will be loaded like following structure:

  $VAR1 = {
          'foods' => [
                     'Sushi',
                     'Tonkatsu',
                     'Curry and Rice',
                     [
                       'More nested element'
                     ]
                   ],
          'job' => 'Programming Language Designer (self-described;-)',
          'name' => 'hkoba',
          'employed' => '0',
          'skill' => 'Random',
          'favorites' => [
                         {
                           'heroine' => 'Chaika Trabant',
                           'title' => 'Chaika - The Coffin Princess'
                         },
                         {
                           'title' => 'Witch Craft Works',
                           'heroine' => 'Ayaka Kagari',
                           'Witch, Witch!' => 'Tower and Workshop!'
                         },
                         undef
                       ]
        };


Above will be written B<in YAML> like below (note: inline comments are omitted):

  ---
  employed: 0
  favorites:
    - heroine: Chaika Trabant
      title: 'Chaika - The Coffin Princess'
    - 'Witch, Witch!': Tower and Workshop!
      heroine: Ayaka Kagari
      title: Witch Craft Works
    - ~
  foods:
    - Sushi
    - Tonkatsu
    - Curry and Rice
    -
      - More nested element
  job: Programming Language Designer (self-described;-)
  name: hkoba
  skill: Random


This YAML example clearly shows how you need to escape strings quite randomly,
e.g. see above value of C<< $VAR1->{favorites}[0]{title} >>.
Also the key of C<< $VAR1->{favorites}[1]{'Witch, Witch!'} >> is nightmare.

I don't want to be bothered by this kind of escaping.
That's why I made XHF.

=head1 FORMAT SPECIFICATION
X<XHF-FORMAT>

XHF は(空行で区切られた)パラグラフ一個ずつの単位でパースされます。
個々のパラグラフは複数の C<xhf-item> を含むことが出来ます。
全ての xhf-item は必ず行の先頭から始まり、最後は改行で終わります。
xhf-item は以下の２つのうち、どちらかの形式を取ります。

  <name> <type-sigil> <sep> <value>         (name-value pair)

  <type-sigil> <sep> <value>                (standalone value)

C<type-sigil> は C<value> の型を決めます。
C<sep> は空白文字(スペース、タブ、改行) のいずれかです。
C<sep>が改行の場合は L<書いたままモード(verbatim text)|/verbatim-text>) を意味します。
ただし dict/array ブロックの場合は、C<sep> は改行だけしか許されません。

C<type-sigil> の全リストと、使える C<sep> は以下の通りです。

=over 4

=item C<"name:"> then C<" "> or C<"\n">

C<":"> は文字列値を(名前付きで)格納します。名前の省略は出来ません。
C<sep> は全ての空白文字です。

=item C<"-"> then C<" "> or C<"\n">

C<"-"> は文字列値を(名前抜きで)格納します。名前は書いてはいけません。

(Note: Currently, C<","> works same as C<"-">. This feature is arguable.)

=item C<"{"> then C<"\n">

=item C<"name{"> then C<"\n">

C<"{"> は辞書ブロック (C< { %HASH } > コンテナ)の始まりです。
名前を付けることが出来ます。

このブロックは C<"}\n"> で閉じられねばなりません。
また、要素の個数は偶数個でなければなりません。


=item C<"["> then C<"\n">

=item C<"name["> then C<"\n">

C<"["> は配列ブロック(C< [ @ARRAY ] > コンテナ)の始まりです。
名前をつけることが出来ます。

このブロックは C<"]\n"> で閉じられねばなりません。

=item C<"="> then C<" "> or C<"\n">

=item C<"name="> then C<" "> or C<"\n">

C<"="> は特別な値を格納するために使います。
名前を付けることが出来ます。

現状では、C<#undef> と、同じ意味の別表記である C<#null> だけが定義されています。

=item C<"#">

C<"#"> は埋め込みコメントを格納するために使います。
名前を付けることは出来ません。

=back

=head2 XHF Syntax definition in extended BNF
X<syntax> X<BNF>

Here is a syntax definition of XHF in extended BNF
(I<roughly> following L<ABNF|https://tools.ietf.org/html/rfc5234>.)

  xhf-block       = 1*xhf-item

  xhf-item        = field-pair / single-text
                   / dict-block / array-block / special-expr
                   / comment

  field-pair      = field-name  field-value

  field-name      = 1*NAME *field-subscript

  field-subscript = "[" *NAME "]"

  field-value     = ":" text-payload / dict-block / array-block / special-expr

  text-payload    = ( trimmed-text / verbatim-text ) NL

  trimmed-text    = SPTAB *( 1*NON-NL / NL SPTAB )

  verbatim-text   = NL    *( 1*NON-NL / NL SPTAB )

  single-text     = "-" text-payload

  dict-block      = "{" NL *xhf-item "}" NL

  array-block     = "[" NL *xhf-item "]" NL

  special-expr    = "=" SPTAB known-specials NL

  known-specials  = "#" ("null" / "undef")

  comment         = "#" *NON-NL NL

  NL              = [\n]
  NON-NL          = [^\n]
  SPTAB           = [\ \t]
  NAME            = [0-9A-Za-z_.-/~!]

=head2 Some notes on current definition
X<notes-on-current-def>

=over 4

=item field-name, field-subscript

B<field-name> can contain C</>,  C<.>, C<~> and C<!>.
Former two are for file names (path separator and extension separator).
Later two (and B<field-subscript>) are incorporated just to help
writing test input/output data for L<YATT::Lite>,
so these can be arguable for general use.


=item trimmed-text vs verbatim-text

If B<field-name> is separated by C<": ">, its B<field-value> will be trimmed
their leading/trailing spaces/tabs.
This is useful to handle hand-written configuration files.

But for some software-testing purpose(e.g. templating engine!),
this space-trimming makes it impossible to write exact input/output data.

So, when B<field-sep> is NL, field-value is not trimmed.

=item LF vs CRLF

Currently, I'm not so rigid to reject the use of CRLF.
This ambiguity may harm use of XHF as a serialization format, however.

=item C<","> can be used in-place of C<"-">.

This feature also may be arguable for general use.

=item C<":"> without C<name> was valid, but is now deprecated.

Previously valid

  : bar

which represents C<< ( "" => "bar" ) >>, is now invalid.
Please use two C<"- "> items like following:

  - 
  - bar

XXX: Hmm, should I provide deprecation cycle? Are there someone
already used XHF to serialize important data even before having this manual?
If so, please contact me. I will add an option to allow this.

=item line-continuation is valid.

Although L<line-continuation|/line-continuation> is obsoleted
in L<HTTP headers|http://tools.ietf.org/html/rfc7230#section-3.2.4>,
line-continuation will be kept valid in XHF spec. This is my preference.

=back


=head1 AUTHOR

"KOBAYASI, Hiroaki" <hkoba@cpan.org>

=head1 LICENSE

This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
