File Coverage

blib/lib/Hardware/Vhdl/Lexer.pm
Criterion Covered Total %
statement 72 75 96.0
branch 52 54 96.3
condition 2 3 66.6
subroutine 16 17 94.1
pod 2 3 66.6
total 144 152 94.7


line stmt bran cond sub pod time code
1             package Hardware::Vhdl::Lexer;
2            
3 3     3   203871 use Class::Std;
  3         46463  
  3         21  
4 3     3   289 use Carp;
  3         7  
  3         207  
5 3     3   3574 use Readonly;
  3         9719  
  3         292  
6 3     3   24 use strict;
  3         5  
  3         94  
7 3     3   17 use warnings;
  3         6  
  3         2992  
8             #use diagnostics;
9            
10             =for To do:
11             'use charnames' instead of \012 and \015
12             test get_nhistory and get_linesource
13             use regexp-generating module for number-matching regexps
14            
15             =cut
16            
17             our $VERSION = "1.00";
18            
19             # Create storage for object attributes...
20             my %nhistory :ATTR( :default<1> :get :init_arg );
21             my %linesource :ATTR( :default :get );
22             my %line :ATTR( :default );
23             my %source_func :ATTR;
24             my %history :ATTR;
25            
26             sub START {
27 53     53 0 77372 my ($self, $obj_ID, $arg_ref) = @_;
28 53         98 my $class = ref($self);
29            
30             # check that a linesource was specified
31 53 100       239 croak "$class constructor requires a linesource to be specified"
32             if !defined $arg_ref->{linesource};
33            
34             {
35 52         56 my $sourcetype = ref $arg_ref->{linesource};
  52         89  
36             # store the source of lines as a subroutine reference
37             $source_func{$obj_ID} =
38             $sourcetype eq q{} ? croak "${class}->new 'linesource' parameter is not of a valid type (it is not a reference)" :
39 9     9   102 $sourcetype eq 'GLOB' ? sub { readline( $arg_ref->{linesource} ) } :
40             $sourcetype eq 'ARRAY' ? _arrayref_to_sub($arg_ref->{linesource}) :
41             $sourcetype eq 'SCALAR' ? _scalarref_to_sub($arg_ref->{linesource}) :
42             $sourcetype eq 'CODE' ? $arg_ref->{linesource} :
43             #~ $sourcetype ne 'REF' &&
44             eval "$sourcetype->can('get_next_line')"
45 98     98   334 ? sub { $arg_ref->{linesource}->get_next_line } :
46 52 100       2544 croak "${class}->new 'linesource' parameter is not of a valid type (type is '$sourcetype')";
    100          
    100          
    100          
    100          
    100          
47            
48             }
49            
50             # set up initial history values
51 48         791 for my $i ( 1 .. $self->get_nhistory ) { $history{$obj_ID}->[ $i - 1 ] = q{} }
  57         378  
52             #@{ $history{$obj_ID} } = q{} x $self->get_nhistory;
53            
54 48         150 pos($line{$obj_ID}) = 0;
55            
56 48         164 return $self;
57             }
58            
59             sub _arrayref_to_sub {
60             # given an array ref, return a ref to a sub which returns the lines in sequence and then returns undef
61 1     1   2 my $array_ref = shift;
62 1         2 my $i = 0;
63             return sub {
64 18     18   40 return $array_ref->[ $i++ ];
65 1         7 };
66             }
67            
68             sub _scalarref_to_sub {
69             # given a scalar ref, return a ref to a sub which returns the line and then returns undef
70 1     1   2 my $scalar_ref = shift;
71 1         3 my $i = 0;
72             return sub {
73 2 100   2   9 return $i++ == 0 ? ${ $scalar_ref } : undef;
  1         4  
74 1         6 };
75             }
76            
77             # after use charnames qw( :full );
78             # \N{CR} is character 13 = 015
79             # \N{LF} is character 10 = 012
80             #my $NEW_LINE = qr/ \N{CR}\N{LF}? | \N{LF}\N{CR}? /xms;
81             my Readonly $NEW_LINE = qr/ \015\012? | \012\015? /xms;
82             my Readonly $WHITESPACE = qr/ [^\S\012\015]+ /xms;
83             my Readonly $COMMENT = qr/ -- [^\015\012]* /xms;
84             my Readonly $BIT_VECTOR_LITERAL = qr/ [BOX] ".+?" /xms;
85             my Readonly $BASED_NUMBER = qr/
86             (?: [23456789] | 1[0123456] ) # the base (2-16)
87             \# [\d_A-F]+ \# # the number
88             /xmsi;
89             my Readonly $BASE10_REAL = qr/ -? \d [\d_]* (?: \. \d*)? (?: E -? \d+)? /xmsi;
90             my Readonly $IDENTIFIER = qr/ (?: \\ [^\\]+ \\) | (?: \w+ ) /xms;
91             my Readonly $PUNCTUATION = qr{
92             [:<>/]= | => | <> | \*\* # 2-character punctuations
93             | [ \.\,\+\-\*\=\:\;\&\'\(\)\<\>\|\/ ]
94             }xms;
95             my Readonly $DBL_QUOTED = qr/
96             " # opening quote
97             .*? # contents of the quotes
98             (?
99             (?:\\\\)* # an even number of backslashes
100             " # closing quote
101             /xms;
102             my Readonly $CHAR_LITERAL = qr/
103             '.' # a character in single-quotes
104             (?= # followed by...
105             (?: .'.' )* # any number of following character literals
106             (?! .' ) # without leaving us with an unmatched single-quote
107             .* # and anything that follows
108             )
109             /xms;
110            
111             sub _as_str :STRINGIFY {
112 0     0   0 my $self = shift;
113 0         0 return scalar $self->get_next_token();
114 3     3   21 }
  3         4  
  3         28  
115            
116             sub get_next_token {
117 392     392 1 4098 my $self = shift;
118 392         673 my $obj_ID = ident $self;
119            
120             # get another line from the line-source if needed
121 392 100 66     1911 if ( defined $line{$obj_ID} && pos($line{$obj_ID}) >= length $line{$obj_ID} ) {
122 143         146 $line{$obj_ID} = &{ $source_func{$obj_ID} };
  143         241  
123 143 100       765 pos($line{$obj_ID}) = 0 if defined $line{$obj_ID};
124             }
125             # an undef line means the end of the VHDL source - no more tokens
126 392 100       871 return if !defined $line{$obj_ID};
127            
128 347 50       7176 my ($token, $match) =
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
    100          
129             $line{$obj_ID} =~ m/\G ($NEW_LINE) /gcxms ? ($1, 'wn') : # newline
130             $line{$obj_ID} =~ m/\G ($WHITESPACE) /gcxms ? ($1, 'ws') : # whitespace
131             substr( $line{$obj_ID}, pos($line{$obj_ID}), 1 ) eq q{"}
132             ? ($self->_dquoted_string(), 'cs') : # string literal
133             $line{$obj_ID} =~ m/\G ($COMMENT) /gcxms ? ($1, 'r' ) : # comment
134             $line{$obj_ID} =~ m/\G ($CHAR_LITERAL) /gcxms ? ($1, 'cc') : # single-character literal
135             $line{$obj_ID} =~ m/\G ($BIT_VECTOR_LITERAL) /gcxms ? ($1, 'cb') : # bit_vector literal
136             $line{$obj_ID} =~ m/\G ($BASED_NUMBER) /gcxms ? ($1, 'cn') : # specified-base integer numeric literal
137             $line{$obj_ID} =~ m/\G ($BASE10_REAL) /gcxms ? ($1, 'cn') : # base-10 numeric literal
138             $line{$obj_ID} =~ m/\G ($IDENTIFIER) /gcxms ? ($1, 'ci') : # extended identifier or keyword
139             $line{$obj_ID} =~ m/\G ($PUNCTUATION) /gcxms ? ($1, 'cp') : # punctuation
140             $line{$obj_ID} =~ m/\G (.) /gcxms ? ($1, 'cu') : # unexpected character
141             croak "Internal error (token failed to match anything): "
142             . "Please file a bug report, showing what input caused this error\n";
143            
144 347 100       753 if ( substr( $match, 0, 1 ) eq 'c' ) {
145            
146             # not whitespace or comment, so add it to the code history
147 252         235 push @{ $history{$obj_ID} }, $token;
  252         499  
148 252         291 while ( @{ $history{$obj_ID} } > $self->get_nhistory ) {
  504         1327  
149 252         1086 shift @{ $history{$obj_ID} };
  252         458  
150             }
151             }
152            
153 347 100       2575 return wantarray ? ( $token, $match ) : $token;
154             }
155            
156             sub _dquoted_string {
157 14     14   20 my $self = shift;
158 14         27 my $obj_ID = ident $self;
159             # this method should only be called when we already know we have an open-quote at the match-start point of $line{$obj_ID}
160 14         16 while (1) {
161 14 100       153 if ( $line{$obj_ID} =~ /\G ($DBL_QUOTED) /gcxms ) {
162 12         46 return $1;
163             }
164            
165             # can't match a closing quote - get another line from the source
166 2         3 my $nextline = &{ $source_func{$obj_ID} };
  2         6  
167 2 50       12 if ( !defined $nextline ) {
168             # reached EOF without finding closing quote: we're done
169 2         3 my $start_pos = pos $line{$obj_ID};
170 2         5 pos $line{$obj_ID} = length $line{$obj_ID};
171 2         8 return substr $line{$obj_ID}, $start_pos;
172             }
173 0         0 $line{$obj_ID} .= $nextline;
174             }
175             }
176            
177             sub history {
178 46     46 1 15885 my $self = shift;
179 46         55 my $age = shift;
180 46         88 my $obj_ID = ident $self;
181            
182 46         1282 croak "more (" . ( $age + 1 ),
183             ") history requested than has been stored ("
184             . ( $nhistory{$obj_ID} ) . ")"
185 46 100       47 if $age >= @{ $history{$obj_ID} };
186 34         163 return $history{$obj_ID}->[ -1 - $age ];
187             }
188            
189             1; # End of Hardware::Vhdl::Lexer
190            
191             __END__