Introduct
i
on to Par
s
e
:
:
RecDe
s
cent
211
noun: 'book' | 'cat'
);
my $parser = Parse::RecDescent->new($grammar);
while (<DATA>) {
chomp;
print "'$_' is ";
print 'NOT ' unless $parser->sentence($_);
print "a valid sentence\n";
}
__END__
Larry wrote Perl
Larry wrote a book
Dave likes Perl
Dave likes the book
Dave wrote this book
the cat ate the book
Dave got very angry
Notice that we have expanded the terminals to actually represent a (very limited)
subset of English words. The output of this script is a follows:
'Larry wrote Perl' is a valid sentence
'Larry wrote a book' is a valid sentence
'Dave likes Perl' is a valid sentence
'Dave likes the book' is a valid sentence
'Dave wrote this book' is a valid sentence
'the cat ate the book' is a valid sentence
'Dave got very angry' is NOT a valid sentence
Which shows that “Dave got very angry” is the only text in our data, which is not a
valid sentence.1
Explaining 
t
he code
The only complex part of this script is the definition of the grammar. The syntax of
this definition is similar to one that we used in chapter 8. The only major difference
is that we have replaced the arrow 
->
with a colon. If you read the rules, replacing
the colon with the phrase “is made up of” and the vertical bar with the word “or”,
then these rules are easy to understand.
In this example all of our terminals are fixed strings. As we shall see later in the
chapter, it is quite possible to match Perl regular expressions instead.
Having defined our grammar, we simply create a parser object using this gram-
mar and use that object to see if our sentences are valid. Notice that we use the
1
By the rules of our grammar of course—not by the real rules of English.
Rotate pages in pdf and save - rotate PDF page permanently in C#.net, ASP.NET, MVC, Ajax, WinForms, WPF
Empower Users to Change the Rotation Angle of PDF File Page Using C#
pdf rotate just one page; how to reverse pages in pdf
Rotate pages in pdf and save - VB.NET PDF Page Rotate Library: rotate PDF page permanently in vb.net, ASP.NET, MVC, Ajax, WinForms, WPF
PDF Document Page Rotation in Visual Basic .NET Class Application
pdf page order reverse; rotate all pages in pdf preview
212
CHAPTER 
Bu
i
ld
i
ng your own par
s
er
s
method 
sentence
to validate each sentence in turn. This method was created by
the 
Parse::RecDescent
object as it read our grammar. The 
sentence
method
returns 
true
or 
false
depending on whether or not the parser object successfully
parsed the input data.
11.2 Re
t
urning parsed da
t
a
The previous example is all very well if you just want to know whether your data
meets the criteria of a given grammar, but it doesn’t actually produce any useful
data structures which represent the parsed data. For that we have to look a little
deeper into 
Parse::RecDescent
.
11.2.1 Example
:
parsing a Windows 
INI
file
Let’s look at parsing a Windows 
INI
file.
These files contain a number of named
sections. Each of these sections contain a
number of assignment statements. Fig-
ure 11.1 shows an example 
INI
together
with the various parts that make up the
file structure.
In this example we have sections called
“files” and “rules.” The files section lists
the names of the input and output files
together with their extension; the rules
section lists a number of configuration options. This file might be used to control the
configuration of a text-processing program.
Before looking at how we would get the data
out, it is a good idea to decide what data struc-
ture we are going to use to store the parsed
data. In this case it seems fairly obvious that a
hash of hashes would be most useful. Each key
within the first hash would be a section name
and the value would be a reference to another
hash. Within these second-level hashes the keys
would be the left-hand side of the assignment statement and the values would be the
right-hand side. Figure 11.2 shows this data structure.
This means that you can get an individual value very easily using code like:
$input_file = $Config{files}{input};
[files]
input=data_in
output = data_out
ext=dat
[rules]
quotes=double
sep=comma
spaces=trim
Figur
e
11.1
I
N
I
fil
e
s
tructur
e
.
S
ection name
V
alue
S
ection
K
ey
input
e
xt
output
d
a
t
a
_in
e
xt
d
a
t
a
_out
rul
e
s
fil
e
s
h
a
s
h
r
e
f
h
a
h
g
r
e
f
quot
e
s
s
e
p
s
p
a
c
e
s
doubl
e
comm
a
trim
Figur
e
11.2
I
N
I
fil
e
data 
s
tructur
e
.
VB.NET PDF Page Delete Library: remove PDF pages in vb.net, ASP.
doc.Save(outPutFilePath). How to VB.NET: Delete Consecutive Pages from PDF. This is a VB .NET example for how to delete a range of pages from a PDF document.
how to change page orientation in pdf document; rotate pdf pages individually
C# PDF Page Insert Library: insert pages into PDF file in C#.net
how to merge PDF document files by C# code, how to rotate PDF document page doc2.Save(outPutFilePath Add and Insert Multiple PDF Pages to PDF Document Using
rotate pdf pages and save; rotate pages in pdf permanently
214
CHAPTER 
Bu
i
ld
i
ng your own par
s
er
s
Using regular expressions
The other thing to notice is that we are using regular expressions in many places to
match our terminals. This is useful because the names of the sections and the keys
and values in each section can be any valid word. In this example we are saying that
they must all be a string made up of Perl’s word characters.2
11.2.3 Parser ac
t
ions and 
t
he @i
t
em array
In order to extract data, we can make use of parser actions. These are
pieces of code that you write and then attach to any rule in a gram-
mar. Your code is then executed whenever that rule is matched.
Within the action code a number of special variables are available.
The most useful of these is probably the 
@item
array which contains
a list of the values that have been matched in the current rule. The
value in 
$item[0]
is always the name of the rule which has matched.
For example, when our 
header
rule is matched, the 
@item
array will
contain “header”, “[”, the name of the section, and “
]
” with ele-
ments 0 to 33 (figure 11.3).
In order to see what values are being matched, you could put action code on
each of the rules in the grammar like the following code. All this code does is print
out the contents of the 
@item
array each time a rule is matched.
file: section(s) { print "$item[0]: $item[1]\n"; }
section: header assign(s) { print "$item[0]: $item[1] $item[2]\n"; }
header: '[' /\w+/ ']' { print "$item[0]: $item[1] $item[2] $item[3]\n"; }
assign: /\w+/ '=' /\w+/
{ print "$item[0]: $item[1] $item[2] $item[3]\n"; }
However, 
Parse::RecDescent
provides an easier way to achieve the same result,
by providing a way to assign a default action to all rules in a grammar. If you assign
a string containing code to the variable 
$::RD_AUTOACTION
, then that code will be
assigned to every rule which doesn’t have an explicit action. 
11.2.4 Example
:
displaying 
t
he con
t
en
t
s of @i
t
em
Here is a sample program which reads an 
INI
file and displays the contents of 
@item
for each matched rule.
use Parse::RecDescent;
my $grammar = q(
2
That is, alphanumeric characters and the underbar character.
3
The same information is also available in a hash called 
%item
, but I’ll use 
@item
in these examples. For
more details on 
%item
see 
perldoc Parse::RecDescent
.
0
1
2
h
e
a
d
e
r
[
fil
e
s
3
]
Figur
e
11.3
Th
e
@it
e
m array 
aft
e
r matching 
th
e
h
e
ad
e
r rul
e
for th
e
fir
s
t tim
e
VB.NET PDF Page Insert Library: insert pages into PDF file in vb.
outPutFilePath As String = Program.RootPath + "\\" Output.pdf" Dim doc1 doc2.InsertPages(pages, pageIndex) ' Output the new document doc2.Save(outPutFilePath
pdf rotate all pages; pdf reverse page order preview
How to C#: Rotate Image according to Specified angle
pages edit, C#.NET PDF pages extract, copy, paste, C#.NET rotate PDF pages, C#.NET VB.NET How-to, VB.NET PDF, VB.NET Word, VB.NET Excel, VB Steps to Rotate image.
pdf rotate pages separately; reverse pdf page order online
Return
i
ng par
s
ed data
215
file: section(s)
section: header assign(s)
header: '[' /\w+/ ']'
assign: /\w+/ '=' /\w+/
);
$::RD_AUTOACTION = q { print "$item[0]: @item[1..$#item]\n"; 1 } ;
$parser = Parse::RecDescent->new($grammar);
my $text;
{
$/ = undef;
$text = <STDIN>;
}
$parser->file($text);
The general structure of the code and the grammar should be familiar. The only
thing new here is the code assigned to 
$::RD_AUTOACTION
. This code will be run
whenever a rule that doesn’t have its own associated action code is matched. When
you run this program using our earlier sample 
INI
file as input, the resulting output
is as follows:
header: [ files ]
assign: input = data_in
assign: output = data_out
assign: ext = dat
section: 1 ARRAY(0x8adc868)
header: [ rules ]
assign: quotes = double
assign: sep = comma
assign: spaces = trim
section: 1 ARRAY(0x8adc844)
file: ARRAY(0x8adc850)
How rule ma
t
ching works
The previous example shows us a couple of interesting things about the way that
Parse::RecDescent
works. Look at the order in which the rules have been
matched and recall what we saw about the workings of top-down parsers in
chapter 8. Here you can clearly see that a rule doesn’t match until all of its subrules
have been matched successfully.
Secondly, look at the output for the 
section
and 
file
rules. Where you have
matched a repeating subrule, 
@item
contains a reference to an array, and where you
have matched a nonrepeating subrule, 
@item
contains the value 
1
. This shows us
something about what a matched rule returns. Each matched rule returns a 
true
value. By default this is the number 
1
, but you can change this in the associated
C# Create PDF from Tiff Library to convert tif images to PDF in C#
Similarly, Tiff image with single page or multiple pages is supported. Description: Convert to PDF and save it on the disk. Parameters:
how to rotate all pages in pdf; rotate pdf page by page
C# Create PDF from Word Library to convert docx, doc to PDF in C#.
Able to get word count in PDF pages. Change Word hyperlink to PDF hyperlink and bookmark. Description: Convert to PDF/TIFF and save it on the disk.
rotate pages in pdf; pdf reverse page order
216
CHAPTER 
Bu
i
ld
i
ng your own par
s
er
s
action code. Be sure that your code has a 
true
return value, or else the parser will
think that the match has failed.
11.2.5 Re
t
urning a da
t
a s
t
ruc
t
ure
The value that is returned from the top-level rule will be the value returned by the
top-level rule method when called by our script. We can use this fact to ensure that
the data structure that we want is returned. Here is the script that will achieve this:
use Parse::RecDescent;
my $grammar = q(
file: section(s)
{ my %file;
foreach (@{$item[1]}) {
$file{$_->[0]} = $_->[1];
}
\%file;
}
section: header assign(s)
{ my %sec;
foreach (@{$item[2]}) {
$sec{$_->[0]} = $_->[1];
}
[ $item[1], \%sec]
}
header: '[' /\w+/ ']' { $item[2] }
assign: /\w+/ '=' /\w+/
{ [$item[1], $item[3]] }
);
$parser = Parse::RecDescent->new($grammar);
my $text;
{
$/ = undef;
$text = <STDIN>;
}
my $tree = $parser->file($text);
foreach (keys %$tree) {
print "$_\n";
foreach my $key (keys %{$tree->{$_}}) {
print "\t$key: $tree->{$_}{$key}\n";
}
}
Another example
:
the CD data f
i
le
217
The code that has been added to the previous script is in two places. First (and most
importantly) in the parser actions and, secondly, at the end of the script to display
the returned data structure and demonstrate what is returned.
The action code might look a little difficult, but it’s probably a bit easier if you
read it in reverse order and see how the data structure builds up.
The 
assign
rule now returns a reference to a two-element list. The first element
is the left-hand side of the assignment and the second element is the right-hand
side. The 
header
rule simply returns the name of the section.
The 
section
rule creates a new hash called 
%sec
. It then iterates across the list
returned by the 
assign
subrule. Each element in this list is the return value from
one 
assign
rule. As we saw in the previous paragraph, this is a reference to a two-
element list. We convert each of these lists to a key
/
value pair in the 
%sec
hash.
Finally, the rule returns a reference to a two-element hash. The first element of this
list is the return value from the 
header
rule (which is the section name), and the
second element is a reference to the section hash.
The file rule uses a very similar technique to take the list of sections and convert
them into a hash called 
%file
. It then returns the 
%file
hash.
This means that the file method returns a reference to a hash. The keys to the
hash are the names of the sections in the file and the values are references to
hashes. The keys to the second level hashes are the text from the left-hand side of
the assignments, and the values are the associated strings from the right-hand side
of the assignment.
The code at the end of the script prints out the values in the returned data struc-
ture. Running this script against our sample 
INI
file gives us the following result:
rules
quotes: double
sep: comma
spaces: trim
files
input: data_in
ext: dat
output: data_out
which demonstrates that we have built up the data structure that we wanted.
11.3 Ano
t
her example
:
t
he 
CD
da
t
a file
Let’s take a look at another example of parsing a data file with 
Parse::RecDescent
.
We’ll take a look at how we’d parse the 
CD
data file that we discussed in chapter 8.
What follows is the data file we were discussing:
220
CHAPTER 
Bu
i
ld
i
ng your own par
s
er
s
This program will print 
valid
or 
invalid
depending on whether or not the file
passed to it on 
STDIN
parses correctly against the given grammar. In this case it
does, but if it doesn’t and you want to find out where the errors are, there are two
useful variables which 
Parse::RecDescent
uses to help you follow what it is doing. 
Debugging 
t
he grammar wi
t
h $
:
:
RD_TRACE
and $
:
:
RD_HINT
Setting 
$::RD_TRACE
to 
true
will display a trace of the parsing process as it
progresses, allowing you to see where your grammar and the structure of the file
disagree. If the problems are earlier in the process and there are syntax errors in
your grammar, then setting 
$::RD_HINT
to 
true
will provide hints on how you
could fix the problems. Setting 
$::RD_AUTOACTION
to a snippet of code which
prints out the values in 
@item
can also be a useful debugging tool.
11.3.3 Adding parser ac
t
ions
Having established that our grammar does what we want, we can proceed with
writing the rest of the program. As previously, most of the interesting code is in the
parser actions. Here is the complete program:
use strict;
use Parse::RecDescent;
use Data::Dumper;
use vars qw(@cols);
my $grammar = q(
file: header body footer
{
my %rec =
(%{$item[1]}, list => $item[2], %{$item[3]});
\%rec;
}
header: /.+/ date
{ { title => $item[1], date => $item[2] } }
date: /\d+\s+\w+\s+\d{4}/ { $item[1] }
body: col_heads /-+/ cd(s) { $item[3] }
col_heads: col_head(s) { @::cols = @{$item[1]} }
col_head: /\w+/ { $item[1] }
cd: cd_line track_line(s)
{ $item[1]->{tracks} = $item[2]; $item[1] }
cd_line: /.{14}/ /.{19}/ /.{15}/ /\d{4}/
{ my %rec; @rec{@::cols} = @item[1 .. $#item]; \%rec }
track_line: '+' /.+/ { $item[2] }
footer: /\d+/ 'CDs'
{ { count => $item[1] } }
);
my $parser = Parse::RecDescent->new($grammar);
Documents you may be interested
Documents you may be interested