File Coverage

blib/lib/Gwybodaeth/Parsers/N3.pm
Criterion Covered Total %
statement 157 164 95.7
branch 40 54 74.0
condition 9 13 69.2
subroutine 19 19 100.0
pod 2 2 100.0
total 227 252 90.0


line stmt bran cond sub pod time code
1             #!/usr/bin/env perl
2              
3 4     4   259691 use warnings;
  4         10  
  4         139  
4 4     4   24 use strict;
  4         11  
  4         147  
5              
6 4     4   2408 use Gwybodaeth::NamespaceManager;
  4         9  
  4         107  
7 4     4   2325 use Gwybodaeth::Triples;
  4         11  
  4         110  
8 4     4   2352 use Gwybodaeth::Tokenize;
  4         9  
  4         2133  
9              
10             package Gwybodaeth::Parsers::N3;
11              
12             =head1 NAME
13              
14             Parsers::N3 - Parses N3 into a data structure.
15              
16             =head1 SYNOPSIS
17              
18             use N3;
19              
20             my $n3 = N3->new();
21              
22             $n3->parse(@data);
23              
24             =head1 DESCRIPTION
25              
26             This module converts N3 data into a data structure.
27              
28             =over
29              
30             =cut
31              
32 4     4   36 use Carp qw(croak);
  4         10  
  4         593  
33 4     4   5852 use Tie::InsertOrderHash;
  4         13575  
  4         8095  
