| 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__ |