File Coverage

blib/lib/Go/Tokenize.pm
Criterion Covered Total %
statement 30 45 66.6
branch 1 4 25.0
condition n/a
subroutine 9 10 90.0
pod 1 2 50.0
total 41 61 67.2


line stmt bran cond sub pod time code
1             package Go::Tokenize;
2 1     1   735 use warnings;
  1         1  
  1         34  
3 1     1   5 use strict;
  1         1  
  1         18  
4 1     1   5 use Carp;
  1         2  
  1         53  
5 1     1   5 use utf8;
  1         2  
  1         4  
6             require Exporter;
7             our @ISA = qw(Exporter);
8             our @EXPORT_OK = qw/tokenize/;
9             our %EXPORT_TAGS = (
10             all => \@EXPORT_OK,
11             );
12             our $VERSION = '0.02';
13              
14 1     1   637 use Text::LineNumber;
  1         370  
  1         32  
15 1     1   517 use C::Tokenize qw!$comment_re!;
  1         4487  
  1         296  
16              
17             our $bt_string_re = qr!`[^`]*`!;
18             our $q_string_re = qr!"(\\"|[^"])*"!;
19             our $string_re = qr!(?:$bt_string_re|$q_string_re)!;
20              
21             # https://golang.org/ref/spec#Keywords
22              
23             # PAUSE thinks this is package switch without the newline
24             our @keywords = qw!
25             break default func interface select
26             case defer go map struct
27             chan else goto package
28             switch
29             const fallthrough if range type
30             continue for import return var
31             !;
32              
33             # https://golang.org/ref/spec#Operators_and_punctuation
34              
35             our $operator_re;
36             {
37             # Perl makes an error message "Possible attempt to separate words with
38             # commas at lib/Go/Tokenize.pm line 40." See
39             # https://stackoverflow.com/questions/19573977/
40 1     1   8 no warnings 'qw';
  1         2  
  1         243  
41             my @operators = (qw@
42             + & += &= && == != ( )
43             - | -= |= || < <= [ ]
44             * ^ *= ^= <- > >= { }
45             / << /= <<= ++ = := , ;
46             % >> %= >>= -- ! ... . :
47             &^ &^=
48             @);
49             $operator_re = make_re (@operators);
50             }
51              
52             our $keyword_re = make_re (@keywords);
53              
54             our $integer_re = qr!
55             0
56             |
57             [1-9][0-9_]*
58             |
59             0[bB][01_]+
60             |
61             0[oO]?[0-7_]+
62             |
63             0[xX][0-9a-fA-F_]+
64             !x;
65              
66             our $numeric_re = qr!
67             u?int(?:8|16|32|64)?
68             |
69             float(?:32|64)
70             |
71             complex(?:64|128)
72             |
73             byte
74             |
75             rune
76             |
77             uintptr
78             !x;
79              
80             # https://perldoc.perl.org/perlre
81             # https://perldoc.perl.org/perlunicode
82             # https://golang.org/ref/spec#unicode_letter
83             # PropertyValueAliases.txt
84              
85             # https://golang.org/ref/spec#Letters_and_digits
86              
87 1     1   6 my $letter = qr!\p{L}|_!;
  1         2  
  1         14  
88              
89             our $identifier_re = qr!$letter(?:$letter|\p{Nd})*!;
90              
91             our $rune_re = qr!
92             '(?:
93             .
94             |
95             \\u(?:[0-9a-fA-F]{4})
96             |
97             \\U(?:[0-9a-fA-F]{8})
98             |
99             \\o(?:[0-7]{3})
100             |
101             \\x(?:[0-9a-fA-F]{2})
102             |
103             # https://golang.org/ref/spec#escaped_char
104             \\[abfnrtv\\'"]
105             )'!x;
106              
107             our $whitespace_re = qr!\x20|\x09|\x0D|\x0A!;
108              
109             our $go_re = qr!
110             # Comment must go before everything else.
111             (?$comment_re)
112             |
113             # String must go before everything except comments.
114             (?$string_re)
115             |
116             (?$keyword_re)
117             |
118             (?$operator_re)
119             |
120             (?$integer_re)
121             |
122             (?$numeric_re)
123             |
124             (?$identifier_re)
125             |
126             (?$whitespace_re)
127             !x;
128              
129             our @types = (qw!
130             comment
131             identifier
132             integer
133             keyword
134             numeric
135             operator
136             rune
137             string
138             whitespace
139             !);
140              
141             sub tokenize
142             {
143 0     0 1 0 my ($go) = @_;
144 0         0 my $tln = Text::LineNumber->new ($go);
145 0         0 my @tokens;
146 0         0 while ($go =~ /($go_re)/g) {
147 0         0 my %token;
148 0         0 $token{contents} = $1;
149 0         0 $token{end} = pos ($go);
150 0         0 $token{start} = $token{end} - length ($token{contents}) + 1;
151 0         0 for my $type (@types) {
152 0 0       0 if ($+{$type}) {
153 0         0 $token{type} = $type;
154 0         0 last;
155             }
156             }
157 0         0 $token{line} = $tln->off2lnr ($token{start});
158 0         0 push @tokens, \%token;
159             }
160 0         0 return \@tokens;
161             }
162              
163             sub make_re
164             {
165 2 50   2 0 6 my @sorted = sort {length ($b) <=> length ($a) || $a cmp $b} @_;
  296         453  
166 2         4 my @quoted = map {quotemeta ($_)} @sorted;
  72         102  
167 2         11 my $re = join '|', @quoted;
168 2         11 return $re;
169             }
170              
171             1;