title: Using Parse::RecDescent
body: <p>I have the following input</p>
  
  <pre><code>@Book{press,
    author    = "Press, W. and Teutolsky, S. and Vetterling, W. and Flannery B.",
    title     = "Numerical {R}ecipes in {C}: The {A}rt of {S}cientific {C}omputing",
    year      = 2007,
    publisher = "Cambridge University Press"
  }
  </code></pre>
  
  <p>and I have to write a grammar for RecDescent parser generator.
  Data at output should be modified for the xml structure and should look like this:</p>
  
  <pre><code>&lt;book&gt;
      &lt;keyword&gt;press&lt;/keyword&gt;
      &lt;author&gt;Press, W.+Teutolsky, S.+Vetterling, W.+Flannery B.&lt;/author&gt;
      &lt;title&gt;Numerical {R}ecipes in {C}: The {A}rt of {S}cientific {C}omputing&lt;/title&gt;
  
      &lt;year&gt;2007&lt;/year&gt;
      &lt;publisher&gt;Cambridge University Press&lt;/publisher&gt;
  &lt;/book&gt;
  </code></pre>
  
  <p>Additional and repeated fields should be reported as errors (proper message with line number and no further parsing). I tried to start with something like this:</p>
  
  <pre><code>use Parse::RecDescent;
  
  open(my $in,  "&lt;",  "parsing.txt")  or die "Can't open parsing.txt: $!";
  
  my $text;
  while (&lt;$in&gt;) {
      $text .= $_;
  }
  
  print $text;
  
  my $grammar = q {
      beginning: "\@Book\{" keyword fields "\}"           { print "&lt;book&gt;\n",$item[2],$item[3],"&lt;/book&gt;"; }
      keyword: /[a-zA-Z]+/ ","                            { return "    &lt;keyword&gt;".$item[1]."&lt;/keyword&gt;\n"; }
      fields: one "," two "," tree "," four               { return $item[1].$item[3].$item[5].$item[7]; }
      one: "author" "=" "\"" /[a-zA-Z\s\.\,\{\}\:]+/ "\"" {   $item[4] =~ s/\sand\s/\+/g;
                                                              return "    &lt;author&gt;",$item[4],"&lt;/author&gt;\n"; }
      two: "title" "=" "\"" /[a-zA-Z\s\.\,\{\}\:]+/ "\""  {   $item[4] =~ s/\sand\s/\+/g;
                                                              return "    &lt;title&gt;",$item[4],"&lt;/title&gt;\n"; }
      three: "year" "=" /[0-2][0-9][0-9][0-9]/            {   return "    &lt;year&gt;",$item[3],"&lt;/year&gt;\n"; }
      four: "publisher" "=" "\"" /[a-zA-Z\s\.\,\{\}\:]+/ "\"" 
                                                          {   $item[4] =~ s/\sand\s/\+/g;
                                                              return "    &lt;publisher&gt;",$item[4],"&lt;/publisher&gt;\n"; }
  };
  
  my $parser = new Parse::RecDescent($grammar) or die ("Bad grammar!");
  defined $parser-&gt;beginning($text) or die ("Bad text!");
  </code></pre>
  
  <p>But I don't even know if it's the correct way to do it. Please help.</p>
  
  <p><strong>There's a one more tiny problem</strong>. Tags at input might not be in that particular order, but each tag can appear only once. Do I have to write subrules for all permutations of (author,title,year,publisher)? Because I came up with:</p>
  
  <pre><code>fields: field "," field "," field "," field
  field: one | two | three | four
  </code></pre>
  
  <p>but it obviously doesn't prevent from repeating tags.</p>