Using Parse :: RecDescent

I have the following input

@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" } 

and I have to write a grammar for the RecDescent parser generator. The output should be modified for the xml structure and should look like this:

 <book> <keyword>press</keyword> <author>Press, W.+Teutolsky, S.+Vetterling, W.+Flannery B.</author> <title>Numerical {R}ecipes in {C}: The {A}rt of {S}cientific {C}omputing</title> <year>2007</year> <publisher>Cambridge University Press</publisher> </book> 

Additional and duplicate fields should be reported as errors (correct message with line number and without further analysis). I tried to start with something like this:

 use Parse::RecDescent; open(my $in, "<", "parsing.txt") or die "Can't open parsing.txt: $!"; my $text; while (<$in>) { $text .= $_; } print $text; my $grammar = q { beginning: "\@Book\{" keyword fields "\}" { print "<book>\n",$item[2],$item[3],"</book>"; } keyword: /[a-zA-Z]+/ "," { return " <keyword>".$item[1]."</keyword>\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 " <author>",$item[4],"</author>\n"; } two: "title" "=" "\"" /[a-zA-Z\s\.\,\{\}\:]+/ "\"" { $item[4] =~ s/\sand\s/\+/g; return " <title>",$item[4],"</title>\n"; } three: "year" "=" /[0-2][0-9][0-9][0-9]/ { return " <year>",$item[3],"</year>\n"; } four: "publisher" "=" "\"" /[a-zA-Z\s\.\,\{\}\:]+/ "\"" { $item[4] =~ s/\sand\s/\+/g; return " <publisher>",$item[4],"</publisher>\n"; } }; my $parser = new Parse::RecDescent($grammar) or die ("Bad grammar!"); defined $parser->beginning($text) or die ("Bad text!"); 

But I don’t even know if this is done correctly. Please, help.

There is another tiny problem . Input tags may not be in that particular order, but each tag can only appear once. Do I have to write subrules for all permutations (author, title, year, publisher)? Because I came up with:

 fields: field "," field "," field "," field field: one | two | three | four 

but this obviously does not stop repeating tags.

+4
source share
1 answer

First, you have a typo: tree instead of three .

I ran your program, but added the lines:

 use strict; use warnings; # you should always have strict and warnings on $::RD_HINT = 1; # Parse::RecDescent hints $::RD_TRACE = 1; # Parse::RecDescent trace 

and got this debug output:

  1|beginning |>>Matched terminal<< (return value: | | |[@Book{]) | 1|beginning | |"press,\n author = "Press, | | |W. and Teutolsky, S. and | | |Vetterling, W. and Flannery | | |B.",\n title = "Numerical | | |{R}ecipes in {C}: The {A}rt | | |of {S}cientific | | |{C}omputing",\n year = | | |2007,\n publisher = | | |"Cambridge University | | |Press"\n}\n" 1|beginning |Trying subrule: [keyword] | 2| keyword |Trying rule: [keyword] | 2| keyword |Trying production: [/[a-zA-Z]+/ ','] | 2| keyword |Trying terminal: [/[a-zA-Z]+/] | 2| keyword |>>Matched terminal<< (return value: | | |[press]) | 2| keyword | |",\n author = "Press, W. and | | |Teutolsky, S. and | | |Vetterling, W. and Flannery | | |B.",\n title = "Numerical | | |{R}ecipes in {C}: The {A}rt | | |of {S}cientific | | |{C}omputing",\n year = | | |2007,\n publisher = | | |"Cambridge University | | |Press"\n}\n" 2| keyword |Trying terminal: [','] | 2| keyword |>>Matched terminal<< (return value: | | |[,]) | 2| keyword | |"\n author = "Press, W. and | | |Teutolsky, S. and | | |Vetterling, W. and Flannery | | |B.",\n title = "Numerical | | |{R}ecipes in {C}: The {A}rt | | |of {S}cientific | | |{C}omputing",\n year = | | |2007,\n publisher = | | |"Cambridge University | | |Press"\n}\n" 2| keyword |Trying action | 1|beginning |>>Matched subrule: [keyword]<< (return| | |value: [ <keyword>press</keyword> ]| 1|beginning | |"press,\n author = "Press, | | |W. and Teutolsky, S. and | | |Vetterling, W. and Flannery | | |B.",\n title = "Numerical | | |{R}ecipes in {C}: The {A}rt | | |of {S}cientific | | |{C}omputing",\n year = | | |2007,\n publisher = | | |"Cambridge University | | |Press"\n}\n" 1|beginning |Trying subrule: [fields] | 2| fields |Trying rule: [fields] | 2| fields |Trying production: [one ',' two ',' | | |three ',' four] | 2| fields |Trying subrule: [one] | 3| one |Trying rule: [one] | 3| one |Trying production: ['author' '=' '\"' | | |/[a-zA-Z\s\.\,{}\:]+/ '\"'] | 3| one |Trying terminal: ['author'] | 3| one |<<Didn't match terminal>> | 3| one |<<Didn't match rule>> | 2| fields |<<Didn't match subrule: [one]>> | 2| fields |<<Didn't match rule>> | 1|beginning |<<Didn't match subrule: [fields]>> | 1|beginning |<<Didn't match rule>> | Bad text! at parser.pl line 32, <$in> line 6. 

This shows that it is stuck in subrule one and that press, returning to the input stream. This is because you are using return , not $return = , as the Parse :: RecDescent manual says you should .

Also, once you assign the variable $return , you can no longer return the list and must merge the strings together manually.

Here's the end result:

 use strict; use warnings; use Parse::RecDescent; open(my $in, "<", "parsing.txt") or die "Can't open parsing.txt: $!"; my $text; while (<$in>) { $text .= $_; } print $text; my $grammar = q { beginning: "\@Book\{" keyword fields /\s*\}\s*/ { print "<book>\n",$item[2],$item[3],"</book>"; } keyword: /[a-zA-Z]+/ "," { $return = " <keyword>$item[1]</keyword>\n"; } fields: one /,\s*/ two /,\s*/ three /,\s*/ four { $return = $item[1].$item[3].$item[5].$item[7]; } one: "author" "=" "\"" /[a-zA-Z\s\.\,\{\}\:]+/ "\"" { $item[4] =~ s/\sand\s/\+/g; $return = " <author>$item[4]</author>\n"; } two: "title" "=" "\"" /[a-zA-Z\s\.\,\{\}\:]+/ "\"" { $item[4] =~ s/\sand\s/\+/g; $return = " <title>$item[4]</title>\n"; } three: "year" "=" /[0-2][0-9][0-9][0-9]/ { $return = " <year>$item[3]</year>\n"; } four: "publisher" "=" "\"" /[a-zA-Z\s\.\,\{\}\:]+/ "\"" { $item[4] =~ s/\sand\s/\+/g; $return = " <publisher>$item[4]</publisher>\n"; } }; my $parser = new Parse::RecDescent($grammar) or die ("Bad grammar!"); defined $parser->beginning($text) or die ("Bad text!"); 
+9
source

Source: https://habr.com/ru/post/1335685/


All Articles