File Coverage

blib/lib/IO/Tokenized.pm
Criterion Covered Total %
statement 133 170 78.2
branch 31 56 55.3
condition n/a
subroutine 19 24 79.1
pod 10 12 83.3
total 193 262 73.6


line stmt bran cond sub pod time code
1             package IO::Tokenized;
2              
3 3     3   46831 use strict;
  3         13  
  3         140  
4 3     3   29 use warnings;
  3         8  
  3         123  
5              
6 3     3   17 use vars qw/$VERSION @ISA @EXPORT @EXPORT_OK %EXPORT_TAGS/;
  3         6  
  3         544  
7 3     3   22 use constant BUFFER_SIZE => 30 * 1024;
  3         8  
  3         2032  
8 3     3   22 use constant TOKEN_SEPARATOR => qr/\s/;
  3         7  
  3         183  
9              
10 3     3   35 use Carp;
  3         6  
  3         570  
11 3     3   7300 use Symbol;
  3         6315  
  3         248  
12 3     3   103 use Exporter;
  3         7  
  3         11258  
13              
14             $VERSION = '0.04';
15              
16             @ISA = qw(Exporter);
17             @EXPORT = ();
18             @EXPORT_OK = qw(initialize_parsing buffer_space token_separator flushbuffer
19             skip resynch getline getlines gettoken gettokens setparser);
20              
21             %EXPORT_TAGS = (all => [@EXPORT_OK],
22             parse => [qw(initialize_parsing gettoken gettokens)],
23             buffer => [qw(bufferspace flushbuffer resynch)]
24             );
25              
26             sub new {
27 1     1 0 4 my $class = shift;
28 1         3 my ($self,@tokens) = @_;
29 1 50       7 $self = gensym unless defined $self;
30 1         3 bless $self,$class;
31 1         4 initialize_parsing($self,@tokens);
32             }
33              
34             sub initialize_parsing {
35 2     2 0 949 my $self = shift;
36 2         8 my @tokens = @_;
37 2         5 @{${*$self}{__PACKAGE__}{definition}} = ();
  2         6  
  2         21  
38 2 50       42 flushbuffer($self) &&
39             carp __PACKAGE__ ."(re)initializing parser with not empty buffer";
40 2         8 token_separator($self,TOKEN_SEPARATOR);
41 2         7 buffer_space($self,BUFFER_SIZE);
42 2 50       18 setparser($self,@tokens) if @tokens;
43 2         12 return $self;
44             }
45              
46             sub buffer_space {
47 2     2 1 3 my $self = shift;
48              
49 2         8 ${*$self}{__PACKAGE__}{bufsize} = BUFFER_SIZE
  2         16  
50 2 50       4 unless defined${*$self}{__PACKAGE__}{bufsize};
51              
52 2         4 my $oldvalue = ${*$self}{__PACKAGE__}{bufsize};
  2         7  
53 2 50       16 ${*$self}{__PACKAGE__}{bufsize} = shift if @_;
  2         5  
54 2         32 return $oldvalue;
55             }
56              
57             sub token_separator {
58 12     12 1 21 my $self = shift;
59 12         15 my $oldvalue = ${*$self}{__PACKAGE__}{token_separator};
  12         37  
60 12 100       43 ${*$self}{__PACKAGE__}{token_separator} = shift if @_;
  2         8  
61 12         26 return $oldvalue;
62             }
63              
64             sub flushbuffer {
65 2     2 1 6 my $self = shift;
66 2         9 my $oldvalue = ${*$self}{__PACKAGE__}{buffer};
  2         11  
67 2         7 ${*$self}{__PACKAGE__}{buffer} = "";
  2         16  
68 2         11 return $oldvalue;
69             }
70              
71             # tries to read from self until repetedly removing skip prefix until
72             # one of the following is verified:
73             # 1. the buffer is not empty and doesn't start with a skip prefix
74             # 2. the end of file is reached without 1. beeing fulfilled.
75             #
76             # The function returns true in the first case, false in the second
77              
78             sub skip {
79 10     10 1 16 my $self = shift;
80 10         28 my $re = token_separator($self);
81 10         18 my $buffer = \${*$self}{__PACKAGE__}{buffer};
  10         32  
82 10         18 while (1) {
83 14 100       160 $$buffer = scalar <$self> unless length($$buffer);
84 14 100       53 return unless defined $$buffer; #end of file
85 12         372 $$buffer =~ s/^$re+//;
86 12 100       51 return 1 if length($$buffer);
87             }
88             }
89              
90             # flushes buffer till the first token, if possible
91             sub resynch {
92 0     0 1 0 my $self = shift;
93 0         0 my $resyncher = ${*$self}{__PACKAGE__}{resyncher};
  0         0  
94 0         0 &$resyncher($self);
95             }
96              
97             sub getline {
98 0     0 1 0 my $self = shift;
99 0         0 my $buffer = \${*$self}{__PACKAGE__}{buffer};
  0         0  
100 0 0       0 if ($$buffer =~ s!^(.*?$/)!!) {
101 0         0 return $1;
102             }
103             else {
104 0         0 $$buffer .= scalar <$self>;
105 0         0 flusbuffer($self);
106             }
107             }
108              
109             sub getlines {
110 0     0 1 0 my $self = shift;
111 0         0 my @lines = (getline($self));
112 0 0       0 push @lines,<$self> unless eof $self;
113 0         0 return @lines;
114             }
115            
116              
117             sub gettoken {
118 10     10 1 18053 my $self = shift;
119 10         23 my $parser = ${*$self}{__PACKAGE__}{parser};
  10         48  
120            
121 10 100       35 skip($self) || return;
122 8         129 my ($token,$value) = &$parser($self);
123 8 50       36 if ($token eq '') {
    50          
124 0         0 return;
125             }
126             elsif ($token eq '') {
127 0         0 croak "Overflowed buffer with no token found";
128             }
129             else {
130 8         27 return ($token => $value);
131             }
132             }
133              
134             sub gettokens {
135 0     0 1 0 my $self = shift;
136 0         0 my @result;
137 0         0 while (my $t = gettoken($self)) {
138 0         0 push @result,$t;
139             }
140 0         0 return @result;
141             }
142              
143             sub setparser {
144 2     2 1 13 my $self = shift;
145 2         4 my @oldvalue = @{${*$self}{__PACKAGE__}{definition}};
  2         3  
  2         10  
146 2         1972 @{${*$self}{__PACKAGE__}{definition}} = @_;
  2         7  
  2         13  
147 2         8 my %regexp = ();
148 2         5 my %functions = ();
149 2         5 my @order = ();
150              
151 2         4 foreach my $definition (@{${*$self}{__PACKAGE__}{definition}}) {
  2         3  
  2         10  
152 6         13 my ($tok,$re,$func) = @$definition;
153 6 50       16 if ($tok eq "") {
154 0         0 token_separator($self,$re);
155 0         0 next;
156             }
157 6 50       15 if (exists $regexp{$tok}) {
158 0         0 carp "token '$tok' redefined!";
159 0         0 next;
160             }
161 6         9 push @order,$tok;
162 6         13 $regexp{$tok} = $re;
163 6 100       21 $functions{$tok} = $func if defined $func;
164             }
165 2         7 ${*$self}{__PACKAGE__}{parser} = eval {
  2         8  
166 2         5 my @checkers = ();
167 2         4 foreach my $tok (@order) {
168 6         9 my $sub = eval {
169 6 100   6   34 my $func = exists $functions{$tok} ? $functions{$tok} : sub {shift @_};
  6         16  
170 6         404 my $re = qr/^($regexp{$tok})(.*)/s;
171 6         21 my $token = $tok;
172              
173             sub {
174 16     16   21 my $self = shift;
175 16         21 my $buffer = \${*$self}{__PACKAGE__}{buffer};
  16         39  
176 16         275 my @items = ($$buffer =~ $re);
177 16 100       49 return (undef,undef) unless @items;
178 8         16 my $tmp = pop @items;
179 8         246 my $value = &$func(@items);
180 8 50       31 return unless defined $value;
181 8         14 $$buffer = $tmp;
182 8         44 return ($token => $value);
183             }
184 6         63 };
185 6 50       18 croak $@ if $@;
186 6         14 push @checkers,$sub;
187             }
188             # now define the parser
189             sub {
190 8     8   16 my $self = shift;
191 8         13 my $buffer = \${*$self}{__PACKAGE__}{buffer};
  8         76  
192 8         18 while (1) {
193 8         16 foreach my $checker (@checkers) {
194 16         40 my ($token,$value) = &$checker($self);
195 16 100       55 return ($token,$value) if defined $token;
196             }
197             # no token matched... we try to extend the buffer by reading
198             # another line but first we check that there is no overflow
199 0 0       0 return ('' => undef) unless
200             length($$buffer) < buffer_space($self);
201 0         0 my $line = scalar <$self>;
202 0 0       0 return ('' => undef) unless defined $line;
203 0         0 $$buffer .= $line;
204             }
205 2         10 };
206             };
207 2 50       7 croak $@ if $@;
208 2         23 my $resynch = "(?=" . join("|",
209             map("(?:$regexp{$_})",@order)) . ")";
210 2         5 ${*$self}{__PACKAGE__}{resyncher} = eval {
  2         8  
211 2         165 my $resynch_re = qr/$resynch/os;
212             #the resyncher
213             sub {
214 0     0   0 my $self = shift;
215 0         0 my $buffer = \${*$self}{__PACKAGE__}{buffer};
  0         0  
216 0         0 while (1) {
217 0 0       0 return 1 if $$buffer =~ s/^.*?(?=$resynch_re)//;
218 0         0 $$buffer = scalar <$self>;
219 0 0       0 return unless defined $$buffer;
220             }
221             }
222 2         27 };
223 2 50       7 croak $@ if $@;
224 2         9 return @oldvalue;
225             }
226              
227             1;
228             __END__