File Coverage

blib/lib/ODO/Query/RDQL/Parser.pm
Criterion Covered Total %
statement 35 47 74.4
branch 5 20 25.0
condition 5 12 41.6
subroutine 8 9 88.8
pod 3 3 100.0
total 56 91 61.5


line stmt bran cond sub pod time code
1             #
2             # Copyright (c) 2004-2006 IBM Corporation.
3             #
4             # All rights reserved. This program and the accompanying materials
5             # are made available under the terms of the Eclipse Public License v1.0
6             # which accompanies this distribution, and is available at
7             # http://www.eclipse.org/legal/epl-v10.html
8             #
9             # File: $Source: /var/lib/cvs/ODO/lib/ODO/Query/RDQL/Parser.pm,v $
10             # Created by: Stephen Evanchik( evanchik@us.ibm.com )
11             # Created on: 10/05/2004
12             # Revision: $Id: Parser.pm,v 1.2 2009-11-25 17:53:53 ubuntu Exp $
13             #
14             # Contributors:
15             # IBM Corporation - initial API and implementation
16             #
17             package ODO::Query::RDQL::Parser;
18              
19 5     5   1479 use strict;
  5         9  
  5         174  
20 5     5   24 use warnings;
  5         10  
  5         137  
21              
22 5     5   2224 use ODO::Exception;
  5         10  
  5         235  
23 5     5   26 use vars qw /$VERSION/;
  5         12  
  5         416  
24             $VERSION = sprintf "%d.%02d", q$Revision: 1.2 $ =~ /: (\d+)\.(\d+)/;
25 5     5   9831 use Parse::RecDescent;
  5         237960  
  5         44  
26              
27 5     5   284 use base qw/ODO/;
  5         13  
  5         4447  
