File Coverage

blib/lib/RDF/Trine/Parser/Turtle.pm
Criterion Covered Total %
statement 307 316 97.1
branch 105 122 86.0
condition 45 54 83.3
subroutine 32 32 100.0
pod 4 4 100.0
total 493 528 93.3


line stmt bran cond sub pod time code
1             # RDF::Trine::Parser::Turtle
2             # -----------------------------------------------------------------------------
3              
4             =head1 NAME
5              
6             RDF::Trine::Parser::Turtle - Turtle RDF Parser
7              
8             =head1 VERSION
9              
10             This document describes RDF::Trine::Parser::Turtle version 1.018
11              
12             =head1 SYNOPSIS
13              
14             use RDF::Trine::Parser;
15             my $parser = RDF::Trine::Parser->new( 'turtle' );
16             $parser->parse_into_model( $base_uri, $data, $model );
17              
18             =head1 DESCRIPTION
19              
20             This module implements a parser for the Turtle RDF format.
21              
22             =head1 METHODS
23              
24             Beyond the methods documented below, this class inherits methods from the
25             L<RDF::Trine::Parser> class.
26              
27             =over 4
28              
29             =cut
30              
31             package RDF::Trine::Parser::Turtle;
32              
33 68     68   460 use utf8;
  68         169  
  68         488  
34 68     68   3054 use 5.010;
  68         308  
35 68     68   344 use strict;
  68         161  
  68         1279  
36 68     68   410 use warnings;
  68         200  
  68         2009  
37 68     68   402 use Scalar::Util qw(blessed);
  68         192  
  68         3495  
38 68     68   401 use base qw(RDF::Trine::Parser);
  68         180  
  68         4803  
39 68     68   445 use RDF::Trine::Error qw(:try);
  68         176  
  68         501  
40 68     68   9206 use Data::Dumper;
  68         172  
  68         2846  
41 68     68   24813 use RDF::Trine::Parser::Turtle::Constants;
  68         179  
  68         6725  
42 68     68   27257 use RDF::Trine::Parser::Turtle::Lexer;
  68         313  
  68         4047  
43 68     68   40179 use RDF::Trine::Parser::Turtle::Token;
  68         298  
  68         11219  
