1 # $Id: PQF.pm,v 1.4 2004-12-17 17:12:05 mike Exp $
3 package Net::Z3950::PQF;
9 use Net::Z3950::PQF::Node;
11 our $VERSION = '0.02';
16 Net::Z3950::PQF - Perl extension for parsing PQF (Prefix Query Format)
21 $parser = new Net::Z3950::PQF();
22 $node = $parser->parse('@and @attr 1=1003 kernighan @attr 1=4 unix');
23 print $node->render(0);
27 This library provides a parser for PQF (Prefix Query Format), an ugly
28 but precise string format for expressing Z39.50 Type-1 queries. This
29 format is widely used behind the scenes of Z39.50 applications, and is
30 also used extensively with test-harness programs such as the YAZ
31 command-line client, C<yaz-client>. A few particularly misguided
32 souls have been known to type it by hand.
34 Unlike PQF itself, this module
35 is simple to use. Create a parser object, then pass PQF strings
36 into its C<parse()> method to yield parse-trees. The trees are made
37 up of nodes whose types are subclasses of
38 C<Net::Z3950::PQF::Node>.
39 and have names of the form
40 C<Net::Z3950::PQF::somethingNode>. You may find it helpful to use
41 C<Data::Dumper> to visualise the structure of the returned
44 What is a PQF parse-tree good for? Not much. You can render a
45 human-readable version by invoking the top node's C<render()> method,
46 which is probably useful only for debugging. If you want to do
47 anything useful, such as implementing an actual query server that
48 understands PQF, you'll have to walk the tree.
54 $parser = new Net::Z3950::PQF();
56 Creates a new parser object.
72 $query = '@and @attr 1=1003 kernighan @attr 1=4 unix';
73 $node = $parser->parse($query);
75 die "parse($query) failed: " . $parser->errmsg();
78 Parses the PQF string provided as its argument. If an error occurs,
79 then an undefined value is returned, and the error message can be
80 obtained by calling the C<errmsg()> method. Otherwise, the top node
81 of the parse tree is returned.
83 $node2 = $parser->parse($query, "zthes");
84 $node3 = $parser->parse($query, "1.2.840.10003.3.13");
86 A second argument may be provided after the query itself. If it is
87 provided, then it is taken to be either the name or the OID of a
88 default attribute set, which attributes specified in the query belong
89 to if no alternative attribute set is explicitly specified within the
90 query. When this second argument is absent, the default attribute set
97 my($text, $attrset) = @_;
98 $attrset = "bib-1" if !defined $attrset;
100 $this->{text} = $text;
101 return $this->_parse($attrset, {});
105 # PRIVATE to parse();
107 # Underlying parse function. $attrset is the default attribute-set to
108 # use for attributes that are not specified with an explicit set, and
109 # $attrhash is hash of attributes (at most one per type per
110 # attribute-set) to be applied to all nodes below this point. The
111 # keys of this hash are of the form "<attrset>:<type>" and the values
112 # are the corresponding attribute values.
116 my($attrset, $attrhash) = @_;
118 ### This rather nasty hack for quoted terms doesn't recognised
119 # backslash-quoted embedded double quotes.
120 $this->{text} =~ s/^\s+//;
121 if ($this->{text} =~ s/^"(.*?)"//) {
122 return $this->_term($1, $attrhash);
125 my $word = $this->_word();
126 if ($word eq '@attrset') {
127 $attrset = $this->_word();
128 return $this->_parse($attrset, $attrhash);
130 } elsif ($word eq '@attr') {
131 $word = $this->_word();
134 $word = $this->_word();
136 my($type, $val) = ($word =~ /(.*)=(.*)/);
138 $h{"$attrset:$type"} = $val;
139 return $this->_parse($attrset, \%h);
141 } elsif ($word eq '@and' || $word eq '@or' || $word eq '@not') {
142 my $sub1 = $this->_parse($attrset, $attrhash);
143 my $sub2 = $this->_parse($attrset, $attrhash);
144 if ($word eq '@and') {
145 return new Net::Z3950::PQF::AndNode($sub1, $sub2);
146 } elsif ($word eq '@or') {
147 return new Net::Z3950::PQF::OrNode($sub1, $sub2);
148 } elsif ($word eq '@not') {
149 return new Net::Z3950::PQF::NotNode($sub1, $sub2);
151 die "Houston, we have a problem";
154 } elsif ($word eq '@prox') {
155 return $this->_error("proximity not yet implemented");
159 # It must be a bareword
160 return $this->_term($word, $attrhash);
164 # PRIVATE to _parse();
168 $this->{text} =~ s/^\s+//;
169 $this->{text} =~ s/^(\S+)//;
174 # PRIVATE to _parse();
179 $this->{errmsg} = join("", @msg);
184 # PRIVATE to _parse();
187 my($word, $attrhash) = @_;
190 foreach my $key (sort keys %$attrhash) {
191 my($attrset, $type) = split /:/, $key;
192 push @attrs, [ $attrset, $type, $attrhash->{$key} ];
195 return new Net::Z3950::PQF::TermNode($word, @attrs);
201 print $parser->errmsg();
203 Returns the last error-message generated by a failed attempt to parse
210 return $this->{errmsg};
216 The C<Net::Z3950::PQF::Node> module.
218 The definition of the Type-1 query in the Z39.50 standard, the
219 relevant section of which is on-line at
220 http://www.loc.gov/z3950/agency/markup/09.html#3.7
222 The documentation of Prefix Query Format in the YAZ Manual, the
223 relevant section of which is on-line at
224 http://indexdata.com/yaz/doc/tools.tkl#PQF
228 Mike Taylor, E<lt>mike@indexdata.comE<gt>
230 =head1 COPYRIGHT AND LICENSE
232 Copyright 2004 by Index Data ApS.
234 This library is free software; you can redistribute it and/or modify
235 it under the same terms as Perl itself.