28              
29             =head1 NAME
30              
31             ODO::Query::RDQL::Parser
32              
33             =head1 SYNOPSIS
34              
35             =head1 DESCRIPTION
36              
37             =head1 INTERNALS
38              
39             =over
40              
41             =item $PARSER
42              
43             =cut
44              
45             our $PARSER = undef;
46              
47             # $::RD_ERRORS =1; # unless undefined, report fatal errors
48             # $::RD_WARN =1; # unless undefined, also report non-fatal problems
49             # $::RD_HINT =1; # if defined, also suggestion remedies
50             # $::RD_TRACE =1; # if defined, also trace parsers' behaviour
51              
52             =item $RDQL_GRAMMAR
53              
54             =cut
55              
56             our $RDQL_GRAMMAR = q(
57              
58             # $::QNames is the QName array list.
59             # It is processed during the Query object construction phase to fully qualify the
60             # QName Resource nodes with the appropriate URI
61             {
62             use ODO::Node;
63             use ODO::Statement;
64             use ODO::Query::Simple;
65             use ODO::Query::Constraint;
66            
67             use ODO::Query::RDQL;
68             }
69              
70             QueryStart: { $::QNames = [ ]; $::Query = ODO::Query::RDQL->new(); } Query
71              
72             Query: SelectClause SourceClause(?) StatementPatternClause ConstraintClause(?) PrefixesClause(?) /^\Z/
73             {
74             # Collapse the nested arrays
75             # This probably could be done in the parse itself
76             my $constraints = [];
77              
78             while( UNIVERSAL::isa($item[4]->[0], 'ARRAY')
79             && @{ $item[4]->[0] }) {
80            
81             my $c = shift @{ $item[4]->[0] };
82            
83             unless(ref $c eq 'ARRAY') {
84             push @{ $constraints }, $c;
85             next;
86             }
87              
88             foreach my $innerc (@{ $c }) {
89             push @{ $item[4]->[0] }, $innerc;
90             }
91             }
92            
93             $::Query->constraints($constraints);
94              
95             $return = {
96             source=> $item[2],
97             query=> $::Query,
98             qnames=> $::QNames,
99             };
100             }
101              
102              
103             SelectClause: /select/i Variable(s /,/)
104             {
105             $::Query->{'result_vars'}->{'#variables'} = $item[2];
106            
107             1;
108             }
109             | /select/i '*'
110             {
111             push @{ $::Query->{'result_vars'}->{'#variables'} }, $ODO::Node::ANY;
112            
113             1;
114             }
115              
116             Variable: /(^[?](\w|[-.])+)/
117             {
118             $return = $item{'__PATTERN1__'};
119             chomp($return);
120             $return =~ s/^[?]//;
121             $return = ODO::Node::Variable->new($return);
122            
123             1;
124             }
125              
126             SourceClause: SourceFrom SourceSelector(s /,/)
127              
128             SourceFrom: /(source|from)/i { $return = lc($item{'__PATTERN1__'}); 1; }
129              
130             SourceSelector: QName
131              
132             StatementPatternClause: /where/i StatementPattern(s /,/) { $return = $item[2]; 1; }
133              
134             StatementPattern: '(' VarOrURI ',' VarOrURI ',' VarOrConst ')'
135             {
136             push @{ $::Query->{'statement_patterns'}->{'#patterns'} }, ODO::Query::Simple->new( $item[2], $item[4], $item[6]);
137            
138             1;
139             }
140              
141             VarOrURI: Variable
142             | URI
143            
144             VarOrConst:
145             Variable
146             | Const
147              
148             ConstraintClause:
149              
150             PrefixesClause: /using/i PrefixDecl(s? /,/) { $return = $item[2]; 1; }
151              
152             PrefixDecl: idchars /for/i '<' URIChars '>'
153             {
154             $::Query->{'prefixes'}->{$item[1]} = $item[4];
155            
156             1;
157             }
158              
159             Const: URI
160             | NumericLiteral { $return = ODO::Node::Literal->new($item[1]); 1; }
161             | TextLiteral { $return = ODO::Node::Literal->new($item[1]); 1; }
162             | BooleanLiteral { $return = ODO::Node::Literal->new($item[1]); 1; }
163             | NullLiteral { $return = ODO::Node::Literal->new($item[1]); 1; }
164              
165             URI: '<' URIChars '>' { $return = $item[2]; }
166             {
167             $return = ODO::Node::Resource->new($item[2]);
168            
169             1;
170             }
171             | QName
172             {
173             $return = ODO::Node::Resource->new($item[1]);
174              
175             push @{ $::QNames }, $return;
176            
177             1;
178             }
179              
180             QName: NSPrefix ':' LocalPart { $return = $item[1] . ':' . $item[3]; }
181              
182             NSPrefix: idchars { $return = $item[1]; }
183              
184             LocalPart: /[^ \t<>(),.;'"+=]+/ { $return = $item{'__PATTERN1__'}; }
185              
186             NumericLiteral:
187             /([0-9]+)/ { $return = $item{'__PATTERN1__'}; chomp($return); }
188             | /(([0-9])*'.'([0-9])+('e'('+'|'-')?([0-9])+)?)/ { $return = $item{'__PATTERN1__'}; chomp($return); 1; }
189              
190             NullLiteral: /null/i { $return = 'null'; 1; }
191              
192             TextLiteral: /"/ idchars /"/ { $return = $item[1]; 1; }
193             | /'/ idchars /'/ { $return = $item[1]; 1; }
194              
195             BooleanLiteral: /true|false/i { $return = lc($item{'__PATTERN1__'}); 1; }
196              
197             idchars: /(([a-zA-Z0-9]|[\-_\.])+)/ { $return = $item{'__PATTERN1__'}; chomp($return); 1; }
198              
199             URIChars: /([A-Za-z0-9]|[:.\-_\/#])+/ { $return = $item{'__PATTERN1__'}; chomp($return); 1; }
200              
201             );
202              
203              
204             =item $CONSTRAINTS
205              
206             =cut
207              
208             our $CONSTRAINTS = q(
209              
210            
211              
212             {
213             # Used with rules that follow the RuleName: Operation TailRuleName
214             $::TailStub = sub {
215              
216             my @item = @{ $_[0] };
217             my %item = %{ $_[1] };
218              
219             if($item[2] eq 'Tail' . $item[0]) {
220             return $item[1];
221             }
222             else {
223             if(UNIVERSAL::isa($item[2], 'ODO::Query::Constraint')) {
224             $item[2]->left($item[1]);
225             return $item[2];
226             }
227             else {
228            
229             # FIXME: This is an error condition
230             return bless \%item, $item[0];
231             }
232             }
233             };
234            
235             # This occurs only during unary actions with an operator
236             $::UnaryAction = sub {
237             my @item = @{ $_[0] };
238             # my %item = %{ $_[1] }; # Not needed but preserved
239              
240             return ODO::Query::Constraint->new(operation=> $item[1], is_unary=> 1, left=> $item[2]);
241             };
242            
243             # Evaluated in the TailRuleName productions
244             $::MakeTailConstraint = sub {
245             my @item = @{ $_[0] };
246             # my %item = %{ $_[1] }; # Not needed but preserved
247              
248             return ODO::Query::Constraint->new(operation=> $item[1], right=> $item[2]);
249             };
250             }
251              
252             ConstraintClause: /and/i ExpressionList
253             { $return = $item[2]; }
254              
255             ExpressionList: ConditionalOrExpression TailExpressionList
256             {
257             $return = [ $item[1] ];
258            
259             push @{ $return }, $item[2]
260             if(ref $item[2]);
261             }
262              
263             TailExpressionList: ',' ExpressionList
264             { $return = $item[2]; }
265             | # Empty
266              
267             ConditionalOrExpression: ConditionalAndExpression TailConditionalOrExpression
268             { $return = $::TailStub->(\@item, \%item); }
269            
270             TailConditionalOrExpression:
271             '||' ConditionalOrExpression
272             { $return = $::MakeTailConstraint->(\@item, \%item); }
273             | # Empty
274              
275             ConditionalAndExpression: StringEqualityExpression TailConditionalAndExpression
276             { $return = $::TailStub->(\@item, \%item); }
277              
278             TailConditionalAndExpression:
279             '&&' ConditionalAndExpression
280             { $return = $::MakeTailConstraint->(\@item, \%item); }
281             | # Empty
282              
283             StringEqualityExpression: InclusiveOrExpression TailStringEqualityExpression
284             { $return = $::TailStub->(\@item, \%item); }
285              
286             TailStringEqualityExpression:
287             /eq|ne/ StringEqualityExpression
288             { $return = $::MakeTailConstraint->(\@item, \%item); }
289             | # Empty
290              
291             InclusiveOrExpression: ExclusiveOrExpression TailInclusiveOrExpression
292             { $return = $::TailStub->(\@item, \%item); }
293            
294             TailInclusiveOrExpression:
295             /[|]/ InclusiveOrExpression
296             { $return = $::MakeTailConstraint->(\@item, \%item); }
297             | # Empty
298              
299             ExclusiveOrExpression: AndExpression TailExclusiveOrExpression
300             { $return = $::TailStub->(\@item, \%item); }
301              
302             TailExclusiveOrExpression:
303             /[\^]/ ExclusiveOrExpression
304             { $return = $::MakeTailConstraint->(\@item, \%item); }
305             | # Empty
306            
307             AndExpression: EqualityExpression TailAndExpression
308             { $return = $::TailStub->(\@item, \%item); }
309              
310             TailAndExpression:
311             /[&]/ AndExpression
312             { $return = $::MakeTailConstraint->(\@item, \%item); }
313             | # Empty
314              
315             EqualityExpression:
316             RelationalExpression /==|!=/ RelationalExpression
317             {
318             $return = ODO::Query::Constraint->new(operation=> $item[2], left=> $item[1], right=> $item[3]);
319             }
320             | RelationalExpression
321             { $return = $item[1]; }
322              
323             RelationalExpression:
324             ShiftExpression /<|>|<=|>=/ ShiftExpression
325             {
326             $return = ODO::Query::Constraint->new(operation=> $item[2], left=> $item[1], right=> $item[3]);
327             }
328             | ShiftExpression
329             { $return = $item[1]; }
330              
331             ShiftExpression: AdditiveExpression TailShiftExpression
332             { $return = $::TailStub->(\@item, \%item); }
333              
334             TailShiftExpression:
335             /<<|>>>|>>/ ShiftExpression
336             { $return = $::MakeTailConstraint->(\@item, \%item); }
337             | # Empty
338              
339             AdditiveExpression: MultiplicativeExpression TailAdditiveExpression
340             { $return = $::TailStub->(\@item, \%item); }
341              
342             TailAdditiveExpression:
343             /[+-]/ AdditiveExpression
344             { $return = $::MakeTailConstraint->(\@item, \%item); }
345             | # Empty
346              
347             MultiplicativeExpression: UnaryExpression TailMultiplicativeExpression
348             { $return = $::TailStub->(\@item, \%item); }
349              
350             TailMultiplicativeExpression:
351             /[*\/%]/ MultiplicativeExpression
352             { $return = $::MakeTailConstraint->(\@item, \%item); }
353             | # Empty
354              
355             UnaryExpression:
356             UnaryExpressionNotPlusMinus
357             { $return = $item[1]; }
358             | /[+-]/ UnaryExpression
359             { $return = $::UnaryAction->(\@item, \%item); }
360              
361             UnaryExpressionNotPlusMinus:
362             /[~!]/ UnaryExpression
363             { $return = $::UnaryAction->(\@item, \%item); }
364             | PrimaryExpression
365             { $return = $item[1]; }
366            
367             PrimaryExpression:
368             Variable
369             {
370             $return = $item[1];
371             }
372             | Const
373             {
374             $return = $item[1];
375            
376             $return = ODO::Node::Literal->new($item[1])
377             unless(UNIVERSAL::isa($item[1], 'ODO::Node'));
378             }
379             | '(' ConditionalOrExpression ')'
380             { # Formerly just Expression
381             $return = $item[2];
382             }
383             | FunctionCall
384              
385             FunctionCall: idchars '(' ArgList ')'
386              
387             ArgList: VarOrConst(s /,/) { $return = $item[1]; }
388              
389             PatternLiteral: idchars { $return = $item[1]; }
390              
391             );
392              
393              
394             =back
395              
396             # TODO: Fix URIChars so that it is a URI regexp
397             =head1 METHODS
398              
399             =over
400              
401             =item parse( $rdql )
402              
403             =cut
404              
405             sub parse {
406 2     2 1 1056 my ($self, $rdql) = @_;
407              
408 2         11 chomp($rdql);
409              
410 2 100       17 unless(UNIVERSAL::isa($PARSER, 'Parse::RecDescent')) {
411 1         12 $PARSER = Parse::RecDescent->new($RDQL_GRAMMAR);
412 1         111383 $PARSER->Replace($CONSTRAINTS);
413             }
414              
415 2         230824 return $self->build_query_object($PARSER->QueryStart($rdql));
416             }
417              
418              
419             =item build_query_object( $parse_tree )
420              
421             =cut
422              
423             sub build_query_object {
424 2     2 1 6320 my ($self, $parse_tree) = @_;
425            
426 2         7 my $query = $parse_tree->{'query'};
427            
428             # Resolve all Resource nodes that contain QNames to fully qualified
429             # URIs
430 2   66     18 while( UNIVERSAL::isa($parse_tree->{'qnames'}, 'ARRAY' )
  42         434  
431             && @{ $parse_tree->{'qnames'} } ) {
432            
433 40         47 my $qname = shift @{ $parse_tree->{'qnames'} };
  40         77  
434             next
435 40 50       126 unless(UNIVERSAL::isa($qname, 'ODO::Node::Resource'));
436              
437             # If applicable, exploit the fact that this is a reference to
438             # the ODO::Node::Resource object and reset the uri
439             # to the appropriate value
440 40 50       98 if($qname->uri() =~ /(\w+)[:](\w+)/) {
441 40         343 my $uri_frag = $query->{'prefixes'}->{$1};
442 40 50 66     307 $qname->uri($uri_frag . $2)
      33        
443             if($uri_frag && $1 && $2); # FIXME: Figure out why this is necessary
444             }
445             }
446            
447 2         13 return $query;
448             }
449              
450              
451             =item print_rule_info( $rule_info, $indent, $parent, [ $fh ] )
452              
453             =cut
454              
455             sub print_rule_info {
456 0     0 1   my ($self, $rule_info, $indent, $parent, $fh) = @_;
457            
458 0 0         $fh = \*STDERR
459             unless(defined $fh);
460            
461 0           my $space = ' ' x $indent;
462 0           foreach my $key (sort(keys(%{ $rule_info }))) {
  0            
463 0 0         print $fh $space, $rule_info->{$key}, "\n"
464             if($key eq '__RULE__');
465            
466 0           my $tail_name = 'Tail';
467 0 0         $tail_name .= $rule_info->{'__RULE__'}
468             if(exists($rule_info->{'__RULE__'}));
469            
470 0 0 0       print $fh $space, "CONSTRAINT: $tail_name\n"
471             if( exists($rule_info->{$tail_name})
472             && UNIVERSAL::isa($rule_info->{$tail_name}, 'HASH') );
473            
474 0 0         print $fh $space, $rule_info->{$key}->value(), "\n"
475             if(UNIVERSAL::isa($rule_info->{$key}, 'ODO::Node'));
476            
477 0 0         if(UNIVERSAL::isa($rule_info->{$key}, 'HASH')) {
478 0           $self->print_rule_info($rule_info->{$key}, $indent + 2, $rule_info, $fh);
479             }
480             else {
481            
482             }
483             }
484             }
485              
486              
487             =back
488              
489             =head1 COPYRIGHT
490              
491             Copyright (c) 2004-2006 IBM Corporation.
492              
493             All rights reserved. This program and the accompanying materials
494             are made available under the terms of the Eclipse Public License v1.0
495             which accompanies this distribution, and is available at
496             http://www.eclipse.org/legal/epl-v10.html
497              
498             =cut
499              
500             1;
501              
502             __END__