File Coverage

blib/lib/AtteanX/Serializer/TurtleTokens.pm
Criterion Covered Total %
statement 34 34 100.0
branch n/a
condition n/a
subroutine 14 14 100.0
pod 4 4 100.0
total 52 52 100.0


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             AtteanX::Serializer::TurtleTokens - Turtle Serializer
4              
5             =head1 VERSION
6              
7             This document describes AtteanX::Serializer::TurtleTokens version 0.032
8              
9             =head1 SYNOPSIS
10              
11             use Attean;
12             my $serializer = Attean->get_serializer('Turtle')->new();
13             $serializer->serialize_iter_to_io( $io, $fh );
14              
15             =head1 DESCRIPTION
16              
17             ...
18              
19             =head1 ATTRIBUTES
20              
21             =over 4
22              
23             =item C<< canonical_media_type >>
24              
25             =item C<< media_types >>
26              
27             =item C<< handled_type >>
28              
29             =item C<< file_extensions >>
30              
31             =back
32              
33             =head1 METHODS
34              
35             =over 4
36              
37             =cut
38              
39 3     3   33 use v5.14;
  3         11  
40 3     3   17 use warnings;
  3         10  
  3         161  
41              
42             use Moo;
43 3     3   19 use Data::Dumper;
  3         13  
  3         14  
44 3     3   964 use Encode qw(encode);
  3         8  
  3         115  
45 3     3   21 use Attean::ListIterator;
  3         6  
  3         118  
46 3     3   17 use List::MoreUtils qw(any);
  3         16  
  3         87  
47 3     3   15 use AtteanX::Parser::Turtle::Constants;
  3         6  
  3         25  
48 3     3   1711 use AtteanX::Parser::Turtle::Lexer;
  3         6  
  3         453  
49 3     3   959 use namespace::clean;
  3         7  
  3         86  
50 3     3   20 with 'Attean::API::AbbreviatingSerializer';
  3         3  
  3         23  
