File Coverage

blib/lib/Text/TokenStream/Lexer.pm
Criterion Covered Total %
statement 52 52 100.0
branch 11 12 91.6
condition n/a
subroutine 14 14 100.0
pod 2 2 100.0
total 79 80 98.7


line stmt bran cond sub pod time code
1             package Text::TokenStream::Lexer;
2              
3 2     2   84758 use v5.12;
  2         8  
4 2     2   611 use Moo;
  2         11890  
  2         13  
5              
6             our $VERSION = '0.02';
7              
8 2     2   2055 use Carp qw(confess);
  2         4  
  2         105  
9 2     2   13 use List::Util qw(pairmap);
  2         4  
  2         171  
10 2     2   527 use Text::TokenStream::Types qw(Identifier LexerRule);
  2         15  
  2         25  
11 2     2   1195 use Types::Standard qw(ArrayRef CycleTuple ScalarRef Str);
  2         5  
  2         14  
12              
13 2     2   2997 use namespace::clean;
  2         11376  
  2         16  
14              
15             has rules => (
16             is => 'ro',
17             isa => CycleTuple[Identifier, LexerRule],
18             required => 1,
19             );
20              
21             has whitespace => (
22             is => 'ro',
23             isa => ArrayRef[LexerRule],
24             default => sub { [] },
25             );
26              
27             has _whitespace_rx => (is => 'lazy', init_arg => undef, builder => sub {
28 3     3   34 my ($self) = @_;
29 3 50       7 my @whitespace = map ref() ? $_ : quotemeta, @{ $self->whitespace }
  3 100       38  
30             or return qr/(*FAIL)/;
31 2         6 local $" = '|';
32 2         76 return qr/^(?:@whitespace)/;
33             });
34              
35             has _rules_rx => (is => 'lazy', init_arg => undef, builder => sub {
36 3     3   29 my ($self) = @_;
37 19     19   465 my @annotated_rules = pairmap { qr/$b(*MARK:$a)/ }
38 19 100   19   54 pairmap { $a => (ref $b ? $b : quotemeta $b) }
39 3 100       19 @{ $self->rules }
  3         48  
40             or return qr/(*FAIL)/;
41 2         31 local $" = '|';
42 2         506 qr/^(?|@annotated_rules)/;
43             });
44              
45             sub skip_whitespace {
46 35     35 1 1366 my ($self, $str_ref) = @_;
47 35         105 (ScalarRef[Str])->assert_valid($str_ref);
48              
49 35         16317 my $ret = 0;
50 35         1981 my $whitespace_rx = $self->_whitespace_rx;
51 35         448 $ret = 1 while $$str_ref =~ s/$whitespace_rx//;
52              
53 35         83 return $ret;
54             }
55              
56             sub next_token {
57 35     35 1 2129 my ($self, $str_ref) = @_;
58 35         101 (ScalarRef[Str])->assert_valid($str_ref);
59              
60 35         18963 my $saw_whitespace = $self->skip_whitespace($str_ref);
61              
62 35 100       107 return undef if !length $$str_ref;
63              
64 34 100       556 if ($$str_ref !~ $self->_rules_rx) {
65 1         4 my $text = substr $$str_ref, 0, 30;
66 1         260 confess("No matching rule; next text is: $text");
67             }
68              
69 33         381 my $type = our $REGMARK;
70 2     2   3061 my $captures = { %+ };
  2         974  
  2         213  
  33         191  
71 33         141 my $text = substr($$str_ref, 0, $+[0], '');
72              
73             return {
74 33         217 type => $type,
75             captures => $captures,
76             text => $text,
77             cuddled => 0+!$saw_whitespace,
78             };
79             }
80              
81             1;
82             __END__