34              
35             =item new()
36              
37             Returns an instance of the N3 class.
38              
39             =cut
40              
41             sub new {
42 4     4 1 166 my $class = shift;
43 4         32 my $self = { triples => Gwybodaeth::Triples->new() };
44 4         56 tie my %func => 'Tie::InsertOrderHash';
45 4         54 $self->{functions} = \%func;
46 4         13 bless $self, $class;
47 4         12 return $self;
48             }
49              
50             =item parse(@data)
51              
52             Parses N3 from an array of rows, @data. Returns an array where the
53             first item is a reference to a hash of triples and the second item
54             is a reference to a hash of functions.
55              
56             =cut
57              
58             sub parse {
59 4     4 1 3683 my($self, @data) = @_;
60              
61 4 50       18 ref($self) or croak "instance variable needed";
62              
63 4         9 my $subject;
64              
65             # Record the namespaces
66 4         33 my $namespace = Gwybodaeth::NamespaceManager->new();
67 4         21 $namespace->map_namespace(\@data);
68              
69 4         220 my $tokenizer = Gwybodaeth::Tokenize->new();
70 4         20 my $tokenized = $tokenizer->tokenize(\@data);
71              
72 4         17 $self->_parse_n3($tokenized);
73              
74 4 50       18 $self->_parse_triplestore($self->{triples})
75             or croak "function population went wrong";
76              
77 4         68 return [$self->{triples},$self->{functions}];
78             }
79              
80             # Expects a reference to the tokenized data as a parameter
81             sub _parse_n3 {
82 7     7   13 my $self = shift;
83 7         11 my $data = shift;
84 7   100     27 my $index_start = shift || 0;
85              
86 7         13 for( my $indx = $index_start; $indx <= $#{ $data }; ++$indx ) {
  32         83  
87              
88 28         28 my $token = ${ $data }[$indx];
  28         49  
89             #my $next_token = ${ $data }[$indx+1 % $#{ $data }];
90 28         31 my $next_token = ${ $data }[$indx+1];
  28         46  
91              
92 28         31 my $subject;
93              
94 28 50       70 if ($token =~ m/
95             # whole string matches @prefix
96             ^\@prefix$/x) {
97             # logic
98 0         0 next;
99             }
100              
101 28 50       53 if ($token =~ m/
102             # whole string matches @base
103             ^\@base$/x) {
104             #logic
105 0         0 next;
106             }
107              
108             # Shorthands for common predicates
109 28 100       52 if ($token =~ m/
110             # whole string matches: a
111             ^a$/x) {
112             # Should return a reference to a Triples type
113 3         18 $self->_parse_triple($data, $indx);
114 3         6 next;
115             }
116              
117 25 50       46 if ($token =~ m/
118             # whole string matches only: =
119             ^\=$/x) {
120             #logic
121 0         0 next;
122             }
123              
124 25 50       47 if ($token =~ m/
125             # whole string matches only: <=
126             ^\<\=$/x) {
127             # logic
128 0         0 next;
129             }
130              
131 25 50       85 if ($token =~ m/
132             # whole string matches only: =>
133             ^\=\>$/x) {
134             #logic
135 0         0 next;
136             }
137             # end of predicate shorthands
138              
139 25 100       60 if ($token =~ m/\< # open angle bracket
140             \s* # any number of whitespace chars
141             Ex: #
142             .* # any number of any char except '\n'
143             \> # close angle bracket/x) {
144             # record the next block as a 'function'
145 1 50       7 if ($next_token =~ m/[.;] # either a period or comma/x) {
146             # This is the call to the function
147             # not its defenition
148 0         0 next;
149             } else {
150 1         7 $self->_record_func($data, $indx);
151 1         4 while((my $tok=$self->_next_token($data,$indx)) =~ /
152             [^\.] # isn't a period
153             /x) {
154 5         12 ++$indx;
155             }
156 1         5 return $self->_parse_n3($data,$indx);
157             }
158             }
159              
160 24 100       52 if ($token =~ m/\[ # matches [ /x) {
161 5 100       14 if ($token =~ m/
162             \[\] # matches []
163             /x) {
164             #logic specific to 'something' bracket operator
165 3         10 next;
166             }
167             # logic
168 2         9 while((my $tok=$self->_next_token($data,$indx)) =~ /
169             # any character which is not
170             # a right square brace
171             [^\]]
172             /x) {
173 10         25 ++$indx;
174             }
175 2         28 $indx = $self->_parse_n3($data,$indx);
176 2         5 next;
177             }
178              
179 19 100       37 if ($token =~ m/\]/x) {
180             # logic
181 2         7 return $indx;
182             }
183              
184 17 100       48 if ($token =~ m/^\.$/x) {
185             #logic
186 3         8 next;
187             }
188              
189 14 100       45 if ($token =~ m/^\;$/x) {
190             #logic
191 4         9 next;
192             }
193              
194             }
195 4         12 return $self->{triples};
196             }
197              
198             sub _next_token {
199 46     46   54 my $self = shift;
200 46         114 my $data = shift;
201 46         46 my $index = shift;
202 46   50     143 my $offset = shift || 1;
203              
204 46         45 return ${ $data }[$index+$offset];
  46         197  
205             }
206              
207             # Takes a reference to the data and pointer to the start
208             # of relevent data as a parameter
209             sub _parse_triple {
210 3     3   7 my $self = shift;
211 3         5 my $data = shift;
212 3         6 my $index = shift;
213              
214 3         4 ++$index;
215              
216 3         4 my $subject = ${ $data }[$index];
  3         47  
217              
218 3 50       14 if ($self->_next_token($data, $index) eq ';') {
219 3         48 $index = $self->_get_verb_and_object($data, $index,
220             $subject, $self->{triples},
221             )
222             }
223 3         6 return $index;
224             }
225              
226             sub _get_verb_and_object {
227 6     6   13 my($self, $data, $index, $subject, $triple) = @_;
228              
229 6         8 my $verb;
230             my $object;
231 0         0 my $next_token;
232              
233 6         14 while (defined($self->_next_token($data,$index))) {
234              
235 13         23 ++$index; # to get past the ';' char
236              
237 13         14 $verb = ${ $data }[++$index];
  13         20  
238 13         68 $object = $self->_get_object($data, ++$index);
239              
240 13 100 66     229 if (defined($object) and defined($verb)) {
241 10 100       36 if ($object =~ /^[\;\]]$ # any string consisting of
242             # one comma or right square
243             # brace
244 4         12 /x ) { next };
245              
246 6         21 $triple->store_triple($subject, $verb, $object);
247 3         6 } else { next; }
248              
249 6 100       10 if (eval {$object->isa('Gwybodaeth::Triples')}) {
  6         50  
250             #while ($self->_next_token($data,$index) =~ /[^\]]/) {
251             #++$index;
252             #}
253 2         6 next;
254             }
255              
256 4         21 $next_token = $self->_next_token($data, $index);
257              
258 4 100 66     29 if ($next_token eq ';') {
    50          
259 1         2 next;
260             } elsif ( $next_token eq '.' or $next_token eq ']') {
261             # end of section;
262 3         3 ++$index;
263 3         7 last;
264             }
265             }
266 6         13 return $index;
267             }
268              
269             sub _get_object {
270 13     13   19 my($self, $data, $index) = @_;
271              
272 13 100       15 unless (defined(${ $data }[$index])) {
  13         32  
273 3         17 return;
274             }
275              
276 10 100 66     13 if ((${ $data }[$index] eq '[')
  10         47  
277             and
278             ($self->_next_token($data, $index) eq 'a'))
279             {
280 2         10 return $self->_get_nested_triple($data, $index);
281             } else {
282 8         11 return ${ $data }[$index];
  8         24  
283             }
284             }
285              
286             sub _get_nested_triple {
287 3     3   7 my($self, $data, $index) = @_;
288              
289 3         4 ++$index; # to get over the ';' and 'a'
290              
291 3         23 my $nest_triple = Gwybodaeth::Triples->new();
292              
293 3         7 my $subject = ${ $data }[++$index];
  3         7  
294              
295 3         11 my $next_token = $self->_next_token($data, $index);
296              
297 3 50       14 if ($next_token eq ';') {
298 3         14 $self->_get_verb_and_object($data,
299             $index,
300             $subject,
301             $nest_triple);
302             }
303 3         7 return $nest_triple;
304             }
305              
306             # Store a defined function in a hash
307             sub _record_func {
308 1     1   3 my($self, $data, $index) = @_;
309              
310 1         2 my $func_name = ${ $data }[$index];
  1         3  
311              
312 1         5 my $func_triple = $self->_get_nested_triple($data, $index);
313              
314 1         12 $self->{functions}->{$func_name} = $func_triple;
315 1         18 return $index;
316             }
317              
318             # Parse the main triples hash so that functions are
319             # placed where they are called.
320             sub _parse_triplestore {
321 4     4   33 my $self = shift;
322 4         6 my $triple = shift;
323              
324 4 50       14 if (defined($self->{functions})) {
325 4 50       14 $self->_parse_functions($self->{functions})
326             or croak "Unable to parse functions";
327             }
328              
329 4         14 return $self->_populate_func($triple);
330             }
331              
332             # Interface to _populate_func for the hash of functions
333             sub _parse_functions {
334 4     4   5 my $self = shift;
335 4         7 my $func_hash = shift; # a reference to the function hash
336              
337 4         5 for my $key (%{ $func_hash }) {
  4         27  
338 2         30 $self->_populate_func($func_hash->{$key});
339             }
340 4         48 return 1;
341             }
342              
343             # Populate any function calls with the triple store they define.
344             sub _populate_func {
345 6     6   15 my $self = shift;
346 6         14 my $triple = shift;
347              
348 6         17 for my $tkey (keys %{ $triple }) {
  6         31  
349 4         6 for my $fkey ( keys %{ $self->{functions} } ) {
  4         16  
350 1         28 for my $i (0..$#{ $triple->{$tkey}{'obj'} }) {
  1         5  
351 1         2 my $obj = $triple->{$tkey}{'obj'}[$i];
352 1 50       8 if ($obj eq $fkey) {
353             #$triple->{$tkey}{'obj'}[$i] = $self->{functions}->{$fkey};
354             #$self->_populate_func($triple);
355             }
356             }
357             }
358             }
359 6         38 return 1;
360             }
361             1;
362             __END__