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.017
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   463 use utf8;
  68         173  
  68         496  
34 68     68   3021 use 5.010;
  68         283  
35 68     68   357 use strict;
  68         161  
  68         1242  
36 68     68   325 use warnings;
  68         169  
  68         1907  
37 68     68   423 use Scalar::Util qw(blessed);
  68         173  
  68         3315  
38 68     68   400 use base qw(RDF::Trine::Parser);
  68         160  
  68         4983  
39 68     68   447 use RDF::Trine::Error qw(:try);
  68         179  
  68         475  
40 68     68   8935 use Data::Dumper;
  68         162  
  68         2810  
41 68     68   24621 use RDF::Trine::Parser::Turtle::Constants;
  68         183  
  68         6857  
42 68     68   27585 use RDF::Trine::Parser::Turtle::Lexer;
  68         318  
  68         4403  
43 68     68   41563 use RDF::Trine::Parser::Turtle::Token;
  68         360  
  68         11967  
44              
45             our $VERSION;
46             BEGIN {
47 68     68   270 $VERSION = '1.017';
48 68         186 foreach my $ext (qw(ttl)) {
49 68         358 $RDF::Trine::Parser::file_extensions{ $ext } = __PACKAGE__;
50             }
51 68         199 $RDF::Trine::Parser::parser_names{ 'turtle' } = __PACKAGE__;
52 68         161 my $class = __PACKAGE__;
53 68         213 $RDF::Trine::Parser::encodings{ $class } = 'utf8';
54 68         206 $RDF::Trine::Parser::format_uris{ 'http://www.w3.org/ns/formats/Turtle' } = __PACKAGE__;
55 68         184 $RDF::Trine::Parser::canonical_media_types{ $class } = 'text/turtle';
56 68         157 foreach my $type (qw(application/x-turtle application/turtle text/turtle)) {
57 204         154188 $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 164170 my $class = shift;
72 386         1443 my %args = @_;
73 386         2190 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 1145 my $self = shift;
86 250         937 local($self->{baseURI}) = shift;
87 250         652 my $string = shift;
88             # warn 'parse() content: ' . Dumper($string); # XXX
89 250         776 local($self->{handle_triple}) = shift;
90 250         1969 require Encode;
91 250         1404 $string = Encode::encode("utf-8", $string);
92 250     12   22962 open(my $fh, '<:encoding(UTF-8)', \$string);
  12     10   81  
  12         27  
  12         85  
  10         7772  
  10         25  
  10         50  
93 250         32527 my $l = RDF::Trine::Parser::Turtle::Lexer->new($fh);
94 250         1025 $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 332 my $self = shift;
107 137         559 local($self->{baseURI}) = shift;
108 137         320 my $fh = shift;
109 137         522 local($self->{handle_triple}) = shift;
110              
111 137 100       607 unless (ref($fh)) {
112 5         11 my $filename = $fh;
113 5         11 undef $fh;
114 5 50       25 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       214 open( $fh, '<:encoding(UTF-8)', $filename ) or throw RDF::Trine::Error::ParserError -text => $!;
119             }
120            
121 137         7017 my $l = RDF::Trine::Parser::Turtle::Lexer->new($fh);
122 137         678 $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         53 my %args = @_;
140 24         314 open(my $fh, '<:encoding(UTF-8)', \$string);
141 24         2790 my $l = RDF::Trine::Parser::Turtle::Lexer->new($fh);
142 24         58 my $t = $self->_next_nonws($l);
143 24 50       60 return unless ($t);
144 24         68 my $node = $self->_term($l, $t);
145 24         43 my $token_ref = $args{token};
146 24 50 33     107 if (defined($token_ref) and ref($token_ref)) {
147 24         44 $$token_ref = $t;
148             }
149 24         700 return $node;
150             }
151              
152             sub _parse {
153 387     387   807 my $self = shift;
154 387         734 my $l = shift;
155 387         1774 $l->check_for_bom;
156 387 100       1381 unless (exists($self->{map})) {
157 377         3438 $self->{map} = RDF::Trine::NamespaceMap->new();
158             }
159 387         1557 while (my $t = $self->_next_nonws($l)) {
160 1062         3888 $self->_statement($l, $t);
161             }
162             }
163              
164             ################################################################################
165              
166             sub _unget_token {
167 3749     3749   6049 my $self = shift;
168 3749         5625 my $t = shift;
169 3749         5662 push(@{ $self->{ stack } }, $t);
  3749         9285  
170             }
171              
172             sub _next_nonws {
173 13365     13365   23162 my $self = shift;
174 13365         20398 my $l = shift;
175 13365 100       19258 if (scalar(@{ $self->{ stack } })) {
  13365         35594  
176 3749         6451 return pop(@{ $self->{ stack } });
  3749         8875  
177             }
178 9616         16465 while (1) {
179 9616         31487 my $t = $l->get_token;
180 9583 100       35286 return unless ($t);
181 9282         243674 my $type = $t->type;
182             # next if ($type == WS or $type == COMMENT);
183             # warn decrypt_constant($type) . "\n";
184 9282         24787 return $t;
185             }
186             }
187              
188             sub _get_token_type {
189 1538     1538   2885 my $self = shift;
190 1538         2897 my $l = shift;
191 1538         2941 my $type = shift;
192 1538         4186 my $t = $self->_next_nonws($l);
193 1537 100       4780 unless ($t) {
194 3         16 $l->_throw_error(sprintf("Expecting %s but got EOF", decrypt_constant($type)));
195 0         0 return;
196             }
197 1534 100       40085 unless ($t->type eq $type) {
198 12         69 $self->_throw_error(sprintf("Expecting %s but got %s", decrypt_constant($type), decrypt_constant($t->type)), $t, $l);
199             }
200 1522         21525 return $t;
201             }
202              
203             sub _statement {
204 1039     1039   2273 my $self = shift;
205 1039         1870 my $l = shift;
206 1039         2023 my $t = shift;
207 1039         26897 my $type = $t->type;
208             # when (WS) {}
209 1039 100 100     9379 if ($type == PREFIX or $type == SPARQLPREFIX) {
    100 100        
210 245         875 $t = $self->_get_token_type($l, PREFIXNAME);
211 241         1073 my $name = $t->value;
212 241         1190 $name =~ s/:$//;
213 241         835 $t = $self->_get_token_type($l, IRI);
214 239         6674 my $r = RDF::Trine::Node::Resource->new($t->value, $self->{baseURI});
215 239         1073 my $iri = $r->uri_value;
216 239 100       878 if ($type == PREFIX) {
217 236         878 $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         6696 $self->{map}->add_mapping( $name => $iri );
224 239 100       3731 if (my $ns = $self->{namespaces}) {
225 2 50       8 unless ($ns->namespace_uri($name)) {
226 2         7 $ns->add_mapping( $name => $iri );
227             }
228             }
229             }
230             elsif ($type == BASE or $type == SPARQLBASE) {
231 11         44 $t = $self->_get_token_type($l, IRI);
232 10         55 my $r = RDF::Trine::Node::Resource->new($t->value, $self->{baseURI});
233 10         44 my $iri = $r->uri_value;
234 10 100       44 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         202 $self->{baseURI} = $iri;
242             }
243             else {
244 783         3492 $self->_triple( $l, $t );
245 709         2717 $t = $self->_get_token_type($l, DOT);
246             }
247             # }
248             }
249              
250             sub _triple {
251 783     783   1672 my $self = shift;
252 783         1838 my $l = shift;
253 783         1590 my $t = shift;
254 783         20943 my $type = $t->type;
255             # subject
256 783         1616 my $subj;
257 783         1635 my $bnode_plist = 0;
258 783 100 100     6267 if ($type == LBRACKET) {
    100          
    100          
259 40         89 $bnode_plist = 1;
260 40         355 $subj = RDF::Trine::Node::Blank->new();
261 40         135 my $t = $self->_next_nonws($l);
262 40 100       1075 if ($t->type != RBRACKET) {
263 11         61 $self->_unget_token($t);
264 11         62 $self->_predicateObjectList( $l, $subj );
265 11         70 $t = $self->_get_token_type($l, RBRACKET);
266             }
267             } elsif ($type == LPAREN) {
268 4         17 my $t = $self->_next_nonws($l);
269 4 50       121 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         82 $subj = RDF::Trine::Node::Blank->new();
273 4         25 my @objects = $self->_object($l, $t);
274            
275 4         10 while (1) {
276 6         22 my $t = $self->_next_nonws($l);
277 6 100       283 if ($t->type == RPAREN) {
278 4         109 last;
279             } else {
280 2         7 push(@objects, $self->_object($l, $t));
281             }
282             }
283 4         18 $self->_assert_list($subj, @objects);
284             }
285             } elsif (not($type==IRI or $type==PREFIXNAME or $type==BNODE)) {
286 21         102 $self->_throw_error("Expecting resource or bnode but got " . decrypt_constant($type), $t, $l);
287             } else {
288 718         2807 $subj = $self->_token_to_node($t);
289             }
290             # warn "Subject: $subj\n"; # XXX
291            
292 758 100       3189 if ($bnode_plist) {
293             #predicateObjectList?
294 40         133 $t = $self->_next_nonws($l);
295 40         220 $self->_unget_token($t);
296 40 100       1055 if ($t->type != DOT) {
297 34         151 $self->_predicateObjectList($l, $subj);
298             }
299             } else {
300             #predicateObjectList
301 718         3019 $self->_predicateObjectList($l, $subj);
302             }
303             }
304              
305             sub _assert_list {
306 24     24   62 my $self = shift;
307 24         52 my $subj = shift;
308 24         100 my @objects = @_;
309 24         54 my $head = $subj;
310 24         79 while (@objects) {
311 330         648 my $obj = shift(@objects);
312 330         1986 $self->_assert_triple($head, $rdf->first, $obj);
313 330 100       1500 my $next = scalar(@objects) ? RDF::Trine::Node::Blank->new() : $rdf->nil;
314 330         1931 $self->_assert_triple($head, $rdf->rest, $next);
315 330         1579 $head = $next;
316             }
317             }
318              
319             sub _predicateObjectList {
320 817     817   1922 my $self = shift;
321 817         1627 my $l = shift;
322 817         1748 my $subj = shift;
323 817         2350 my $t = $self->_next_nonws($l);
324 811         1540 while (1) {
325 2208         55790 my $type = $t->type;
326 2208 100 100     12345 unless ($type==IRI or $type==PREFIXNAME or $type==A) {
      100        
327 18         92 $self->_throw_error("Expecting verb but got " . decrypt_constant($type), $t, $l);
328             }
329 2190         6751 my $pred = $self->_token_to_node($t);
330 2187         7919 $self->_objectList($l, $subj, $pred);
331            
332 2166         6243 $t = $self->_next_nonws($l);
333 2166 100       57210 last unless ($t);
334 2163 100       54594 if ($t->type == SEMICOLON) {
335 1690         3337 my $sc = $t;
336 1694         4208 SEMICOLON_REPEAT:
337             $t = $self->_next_nonws($l);
338 1694 100       4196 unless ($t) {
339 1         5 $l->_throw_error("Expecting token after semicolon, but got EOF");
340             }
341 1693 100       42091 goto SEMICOLON_REPEAT if ($t->type == SEMICOLON);
342 1689 100 100     41069 if ($t->type == IRI or $t->type == PREFIXNAME or $t->type == A) {
      66        
343 1397         37199 next;
344             } else {
345 292         1377 $self->_unget_token($t);
346 292         7746 return;
347             }
348             } else {
349 473         1866 $self->_unget_token($t);
350 473         2256 return;
351             }
352             }
353             }
354              
355             sub _objectList {
356 2187     2187   4295 my $self = shift;
357 2187         3509 my $l = shift;
358 2187         3486 my $subj = shift;
359 2187         3429 my $pred = shift;
360             # warn "objectList: " . Dumper($subj, $pred); # XXX
361 2187         3710 while (1) {
362 2208         5362 my $t = $self->_next_nonws($l);
363 2197 100       5842 last unless ($t);
364 2195         7260 my $obj = $self->_object($l, $t);
365 2188         7673 $self->_assert_triple($subj, $pred, $obj);
366            
367 2188         7116 $t = $self->_next_nonws($l);
368 2185 100 100     64991 if ($t and $t->type == COMMA) {
369 21         448 next;
370             } else {
371 2164         8330 $self->_unget_token($t);
372 2164         5403 return;
373             }
374             }
375             }
376              
377             sub _assert_triple {
378 2817     2817   5042 my $self = shift;
379 2817         5395 my $subj = shift;
380 2817         4659 my $pred = shift;
381 2817         4261 my $obj = shift;
382 2817 100 66     25731 if ($self->{canonicalize} and blessed($obj) and $obj->isa('RDF::Trine::Node::Literal')) {
      100        
383 588         2045 $obj = $obj->canonicalize;
384             }
385            
386 2817         13655 my $t = RDF::Trine::Statement->new($subj, $pred, $obj);
387 2817 100       9048 if ($self->{handle_triple}) {
388 2613         8793 $self->{handle_triple}->( $t );
389             }
390             }
391              
392             sub _object {
393 2527     2527   4224 my $self = shift;
394 2527         4567 my $l = shift;
395 2527         4069 my $t = shift;
396 2527         63678 my $type = $t->type;
397 2527         5521 my $tcopy = $t;
398 2527         4151 my $obj;
399 2527 100 100     20662 if ($type==LBRACKET) {
    100          
    100          
400 36         229 $obj = RDF::Trine::Node::Blank->new();
401 36         129 my $t = $self->_next_nonws($l);
402 36 50       139 unless ($t) {
403 0         0 $self->_throw_error("Expecting object but got only opening bracket", $tcopy, $l);
404             }
405 36 100       929 if ($t->type != RBRACKET) {
406 28         113 $self->_unget_token($t);
407 28         149 $self->_predicateObjectList( $l, $obj );
408 28         109 $t = $self->_get_token_type($l, RBRACKET);
409             }
410             } elsif ($type == LPAREN) {
411 27         86 my $t = $self->_next_nonws($l);
412 27 50       88 unless ($t) {
413 0         0 $self->_throw_error("Expecting object but got only opening paren", $tcopy, $l);
414             }
415 27 100       718 if ($t->type == RPAREN) {
416 6         34 $obj = RDF::Trine::Node::Resource->new('http://www.w3.org/1999/02/22-rdf-syntax-ns#nil');
417             } else {
418 21         141 $obj = RDF::Trine::Node::Blank->new();
419 21         97 my @objects = $self->_object($l, $t);
420            
421 21         52 while (1) {
422 325         922 my $t = $self->_next_nonws($l);
423 325 100       7618 if ($t->type == RPAREN) {
424 20         563 last;
425             } else {
426 305         903 push(@objects, $self->_object($l, $t));
427             }
428             }
429 20         104 $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         7368 $obj = $self->_term($l, $t);
435             }
436 2519         14875 return $obj;
437             }
438              
439             sub _term {
440 2486     2486   4611 my $self = shift;
441 2486         4431 my $l = shift;
442 2486         4361 my $t = shift;
443 2486         4043 my $tcopy = $t;
444 2486         3911 my $obj;
445 2486         63317 my $type = $t->type;
446 2486 100 100     17554 if ($type==STRING1D or $type==STRING3D or $type==STRING1S or $type==STRING3S) {
      100        
      100        
447 781         3083 my $value = $t->value;
448 781         2315 my $t = $self->_next_nonws($l);
449 778         1861 my $dt;
450             my $lang;
451 778 50       1998 if ($t) {
452 778 100       19287 if ($t->type == HATHAT) {
    100          
453 36         176 my $t = $self->_next_nonws($l);
454 36 50 66     1179 if ($t->type == IRI or $t->type == PREFIXNAME) {
455 36         175 $dt = $self->_token_to_node($t);
456             }
457             } elsif ($t->type == LANG) {
458 15         61 $lang = $t->value;
459             } else {
460 727         2251 $self->_unget_token($t);
461             }
462             }
463 778         4586 $obj = RDF::Trine::Node::Literal->new($value, $lang, $dt);
464             } else {
465 1705         4920 $obj = $self->_token_to_node($t, $type);
466             }
467 2482         6248 return $obj;
468             }
469              
470             sub _token_to_node {
471 4682     4682   8443 my $self = shift;
472 4682         7633 my $t = shift;
473 4682   66     83192 my $type = shift || $t->type;
474 4682 100       24449 if ($type eq A) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
    0          
    0          
475 55         568 return $rdf->type;
476             }
477             elsif ($type eq IRI) {
478 1599         6104 return RDF::Trine::Node::Resource->new($t->value, $self->{baseURI});
479             }
480             elsif ($type eq INTEGER) {
481 59         262 return RDF::Trine::Node::Literal->new($t->value, undef, $xsd->integer);
482             }
483             elsif ($type eq DECIMAL) {
484 11         56 return RDF::Trine::Node::Literal->new($t->value, undef, $xsd->decimal);
485             }
486             elsif ($type eq DOUBLE) {
487 9         56 return RDF::Trine::Node::Literal->new($t->value, undef, $xsd->double);
488             }
489             elsif ($type eq BOOLEAN) {
490 8         34 return RDF::Trine::Node::Literal->new($t->value, undef, $xsd->boolean);
491             }
492             elsif ($type eq PREFIXNAME) {
493 2860         4610 my ($ns, $local) = @{ $t->args };
  2860         74316  
494 2860         11402 $ns =~ s/:$//;
495 2860         13315 my $prefix = $self->{map}->namespace_uri($ns);
496 2860 100       12150 unless (blessed($prefix)) {
497 8         50 $self->_throw_error("Use of undeclared prefix '$ns'", $t);
498             }
499 2852         10645 my $iri = $prefix->uri($local);
500 2852         7804 return $iri;
501             }
502             elsif ($type eq BNODE) {
503 81         308 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   137 my $self = shift;
518 61         131 my $message = shift;
519 61         107 my $t = shift;
520 61         120 my $l = shift;
521 61         1778 my $line = $t->start_line;
522 61         1722 my $col = $t->start_column;
523             # Carp::cluck "$message at $line:$col";
524 61         243 my $text = "$message at $line:$col";
525 61 100       217 if (defined($t->value)) {
526 30         107 $text .= " (near '" . $t->value . "')";
527             }
528             RDF::Trine::Error::ParserError::Tokenized->throw(
529 61         705 -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