File Coverage

blib/lib/AtteanX/API/Lexer.pm
Criterion Covered Total %
statement 59 65 90.7
branch 14 22 63.6
condition 1 3 33.3
subroutine 12 12 100.0
pod 7 7 100.0
total 93 109 85.3


line stmt bran cond sub pod time code
1 21     21   11782 use v5.14;
  21         75  
2 21     21   115 use warnings;
  21         735  
  21         1067  
3              
4             =head1 NAME
5              
6             AtteanX::API::Lexer - Role defining common functionality for lexers.
7              
8             =head1 VERSION
9              
10             This document describes AtteanX::API::Lexer version 0.032
11              
12             =head1 DESCRIPTION
13              
14             The AtteanX::API::Lexer role provides a common interface and implementation
15             for lexer implementations, allowing line-based buffer filling, and consuming
16             of characters, constant strings, and fixed-length buffers.
17              
18             =head1 ATTRIBUTES
19              
20             =over 4
21              
22             =item C<< file >>
23              
24             =item C<< linebuffer >>
25              
26             =item C<< line >>
27              
28             =item C<< column >>
29              
30             =item C<< buffer >>
31              
32             =item C<< start_column >>
33              
34             =item C<< start_line >>
35              
36             =back
37              
38             =head1 METHODS
39              
40             =over 4
41              
42             =cut
43              
44             use strict;
45 21     21   121 use Types::Standard qw(FileHandle Ref Str Int ArrayRef HashRef ConsumerOf InstanceOf);
  21         40  
  21         562  
46 21     21   101  
  21         47  
  21         175  
47             use Moo::Role;
48 21     21   23822  
  21         46  
  21         159  
49             has file => ( is => 'ro', isa => FileHandle, required => 1, );
50             has linebuffer => ( is => 'rw', isa => Str, default => '', );
51             has line => ( is => 'rw', isa => Int, default => 1, );
52             has column => ( is => 'rw', isa => Int, default => 1, );
53             has buffer => ( is => 'rw', isa => Str, default => '', );
54             has start_column => ( is => 'rw', isa => Int, default => -1, );
55             has start_line => ( is => 'rw', isa => Int, default => -1, );
56              
57             around 'BUILDARGS' => sub {
58             my $orig = shift;
59             my $class = shift;
60             return { file => shift } if (scalar(@_) == 1);
61             return $orig->( $class, @_ );
62             };
63              
64             =item C<< fill_buffer >>
65              
66             Fills the buffer with a new line from the underlying filehandle.
67              
68             =cut
69              
70             my $self = shift;
71             unless (length($self->buffer)) {
72 183     183 1 1032 my $line = $self->file->getline;
73 183 50       2551 $self->{buffer} .= $line if (defined($line));
74 183         4864 }
75 183 100       4965 }
76              
77             =item C<< check_for_bom >>
78              
79             Remove a BOM character if one appears at the start of the buffer.
80              
81             =cut
82              
83             my $self = shift;
84             my $c = $self->peek_char();
85             $self->get_char if (defined($c) and $c eq "\x{FEFF}");
86 32     32 1 71 }
87 32         126  
88 32 50 33     247 =item C<< get_char_safe( $char ) >>
89              
90             Consume the single character C<< $char >> from the buffer.
91             Throw an error if C<< $char >> is not at the start of the buffer.
92              
93             =cut
94              
95             my $self = shift;
96             my $char = shift;
97             my $c = $self->get_char;
98             $self->_throw_error("Expected '$char' but got '$c'") if ($c ne $char);
99 600     600 1 829 return $c;
100 600         856 }
101 600         1121  
102 600 50       1210 =item C<< get_char( $char ) >>
103 600         961  
104             Consume and return a single character from the buffer.
105              
106             =cut
107              
108             my $self = shift;
109             my $c = substr($self->{buffer}, 0, 1, '');
110             if ($c eq "\n") {
111             # $self->{linebuffer} = '';
112             $self->{line} = 1+$self->{line};
113 857     857 1 1146 $self->{column} = 1;
114 857         2412 } else {
115 857 50       1597 # $self->{linebuffer} .= $c;
116             $self->{column} = 1+$self->{column};
117 0         0 }
118 0         0 return $c;
119             }
120              
121 857         1466 =item C<< peek_char( $char ) >>
122              
123 857         1484 Return a single character from the start of the buffer.
124              
125             =cut
126              
127             my $self = shift;
128             if (length($self->{buffer}) == 0) {
129             $self->fill_buffer;
130             return if (length($self->{buffer}) == 0);
131             }
132             return substr($self->{buffer}, 0, 1);
133 1582     1582 1 2147 }
134 1582 100       3550  
135 337         814 =item C<< read_word( $word ) >>
136 337 100       1196  
137             Consume the string C<< $word >> from the start of the buffer.
138 1277         3224 Throw an error if C<< $word >> is not at the start of the buffer.
139              
140             =cut
141              
142             my $self = shift;
143             my $word = shift;
144             $self->fill_buffer while (length($self->{buffer}) < length($word));
145             $self->_throw_error("Expected '$word'") if (substr($self->{buffer}, 0, length($word)) ne $word);
146            
147             my $lines = ($word =~ tr/\n//);
148             my $lastnl = rindex($word, "\n");
149 88     88 1 148 my $cols = length($word) - $lastnl - 1;
150 88         125 $self->{lines} += $lines;
151 88         268 if ($lines) {
152 88 50       256 $self->{column} = $cols;
153             } else {
154 88         175 $self->{column} += $cols;
155 88         190 }
156 88         161 substr($self->{buffer}, 0, length($word), '');
157 88         173 }
158 88 50       185  
159 0         0 =item C<< read_length( $length ) >>
160              
161 88         132 Consume and return C<< $length >> characters from the start of the buffer.
162              
163 88         282 =cut
164              
165             my $self = shift;
166             my $len = shift;
167             while (length($self->{buffer}) < $len) {
168             my $curlen = length($self->{buffer});
169             $self->fill_buffer;
170             last if (length($self->{buffer}) == $curlen);
171             }
172            
173 1906     1906 1 3375 my $word = substr($self->{buffer}, 0, $len, '');
174 1906         3134 my $lines = ($word =~ tr/\n//);
175 1906         4463 my $lastnl = rindex($word, "\n");
176 0         0 my $cols = length($word) - $lastnl - 1;
177 0         0 $self->{lines} += $lines;
178 0 0       0 if ($lines) {
179             $self->{column} = $cols;
180             } else {
181 1906         5360 $self->{column} += $cols;
182 1906         3236 }
183 1906         2936 return $word;
184 1906         2846 }
185 1906         2770 }
186 1906 100       3050  
187 123         240 1;
188              
189 1783         2338  
190             =back
191 1906         3801  
192             =head1 BUGS
193              
194             Please report any bugs or feature requests to through the GitHub web interface
195             at L<https://github.com/kasei/attean/issues>.
196              
197             =head1 SEE ALSO
198              
199              
200              
201             =head1 AUTHOR
202              
203             Gregory Todd Williams C<< <gwilliams@cpan.org> >>
204              
205             =head1 COPYRIGHT
206              
207             Copyright (c) 2014--2022 Gregory Todd Williams.
208             This program is free software; you can redistribute it and/or modify it under
209             the same terms as Perl itself.
210              
211             =cut