44              
45             our $VERSION;
46             BEGIN {
47 68     68   261 $VERSION = '1.018';
48 68         183 foreach my $ext (qw(ttl)) {
49 68         337 $RDF::Trine::Parser::file_extensions{ $ext } = __PACKAGE__;
50             }
51 68         206 $RDF::Trine::Parser::parser_names{ 'turtle' } = __PACKAGE__;
52 68         169 my $class = __PACKAGE__;
53 68         213 $RDF::Trine::Parser::encodings{ $class } = 'utf8';
54 68         203 $RDF::Trine::Parser::format_uris{ 'http://www.w3.org/ns/formats/Turtle' } = __PACKAGE__;
55 68         177 $RDF::Trine::Parser::canonical_media_types{ $class } = 'text/turtle';
56 68         162 foreach my $type (qw(application/x-turtle application/turtle text/turtle)) {
57 204         148743 $RDF::Trine::Parser::media_types{ $type } = __PACKAGE__;
58             }
59             }
60              
61             my $rdf = RDF::Trine::Namespace->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#');
62             my $xsd = RDF::Trine::Namespace->new('http://www.w3.org/2001/XMLSchema#');
63              
64             =item C<< new ( [ namespaces => $map ] ) >>
65              
66             Returns a new Turtle parser.
67              
68             =cut
69              
70             sub new {
71 386     386 1 141411 my $class = shift;
72 386         1239 my %args = @_;
73 386         2160 return bless({ %args, stack => [] }, $class);
74             }
75              
76             =item C<< parse ( $base_uri, $rdf, \&handler ) >>
77              
78             Parses the bytes in C<< $data >>, using the given C<< $base_uri >>. Calls the
79             C<< triple >> method for each RDF triple parsed. This method does nothing by
80             default, but can be set by using one of the default C<< parse_* >> methods.
81              
82             =cut
83              
84             sub parse {
85 250     250 1 1008 my $self = shift;
86 250         893 local($self->{baseURI}) = shift;
87 250         563 my $string = shift;
88             # warn 'parse() content: ' . Dumper($string); # XXX
89 250         679 local($self->{handle_triple}) = shift;
90 250         1968 require Encode;
91 250         1171 $string = Encode::encode("utf-8", $string);
92 250     12   20693 open(my $fh, '<:encoding(UTF-8)', \$string);
  12     10   71  
  12         26  
  12         76  
  10         7583  
  10         24  
  10         47  
93 250         30655 my $l = RDF::Trine::Parser::Turtle::Lexer->new($fh);
94 250         947 $self->_parse($l);
95             }
96              
97             =item C<< parse_file ( $base_uri, $fh, $handler ) >>
98              
99             Parses all data read from the filehandle or file C<< $fh >>, using the given
100             C<< $base_uri >>. If C<< $fh >> is a filename, this method can guess the
101             associated parse. For each RDF statement parses C<< $handler >> is called.
102              
103             =cut
104              
105             sub parse_file {
106 137     137 1 309 my $self = shift;
107 137         482 local($self->{baseURI}) = shift;
108 137         283 my $fh = shift;
109 137         405 local($self->{handle_triple}) = shift;
110              
111 137 100       441 unless (ref($fh)) {
112 5         8 my $filename = $fh;
113 5         9 undef $fh;
114 5 50       20 unless ($self->can('parse')) {
115 0         0 my $pclass = $self->guess_parser_by_filename( $filename );
116 0 0 0     0 $self = $pclass->new() if ($pclass and $pclass->can('new'));
117             }
118 5 50       151 open( $fh, '<:encoding(UTF-8)', $filename ) or throw RDF::Trine::Error::ParserError -text => $!;
119             }
120            
121 137         5831 my $l = RDF::Trine::Parser::Turtle::Lexer->new($fh);
122 137         544 $self->_parse($l);
123             }
124              
125             =item C<< parse_node ( $string, $base, [ token => \$token ] ) >>
126              
127             Returns the RDF::Trine::Node object corresponding to the node whose N-Triples
128             serialization is found at the beginning of C<< $string >>.
129             If a reference to C<< $token >> is given, it is dereferenced and set to the
130             RDF::Trine::Parser::Turtle::Token tokenizer object, allowing access to information such
131             as the token's position in the input string.
132              
133             =cut
134              
135             sub parse_node {
136 24     24 1 39 my $self = shift;
137 24         40 my $string = shift;
138 24         66 local($self->{baseURI}) = shift;
139 24         58 my %args = @_;
140 24         343 open(my $fh, '<:encoding(UTF-8)', \$string);
141 24         2861 my $l = RDF::Trine::Parser::Turtle::Lexer->new($fh);
142 24         71 my $t = $self->_next_nonws($l);
143 24 50       64 return unless ($t);
144 24         71 my $node = $self->_term($l, $t);
145 24         45 my $token_ref = $args{token};
146 24 50 33     119 if (defined($token_ref) and ref($token_ref)) {
147 24         47 $$token_ref = $t;
148             }
149 24         671 return $node;
150             }
151              
152             sub _parse {
153 387     387   759 my $self = shift;
154 387         731 my $l = shift;
155 387         1564 $l->check_for_bom;
156 387 100       1315 unless (exists($self->{map})) {
157 377         2809 $self->{map} = RDF::Trine::NamespaceMap->new();
158             }
159 387         1361 while (my $t = $self->_next_nonws($l)) {
160 1062         3657 $self->_statement($l, $t);
161             }
162             }
163              
164             ################################################################################
165              
166             sub _unget_token {
167 3749     3749   6663 my $self = shift;
168 3749         5874 my $t = shift;
169 3749         5528 push(@{ $self->{ stack } }, $t);
  3749         8657  
170             }
171              
172             sub _next_nonws {
173 13365     13365   20549 my $self = shift;
174 13365         19753 my $l = shift;
175 13365 100       19201 if (scalar(@{ $self->{ stack } })) {
  13365         35023  
176 3749         5879 return pop(@{ $self->{ stack } });
  3749         8937  
177             }
178 9616         14924 while (1) {
179 9616         28622 my $t = $l->get_token;
180 9583 100       33685 return unless ($t);
181 9282         232016 my $type = $t->type;
182             # next if ($type == WS or $type == COMMENT);
183             # warn decrypt_constant($type) . "\n";
184 9282         23499 return $t;
185             }
186             }
187              
188             sub _get_token_type {
189 1538     1538   2972 my $self = shift;
190 1538         2700 my $l = shift;
191 1538         2752 my $type = shift;
192 1538         3812 my $t = $self->_next_nonws($l);
193 1537 100       4200 unless ($t) {
194 3         13 $l->_throw_error(sprintf("Expecting %s but got EOF", decrypt_constant($type)));
195 0         0 return;
196             }
197 1534 100       37540 unless ($t->type eq $type) {
198 12         50 $self->_throw_error(sprintf("Expecting %s but got %s", decrypt_constant($type), decrypt_constant($t->type)), $t, $l);
199             }
200 1522         20547 return $t;
201             }
202              
203             sub _statement {
204 1039     1039   2085 my $self = shift;
205 1039         1888 my $l = shift;
206 1039         1819 my $t = shift;
207 1039         25346 my $type = $t->type;
208             # when (WS) {}
209 1039 100 100     8475 if ($type == PREFIX or $type == SPARQLPREFIX) {
    100 100        
210 245         693 $t = $self->_get_token_type($l, PREFIXNAME);
211 241         826 my $name = $t->value;
212 241         1002 $name =~ s/:$//;
213 241         774 $t = $self->_get_token_type($l, IRI);
214 239         6231 my $r = RDF::Trine::Node::Resource->new($t->value, $self->{baseURI});
215 239         868 my $iri = $r->uri_value;
216 239 100       796 if ($type == PREFIX) {
217 236         777 $t = $self->_get_token_type($l, DOT);
218             # $t = $self->_next_nonws($l);
219             # if ($t and $t->type != DOT) {
220             # $self->_unget_token($t);
221             # }
222             }
223 239         6190 $self->{map}->add_mapping( $name => $iri );
224 239 100       3183 if (my $ns = $self->{namespaces}) {
225 2 50       8 unless ($ns->namespace_uri($name)) {
226 2         6 $ns->add_mapping( $name => $iri );
227             }
228             }
229             }
230             elsif ($type == BASE or $type == SPARQLBASE) {
231 11         45 $t = $self->_get_token_type($l, IRI);
232 10         46 my $r = RDF::Trine::Node::Resource->new($t->value, $self->{baseURI});
233 10         40 my $iri = $r->uri_value;
234 10 100       40 if ($type == BASE) {
235 6         24 $t = $self->_get_token_type($l, DOT);
236             # $t = $self->_next_nonws($l);
237             # if ($t and $t->type != DOT) {
238             # $self->_unget_token($t);
239             # }
240             }
241 10         207 $self->{baseURI} = $iri;
242             }
243             else {
244 783         2859 $self->_triple( $l, $t );
245 709         2740 $t = $self->_get_token_type($l, DOT);
246             }
247             # }
248             }
249              
250             sub _triple {
251 783     783   1479 my $self = shift;
252 783         1443 my $l = shift;
253 783         1339 my $t = shift;
254 783         19649 my $type = $t->type;
255             # subject
256 783         1671 my $subj;
257 783         2012 my $bnode_plist = 0;
258 783 100 100     5995 if ($type == LBRACKET) {
    100          
    100          
259 40         85 $bnode_plist = 1;
260 40         299 $subj = RDF::Trine::Node::Blank->new();
261 40         128 my $t = $self->_next_nonws($l);
262 40 100       994 if ($t->type != RBRACKET) {
263 11         52 $self->_unget_token($t);
264 11         46 $self->_predicateObjectList( $l, $subj );
265 11         41 $t = $self->_get_token_type($l, RBRACKET);
266             }
267             } elsif ($type == LPAREN) {
268 4         17 my $t = $self->_next_nonws($l);
269 4 50       95 if ($t->type == RPAREN) {
270 0         0 $subj = RDF::Trine::Node::Resource->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#nil');
271             } else {
272 4         38 $subj = RDF::Trine::Node::Blank->new();
273 4         22 my @objects = $self->_object($l, $t);
274            
275 4         9 while (1) {
276 6         20 my $t = $self->_next_nonws($l);
277 6 100       269 if ($t->type == RPAREN) {
278 4         106 last;
279             } else {
280 2         9 push(@objects, $self->_object($l, $t));
281             }
282             }
283 4         21 $self->_assert_list($subj, @objects);
284             }
285             } elsif (not($type==IRI or $type==PREFIXNAME or $type==BNODE)) {
286 21         76 $self->_throw_error("Expecting resource or bnode but got " . decrypt_constant($type), $t, $l);
287             } else {
288 718         2321 $subj = $self->_token_to_node($t);
289             }
290             # warn "Subject: $subj\n"; # XXX
291            
292 758 100       2794 if ($bnode_plist) {
293             #predicateObjectList?
294 40         125 $t = $self->_next_nonws($l);
295 40         173 $self->_unget_token($t);
296 40 100       998 if ($t->type != DOT) {
297 34         141 $self->_predicateObjectList($l, $subj);
298             }
299             } else {
300             #predicateObjectList
301 718         2735 $self->_predicateObjectList($l, $subj);
302             }
303             }
304              
305             sub _assert_list {
306 24     24   63 my $self = shift;
307 24         50 my $subj = shift;
308 24         110 my @objects = @_;
309 24         54 my $head = $subj;
310 24         105 while (@objects) {
311 330         663 my $obj = shift(@objects);
312 330         1907 $self->_assert_triple($head, $rdf->first, $obj);
313 330 100       1435 my $next = scalar(@objects) ? RDF::Trine::Node::Blank->new() : $rdf->nil;
314 330         1849 $self->_assert_triple($head, $rdf->rest, $next);
315 330         1746 $head = $next;
316             }
317             }
318              
319             sub _predicateObjectList {
320 817     817   1679 my $self = shift;
321 817         1626 my $l = shift;
322 817         1554 my $subj = shift;
323 817         2198 my $t = $self->_next_nonws($l);
324 811         1603 while (1) {
325 2208         53026 my $type = $t->type;
326 2208 100 100     12004 unless ($type==IRI or $type==PREFIXNAME or $type==A) {
      100        
327 18         60 $self->_throw_error("Expecting verb but got " . decrypt_constant($type), $t, $l);
328             }
329 2190         6487 my $pred = $self->_token_to_node($t);
330 2187         6846 $self->_objectList($l, $subj, $pred);
331            
332 2166         5892 $t = $self->_next_nonws($l);
333 2166 100       54272 last unless ($t);
334 2163 100       52728 if ($t->type == SEMICOLON) {
335 1690         2956 my $sc = $t;
336 1694         4087 SEMICOLON_REPEAT:
337             $t = $self->_next_nonws($l);
338 1694 100       4134 unless ($t) {
339 1         4 $l->_throw_error("Expecting token after semicolon, but got EOF");
340             }
341 1693 100       39660 goto SEMICOLON_REPEAT if ($t->type == SEMICOLON);
342 1689 100 100     39105 if ($t->type == IRI or $t->type == PREFIXNAME or $t->type == A) {
      66        
343 1397         36041 next;
344             } else {
345 292         1101 $self->_unget_token($t);
346 292         7722 return;
347             }
348             } else {
349 473         1472 $self->_unget_token($t);
350 473         1885 return;
351             }
352             }
353             }
354              
355             sub _objectList {
356 2187     2187   4218 my $self = shift;
357 2187         3661 my $l = shift;
358 2187         3341 my $subj = shift;
359 2187         3264 my $pred = shift;
360             # warn "objectList: " . Dumper($subj, $pred); # XXX
361 2187         3281 while (1) {
362 2208         5456 my $t = $self->_next_nonws($l);
363 2197 100       5811 last unless ($t);
364 2195         6625 my $obj = $self->_object($l, $t);
365 2188         7077 $self->_assert_triple($subj, $pred, $obj);
366            
367 2188         6553 $t = $self->_next_nonws($l);
368 2185 100 100     62354 if ($t and $t->type == COMMA) {
369 21         469 next;
370             } else {
371 2164         8121 $self->_unget_token($t);
372 2164         4917 return;
373             }
374             }
375             }
376              
377             sub _assert_triple {
378 2817     2817   4682 my $self = shift;
379 2817         4827 my $subj = shift;
380 2817         4305 my $pred = shift;
381 2817         4176 my $obj = shift;
382 2817 100 66     24996 if ($self->{canonicalize} and blessed($obj) and $obj->isa('RDF::Trine::Node::Literal')) {
      100        
383 588         2366 $obj = $obj->canonicalize;
384             }
385            
386 2817         13968 my $t = RDF::Trine::Statement->new($subj, $pred, $obj);
387 2817 100       8344 if ($self->{handle_triple}) {
388 2613         8965 $self->{handle_triple}->( $t );
389             }
390             }
391              
392             sub _object {
393 2527     2527   4356 my $self = shift;
394 2527         4289 my $l = shift;
395 2527         4256 my $t = shift;
396 2527         60467 my $type = $t->type;
397 2527         4468 my $tcopy = $t;
398 2527         3806 my $obj;
399 2527 100 100     19678 if ($type==LBRACKET) {
    100          
    100          
400 36         186 $obj = RDF::Trine::Node::Blank->new();
401 36         119 my $t = $self->_next_nonws($l);
402 36 50       124 unless ($t) {
403 0         0 $self->_throw_error("Expecting object but got only opening bracket", $tcopy, $l);
404             }
405 36 100       880 if ($t->type != RBRACKET) {
406 28         105 $self->_unget_token($t);
407 28         127 $self->_predicateObjectList( $l, $obj );
408 28         88 $t = $self->_get_token_type($l, RBRACKET);
409             }
410             } elsif ($type == LPAREN) {
411 27         93 my $t = $self->_next_nonws($l);
412 27 50       90 unless ($t) {
413 0         0 $self->_throw_error("Expecting object but got only opening paren", $tcopy, $l);
414             }
415 27 100       668 if ($t->type == RPAREN) {
416 6         30 $obj = RDF::Trine::Node::Resource->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#nil');
417             } else {
418 21         166 $obj = RDF::Trine::Node::Blank->new();
419 21         103 my @objects = $self->_object($l, $t);
420            
421 21         50 while (1) {
422 325         1023 my $t = $self->_next_nonws($l);
423 325 100       8158 if ($t->type == RPAREN) {
424 20         554 last;
425             } else {
426 305         979 push(@objects, $self->_object($l, $t));
427             }
428             }
429 20         114 $self->_assert_list($obj, @objects);
430             }
431             } elsif (not($type==IRI or $type==PREFIXNAME or $type==STRING1D or $type==STRING3D or $type==STRING1S or $type==STRING3S or $type==BNODE or $type==INTEGER or $type==DECIMAL or $type==DOUBLE or $type==BOOLEAN)) {
432 2         13 $self->_throw_error("Expecting object but got " . decrypt_constant($type), $t, $l);
433             } else {
434 2462         6895 $obj = $self->_term($l, $t);
435             }
436 2519         14319 return $obj;
437             }
438              
439             sub _term {
440 2486     2486   4448 my $self = shift;
441 2486         3682 my $l = shift;
442 2486         3851 my $t = shift;
443 2486         3784 my $tcopy = $t;
444 2486         3601 my $obj;
445 2486         61269 my $type = $t->type;
446 2486 100 100     16790 if ($type==STRING1D or $type==STRING3D or $type==STRING1S or $type==STRING3S) {
      100        
      100        
447 781         2626 my $value = $t->value;
448 781         2218 my $t = $self->_next_nonws($l);
449 778         1872 my $dt;
450             my $lang;
451 778 50       2390 if ($t) {
452 778 100       18683 if ($t->type == HATHAT) {
    100          
453 36         133 my $t = $self->_next_nonws($l);
454 36 50 66     862 if ($t->type == IRI or $t->type == PREFIXNAME) {
455 36         138 $dt = $self->_token_to_node($t);
456             }
457             } elsif ($t->type == LANG) {
458 15         55 $lang = $t->value;
459             } else {
460 727         2202 $self->_unget_token($t);
461             }
462             }
463 778         4477 $obj = RDF::Trine::Node::Literal->new($value, $lang, $dt);
464             } else {
465 1705         4785 $obj = $self->_token_to_node($t, $type);
466             }
467 2482         5983 return $obj;
468             }
469              
470             sub _token_to_node {
471 4682     4682   7932 my $self = shift;
472 4682         7884 my $t = shift;
473 4682   66     79405 my $type = shift || $t->type;
474 4682 100       23092 if ($type eq A) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    0          
    0          
475 55         487 return $rdf->type;
476             }
477             elsif ($type eq IRI) {
478 1599         5193 return RDF::Trine::Node::Resource->new($t->value, $self->{baseURI});
479             }
480             elsif ($type eq INTEGER) {
481 59         215 return RDF::Trine::Node::Literal->new($t->value, undef, $xsd->integer);
482             }
483             elsif ($type eq DECIMAL) {
484 11         61 return RDF::Trine::Node::Literal->new($t->value, undef, $xsd->decimal);
485             }
486             elsif ($type eq DOUBLE) {
487 9         32 return RDF::Trine::Node::Literal->new($t->value, undef, $xsd->double);
488             }
489             elsif ($type eq BOOLEAN) {
490 8         41 return RDF::Trine::Node::Literal->new($t->value, undef, $xsd->boolean);
491             }
492             elsif ($type eq PREFIXNAME) {
493 2860         4499 my ($ns, $local) = @{ $t->args };
  2860         70081  
494 2860         10836 $ns =~ s/:$//;
495 2860         12358 my $prefix = $self->{map}->namespace_uri($ns);
496 2860 100       11813 unless (blessed($prefix)) {
497 8         35 $self->_throw_error("Use of undeclared prefix '$ns'", $t);
498             }
499 2852         10872 my $iri = $prefix->uri($local);
500 2852         7000 return $iri;
501             }
502             elsif ($type eq BNODE) {
503 81         321 return RDF::Trine::Node::Blank->new($t->value);
504             }
505             elsif ($type eq STRING1D) {
506 0         0 return RDF::Trine::Node::Literal->new($t->value);
507             }
508             elsif ($type eq STRING1S) {
509 0         0 return RDF::Trine::Node::Literal->new($t->value);
510             }
511             else {
512 0         0 $self->_throw_error("Converting $type to node not implemented", $t);
513             }
514             }
515              
516             sub _throw_error {
517 61     61   115 my $self = shift;
518 61         95 my $message = shift;
519 61         96 my $t = shift;
520 61         83 my $l = shift;
521 61         1607 my $line = $t->start_line;
522 61         1580 my $col = $t->start_column;
523             # Carp::cluck "$message at $line:$col";
524 61         186 my $text = "$message at $line:$col";
525 61 100       176 if (defined($t->value)) {
526 30         81 $text .= " (near '" . $t->value . "')";
527             }
528             RDF::Trine::Error::ParserError::Tokenized->throw(
529 61         601 -text => $text,
530             -object => $t,
531             );
532             }
533              
534             1;
535              
536             __END__
537              
538             =back
539              
540             =head1 BUGS
541              
542             Please report any bugs or feature requests to through the GitHub web interface
543             at L<https://github.com/kasei/perlrdf/issues>.
544              
545             =head1 AUTHOR
546              
547             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
548              
549             =head1 COPYRIGHT
550              
551             Copyright (c) 2006-2012 Gregory Todd Williams. This
552             program is free software; you can redistribute it and/or modify it under
553             the same terms as Perl itself.
554              
555             =cut