51             with 'Attean::API::AppendableSerializer';
52            
53              
54 1     1 1 919 return [qw(text/turtle)];
55             }
56            
57 10     10 1 25 state $ITEM_TYPE = Type::Tiny::Role->new(role => 'AtteanX::Parser::Turtle::Token');
58             return $ITEM_TYPE;
59             }
60              
61 1     1 1 364  
62 1         105 =item C<< serialize_iter_to_io( $fh, $iterator ) >>
63              
64             Serializes the Turtle token objects from C<< $iterator >> to the
65 1     1 1 1033 L<IO::Handle> object C<< $fh >>.
66              
67             =cut
68              
69             my $self = shift;
70             my $io = shift;
71             my $iter = shift;
72             my $indent = 0;
73             my $newline = 1;
74             my $semicolon = 0;
75             my $need_space = 0;
76              
77             my $map = $self->namespaces;
78             my %namespace_map;
79             if ($map) {
80             foreach my $p ($map->list_prefixes) {
81             my $prefix = $map->namespace_uri($p)->as_string;
82             $namespace_map{$prefix} = $p;
83             }
84             }
85            
86             while (my $t = $iter->next()) {
87             my $type = $t->type;
88            
89             if ($map) {
90             if ($type == IRI) {
91             my $value = $t->value;
92             if ($value =~ /^(?<namespace>.*?)(?<local>$AtteanX::Parser::Turtle::Lexer::r_PN_LOCAL)$/) {
93             if (my $ns = $namespace_map{$+{namespace}}) {
94             $type = PREFIXNAME;
95             $t = AtteanX::SPARQL::Token->fast_constructor( $type, $t->start_line, $t->start_column, $t->line, $t->column, ["${ns}:", $+{local}] );
96             }
97             }
98             }
99             }
100            
101             if ($type == LANG or $type == HATHAT) {
102             $need_space= 0;
103             }
104            
105             unless ($newline) {
106             if ($type == BASE or $type == PREFIX or $type == TURTLEBASE or $type == TURTLEPREFIX) {
107             $io->print("\n");
108             $newline = 1;
109             }
110             }
111            
112             if ($newline) {
113             $io->print(' ' x $indent);
114             $newline = 0;
115             } elsif ($need_space) {
116             $io->print(' ');
117             $need_space = 0;
118             }
119            
120             if ($type == PREFIX or $type == TURTLEPREFIX) {
121             # If we're serializing a PREFIX, also serialize the PREFIXNAME
122             # and IRI that must follow it so that we don't accidentally
123             # shorten the prefix IRI with its own namespace. For example,
124             # if we didn't serialize the PREFIXNAME and IRI here, we might
125             # end up with this:
126             #
127             # @prefix foaf: foaf:
128             #
129             # instead of:
130             #
131             # @prefix foaf: <http://xmlns.com/foaf/0.1/>
132             $io->print($t->value);
133             $io->print(' ');
134             my $pname = $iter->next();
135             unless ($pname->type == PREFIXNAME) {
136             die "PREFIX namespace not found during Turtle serialization";
137             }
138             my $args = $pname->args;
139             $io->print(join('', @$args));
140             $io->print(' ');
141            
142             my $iri = $iter->next();
143             unless ($iri->type == IRI) {
144             die "PREFIX IRI not found during Turtle serialization";
145             }
146             $io->print('<');
147             $io->print($iri->value);
148             $io->print('>');
149             $need_space++;
150             } elsif ($type == PREFIXNAME) {
151             my $args = $t->args;
152             $io->print(join('', @$args));
153             $need_space++;
154             } elsif ($type == BNODE) {
155             $io->print('_:');
156             $io->print($t->value);
157             $need_space++;
158             } elsif ($type == IRI) {
159             # TODO: escape
160             $io->print('<');
161             $io->print($t->value);
162             $io->print('>');
163             $need_space++;
164             } elsif ($type == LANG) {
165             $io->print('@');
166             $io->print($t->value);
167             $need_space++;
168             } elsif ($type == STRING1S) {
169             my $value = $t->value;
170             $value =~ s/'/\\'/g;
171             $io->print("'");
172             $io->print($value);
173             $io->print("'");
174             $need_space++;
175             } elsif ($type == STRING1D) {
176             my $value = $t->value;
177             $value =~ s/"/\\"/g;
178             $io->print('"');
179             $io->print($value);
180             $io->print('"');
181             $need_space++;
182             } elsif ($type == STRING3S) {
183             my $value = $t->value;
184             $value =~ s/'''/''\\'/g;
185             $io->print("'''");
186             $io->print($value);
187             $io->print("'''");
188             $need_space++;
189             } elsif ($type == STRING3D) {
190             my $value = $t->value;
191             $value =~ s/"""/""\\"/g;
192             $io->print('"""');
193             $io->print($value);
194             $io->print('"""');
195             $need_space++;
196             } elsif ($type == A) {
197             $io->print('a');
198             $need_space++;
199             } elsif ($type == WS) {
200             } elsif ($type == COMMENT) {
201             if ($t->value =~ /\n/) {
202             die "Unexpected newline found in Turtle comment token";
203             }
204             $io->print('# ');
205             $io->print($t->value);
206             $io->print("\n");
207             } elsif ($type == HATHAT) {
208             $io->print($t->value);
209             } else {
210             $io->print($t->value);
211             $need_space++;
212             }
213            
214             if ($type == DOT) {
215             if ($semicolon) {
216             $indent--;
217             $semicolon = 0;
218             }
219             $need_space = 0;
220             $io->print("\n");
221             $newline = 1;
222             } elsif ($type == SEMICOLON) {
223             $io->print("\n");
224             $need_space = 0;
225             $newline = 1;
226             unless ($semicolon) {
227             $indent++;
228             }
229             $semicolon = 1;
230             }
231             }
232             unless ($newline) {
233             $io->print("\n");
234             }
235             return;
236             }
237            
238             =item C<< serialize_iter_to_bytes( $iterator ) >>
239              
240             Serializes the Turtle token objects from C<< $iterator >>
241             and returns the serialization as a UTF-8 encoded byte string.
242              
243             =cut
244              
245             my $self = shift;
246             my $iter = shift;
247             my $data = '';
248             open(my $fh, '>:utf8', \$data);
249             $self->serialize_iter_to_io($fh, $iter);
250             close($fh);
251             return $data;
252             }
253             }
254              
255             1;
256              
257              
258             =back
259              
260             =head1 BUGS
261              
262             Please report any bugs or feature requests to through the GitHub web interface
263             at L<https://github.com/kasei/perlrdf/issues>.
264              
265             =head1 AUTHOR
266              
267             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
268              
269             =head1 COPYRIGHT
270              
271             Copyright (c) 2014--2022 Gregory Todd Williams. This
272             program is free software; you can redistribute it and/or modify it under
273             the same terms as Perl itself.
274              
275             =cut