| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package CodeGen::Cpppp::Enum; |
|
2
|
|
|
|
|
|
|
|
|
3
|
|
|
|
|
|
|
our $VERSION = '0.005'; # VERSION |
|
4
|
|
|
|
|
|
|
# ABSTRACT: Helper for enumerations and generating related utility functions |
|
5
|
|
|
|
|
|
|
|
|
6
|
1
|
|
|
1
|
|
192893
|
use v5.20; |
|
|
1
|
|
|
|
|
3
|
|
|
7
|
1
|
|
|
1
|
|
5
|
use warnings; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
34
|
|
|
8
|
1
|
|
|
1
|
|
4
|
use Carp; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
67
|
|
|
9
|
1
|
|
|
1
|
|
417
|
use experimental 'signatures', 'lexical_subs', 'postderef'; |
|
|
1
|
|
|
|
|
2955
|
|
|
|
1
|
|
|
|
|
4
|
|
|
10
|
1
|
|
|
1
|
|
160
|
use Scalar::Util 'looks_like_number'; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
54
|
|
|
11
|
1
|
|
|
1
|
|
3
|
use List::Util 'any', 'min', 'max'; |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
139
|
|
|
12
|
1
|
|
33
|
1
|
|
27
|
BEGIN { *uniqstr= List::Util->can('uniqstr') // sub { my %seen; grep !$seen{$_}++, @_ } } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
13
|
1
|
|
|
1
|
|
420
|
use CodeGen::Cpppp::CParser; |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
3622
|
|
|
14
|
|
|
|
|
|
|
|
|
15
|
|
|
|
|
|
|
|
|
16
|
6
|
|
|
6
|
1
|
171512
|
sub new($class, %attrs) { |
|
|
6
|
|
|
|
|
8
|
|
|
|
6
|
|
|
|
|
14
|
|
|
|
6
|
|
|
|
|
5
|
|
|
17
|
6
|
|
|
|
|
11
|
my $self= bless {}, $class; |
|
18
|
|
|
|
|
|
|
# apply num_format first because it affects set_values |
|
19
|
|
|
|
|
|
|
$self->num_format(delete $attrs{num_format}) |
|
20
|
6
|
50
|
|
|
|
34
|
if exists $attrs{num_format}; |
|
21
|
6
|
|
|
|
|
25
|
$self->$_($attrs{$_}) for keys %attrs; |
|
22
|
6
|
|
|
|
|
13
|
return $self; |
|
23
|
|
|
|
|
|
|
} |
|
24
|
|
|
|
|
|
|
|
|
25
|
|
|
|
|
|
|
|
|
26
|
15
|
|
|
15
|
1
|
13
|
sub prefix($self, @val) { |
|
|
15
|
|
|
|
|
16
|
|
|
|
15
|
|
|
|
|
16
|
|
|
|
15
|
|
|
|
|
11
|
|
|
27
|
15
|
100
|
|
|
|
19
|
if (@val) { $self->{prefix}= $val[0]; return $self } |
|
|
4
|
|
|
|
|
6
|
|
|
|
4
|
|
|
|
|
10
|
|
|
28
|
11
|
|
50
|
|
|
46
|
$self->{prefix} // '' |
|
29
|
|
|
|
|
|
|
} |
|
30
|
|
|
|
|
|
|
|
|
31
|
7
|
|
|
7
|
0
|
8
|
sub macro_prefix($self, @val) { |
|
|
7
|
|
|
|
|
6
|
|
|
|
7
|
|
|
|
|
6
|
|
|
|
7
|
|
|
|
|
7
|
|
|
32
|
7
|
50
|
|
|
|
9
|
if (@val) { $self->{macro_prefix}= $val[0]; return $self } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
33
|
7
|
|
33
|
|
|
15
|
$self->{macro_prefix} // uc($self->prefix); |
|
34
|
|
|
|
|
|
|
} |
|
35
|
|
|
|
|
|
|
|
|
36
|
3
|
|
|
3
|
0
|
4
|
sub symbol_prefix($self, @val) { |
|
|
3
|
|
|
|
|
3
|
|
|
|
3
|
|
|
|
|
3
|
|
|
|
3
|
|
|
|
|
3
|
|
|
37
|
3
|
50
|
|
|
|
5
|
if (@val) { $self->{symbol_prefix}= $val[0]; return $self } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
38
|
3
|
|
33
|
|
|
8
|
$self->{symbol_prefix} // lc($self->prefix); |
|
39
|
|
|
|
|
|
|
} |
|
40
|
|
|
|
|
|
|
|
|
41
|
1
|
|
|
1
|
1
|
1
|
sub type($self, @val) { |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
1
|
|
|
42
|
1
|
50
|
|
|
|
2
|
if (@val) { $self->{type}= $val[0]; return $self; } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
43
|
1
|
|
50
|
|
|
5
|
$self->{type} // 'int'; |
|
44
|
|
|
|
|
|
|
} |
|
45
|
|
|
|
|
|
|
|
|
46
|
20
|
|
|
20
|
1
|
31
|
sub values($self, @val) { |
|
|
20
|
|
|
|
|
24
|
|
|
|
20
|
|
|
|
|
22
|
|
|
|
20
|
|
|
|
|
21
|
|
|
47
|
20
|
100
|
|
|
|
39
|
return $self->set_values(@val) if @val; |
|
48
|
11
|
|
50
|
|
|
12
|
@{ $self->{values} // [] } |
|
|
11
|
|
|
|
|
63
|
|
|
49
|
|
|
|
|
|
|
} |
|
50
|
|
|
|
|
|
|
|
|
51
|
9
|
|
|
9
|
0
|
8
|
sub set_values($self, @spec) { |
|
|
9
|
|
|
|
|
9
|
|
|
|
9
|
|
|
|
|
12
|
|
|
|
9
|
|
|
|
|
7
|
|
|
52
|
9
|
|
|
|
|
10
|
my @values; |
|
53
|
9
|
100
|
66
|
|
|
30
|
for (@spec == 1 && ref $spec[0]? @{$spec[0]} : @spec) { |
|
|
7
|
|
|
|
|
13
|
|
|
54
|
34
|
100
|
|
|
|
107
|
if ('ARRAY' eq ref) { |
|
|
|
100
|
|
|
|
|
|
|
55
|
2
|
|
|
|
|
4
|
push @values, [ @$_ ]; |
|
56
|
|
|
|
|
|
|
} elsif (/^\w+$/) { |
|
57
|
30
|
|
|
|
|
43
|
push @values, [ $_ ]; |
|
58
|
|
|
|
|
|
|
} else { |
|
59
|
2
|
50
|
|
|
|
11
|
defined $values[-1] or croak "Got an enum value '$_' before a name"; |
|
60
|
2
|
50
|
|
|
|
4
|
defined $values[-1][1] and croak "'$_' is not a valid enum name"; |
|
61
|
2
|
|
|
|
|
5
|
$values[-1][1]= $_; |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
} |
|
64
|
|
|
|
|
|
|
# Fill in missing values with next sequential integer |
|
65
|
9
|
|
100
|
|
|
29
|
my $prev= $values[0][1] //= 0; |
|
66
|
9
|
|
|
|
|
18
|
for (@values[1..$#values]) { |
|
67
|
23
|
100
|
|
|
|
37
|
if (!defined $_->[1]) { |
|
68
|
22
|
|
|
|
|
33
|
my ($base, $ofs, $fmt)= $self->_parse_value_expr($prev); |
|
69
|
22
|
|
|
|
|
55
|
$_->[1]= sprintf $fmt, $ofs+1; |
|
70
|
|
|
|
|
|
|
} |
|
71
|
23
|
|
|
|
|
28
|
$prev= $_->[1]; |
|
72
|
|
|
|
|
|
|
} |
|
73
|
9
|
|
|
|
|
21
|
$self->{values}= \@values; |
|
74
|
9
|
|
|
|
|
12
|
$self->{_analysis}= undef; |
|
75
|
9
|
|
|
|
|
20
|
$self; |
|
76
|
|
|
|
|
|
|
} |
|
77
|
|
|
|
|
|
|
|
|
78
|
3
|
|
|
3
|
1
|
4
|
sub value_table_var($self, @val) { |
|
|
3
|
|
|
|
|
3
|
|
|
|
3
|
|
|
|
|
3
|
|
|
|
3
|
|
|
|
|
3
|
|
|
79
|
3
|
50
|
|
|
|
4
|
if (@val) { |
|
80
|
0
|
|
|
|
|
0
|
$self->{value_table_var}= $val[0]; |
|
81
|
0
|
|
|
|
|
0
|
return $self; |
|
82
|
|
|
|
|
|
|
} |
|
83
|
3
|
|
33
|
|
|
9
|
$self->{value_table_var} // $self->symbol_prefix . 'value_table'; |
|
84
|
|
|
|
|
|
|
} |
|
85
|
|
|
|
|
|
|
|
|
86
|
4
|
|
|
4
|
1
|
4
|
sub indent($self, @val) { |
|
|
4
|
|
|
|
|
22
|
|
|
|
4
|
|
|
|
|
6
|
|
|
|
4
|
|
|
|
|
4
|
|
|
87
|
4
|
50
|
|
|
|
7
|
if (@val) { |
|
88
|
0
|
|
|
|
|
0
|
$self->{indent}= $val[0]; |
|
89
|
0
|
|
|
|
|
0
|
return $self; |
|
90
|
|
|
|
|
|
|
} |
|
91
|
4
|
|
50
|
|
|
13
|
$self->{indent} // ' '; |
|
92
|
|
|
|
|
|
|
} |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub _current_indent { |
|
95
|
4
|
|
33
|
4
|
|
10
|
$CodeGen::Cpppp::INDENT // shift->indent; |
|
96
|
|
|
|
|
|
|
} |
|
97
|
|
|
|
|
|
|
|
|
98
|
0
|
|
|
0
|
1
|
0
|
sub num_format($self, @val) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
99
|
0
|
0
|
|
|
|
0
|
if (@val) { |
|
100
|
0
|
|
|
|
|
0
|
$self->{num_format}= $val[0]; |
|
101
|
0
|
|
|
|
|
0
|
$self->{_analysis}= undef; |
|
102
|
0
|
|
|
|
|
0
|
return $self; |
|
103
|
|
|
|
|
|
|
} |
|
104
|
0
|
|
0
|
|
|
0
|
$self->{num_format} // '%d'; |
|
105
|
|
|
|
|
|
|
} |
|
106
|
|
|
|
|
|
|
|
|
107
|
4
|
|
|
4
|
0
|
5
|
sub max_waste_factor($self, @val) { |
|
|
4
|
|
|
|
|
4
|
|
|
|
4
|
|
|
|
|
4
|
|
|
|
4
|
|
|
|
|
2
|
|
|
108
|
4
|
50
|
|
|
|
7
|
if (@val) { |
|
109
|
0
|
|
|
|
|
0
|
$self->{max_waste_factor}= $val[0]; |
|
110
|
0
|
|
|
|
|
0
|
$self->{_analysis}= undef; |
|
111
|
0
|
|
|
|
|
0
|
return $self; |
|
112
|
|
|
|
|
|
|
} |
|
113
|
4
|
|
50
|
|
|
19
|
$self->{max_waste_factor} // 2; |
|
114
|
|
|
|
|
|
|
} |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
|
|
117
|
|
|
|
|
|
|
our %_algorithm= map +( $_ => 1 ), qw( bsearch hashtable switch ); |
|
118
|
0
|
|
|
0
|
1
|
0
|
sub algorithm($self, @val) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
119
|
0
|
0
|
|
|
|
0
|
if (@val) { |
|
120
|
0
|
0
|
0
|
|
|
0
|
!defined $val[0] or $_algorithm{$val[0]} |
|
121
|
|
|
|
|
|
|
or croak "Unknown parse_design '$val[0]', expected one of ".join(', ', keys %_algorithm); |
|
122
|
0
|
|
|
|
|
0
|
$self->{algorithm}= $val[0]; |
|
123
|
0
|
|
|
|
|
0
|
return $self; |
|
124
|
|
|
|
|
|
|
} |
|
125
|
|
|
|
|
|
|
$self->{algorithm} |
|
126
|
0
|
|
|
|
|
0
|
} |
|
127
|
|
|
|
|
|
|
|
|
128
|
30
|
|
|
30
|
|
30
|
sub _parse_value_expr($self, $val) { |
|
|
30
|
|
|
|
|
25
|
|
|
|
30
|
|
|
|
|
25
|
|
|
|
30
|
|
|
|
|
29
|
|
|
129
|
|
|
|
|
|
|
# Make the common case fast |
|
130
|
30
|
100
|
|
|
|
114
|
return '', +$val, '%d' |
|
131
|
|
|
|
|
|
|
if $val =~ /^[-+]?(?:0|[1-9][0-9]*)\Z/; |
|
132
|
|
|
|
|
|
|
# else need to parse the expression |
|
133
|
5
|
|
|
|
|
17
|
my @tokens= CodeGen::Cpppp::CParser->tokenize($val); |
|
134
|
5
|
|
|
|
|
12
|
my $type_pattern= join '', map $_->type, @tokens; |
|
135
|
|
|
|
|
|
|
# Recognize patterns where a +N occurs at the end of the expression |
|
136
|
|
|
|
|
|
|
# Else, the whole value is the expression and will get '+N' appended. |
|
137
|
5
|
50
|
0
|
|
|
19
|
return $val, 0, "($val+".($self->{num_format}//'%d').")" |
|
138
|
|
|
|
|
|
|
unless $type_pattern =~ /(^|[-+])integer\W*$/; |
|
139
|
5
|
|
|
|
|
9
|
my $context= $1; |
|
140
|
|
|
|
|
|
|
# walk backward to last 'integer' token |
|
141
|
5
|
|
|
|
|
6
|
my $i= $#tokens; |
|
142
|
5
|
|
|
|
|
8
|
$i-- while $tokens[$i]->type ne 'integer'; |
|
143
|
|
|
|
|
|
|
# could be start of string, -N, +N, EXPR-N, EXPR+N, or EXPR OP -N |
|
144
|
5
|
|
|
|
|
6
|
my $fmt_str= $val; |
|
145
|
5
|
|
|
|
|
11
|
my ($pos, $pos2)= ($tokens[$i]->src_pos, $tokens[$i]->src_pos+$tokens[$i]->src_len); |
|
146
|
5
|
|
|
|
|
8
|
my $n= $tokens[$i]->value; |
|
147
|
|
|
|
|
|
|
# If start of string or preceeded by '+', nothing to do. |
|
148
|
|
|
|
|
|
|
# If preceeded by '-', need to convert that to '+' in format string |
|
149
|
5
|
100
|
|
|
|
7
|
if ($context eq '-') { |
|
150
|
1
|
|
|
|
|
2
|
$n= -$n; |
|
151
|
1
|
|
|
|
|
3
|
$pos= $tokens[$i-1]->src_pos + 1; |
|
152
|
1
|
|
|
|
|
22
|
substr($fmt_str, $tokens[$i-1]->src_pos, 1, '+'); |
|
153
|
|
|
|
|
|
|
} |
|
154
|
5
|
|
|
|
|
10
|
my $num_str= substr($val, $tokens[$i]->src_pos, $tokens[$i]->src_len); |
|
155
|
|
|
|
|
|
|
my $notation= $self->{num_format} |
|
156
|
5
|
50
|
33
|
|
|
20
|
// $num_str =~ /^-?0x[0-9A-F]+$/? 'X' |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
157
|
|
|
|
|
|
|
: $num_str =~ /^-?0x[0-9a-f]+$/? 'x' |
|
158
|
|
|
|
|
|
|
: $num_str =~ /^-?0[0-9]+/? 'o' |
|
159
|
|
|
|
|
|
|
: 'd'; |
|
160
|
5
|
|
|
|
|
16
|
substr($fmt_str, $pos, $pos2-$pos, '%'.($pos2-$pos).$notation); |
|
161
|
|
|
|
|
|
|
# The "base" is everying to the left of the number minus the number of "(" |
|
162
|
|
|
|
|
|
|
# to match the number of ")" to the right of the number |
|
163
|
5
|
|
|
|
|
12
|
my $rparen= grep $_->type eq ')', @tokens[$i..$#tokens]; |
|
164
|
5
|
|
66
|
|
|
8
|
shift @tokens while $tokens[0]->type eq '(' && $rparen--; |
|
165
|
5
|
|
|
|
|
7
|
my $base= substr($val, $tokens[0]->src_pos, $pos-$tokens[0]->src_pos); |
|
166
|
5
|
|
|
|
|
20
|
return ($base, $n, $fmt_str); |
|
167
|
|
|
|
|
|
|
} |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
|
|
170
|
0
|
|
|
0
|
1
|
0
|
sub is_symbolic($self) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
171
|
0
|
|
|
|
|
0
|
$self->_analysis->{base_expr} ne ''; |
|
172
|
|
|
|
|
|
|
} |
|
173
|
|
|
|
|
|
|
|
|
174
|
0
|
|
|
0
|
1
|
0
|
sub is_sequential($self) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
175
|
|
|
|
|
|
|
$self->_analysis->{is_seq} |
|
176
|
0
|
|
|
|
|
0
|
} |
|
177
|
|
|
|
|
|
|
|
|
178
|
0
|
|
|
0
|
0
|
0
|
sub is_nearly_sequential($self) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
179
|
|
|
|
|
|
|
$self->_analysis->{is_nearly_seq} |
|
180
|
0
|
|
|
|
|
0
|
} |
|
181
|
|
|
|
|
|
|
|
|
182
|
2
|
|
|
2
|
|
2
|
sub _analysis($self) { |
|
|
2
|
|
|
|
|
3
|
|
|
|
2
|
|
|
|
|
2
|
|
|
183
|
2
|
|
33
|
|
|
6
|
$self->{_analysis} //= do { |
|
184
|
2
|
|
|
|
|
3
|
my @vals= map +[ $_->[0], $self->_parse_value_expr($_->[1]) ], $self->values; |
|
185
|
2
|
|
|
|
|
4
|
my $base_expr= $vals[0][1]; |
|
186
|
2
|
|
|
|
|
5
|
my %seen_ofs= ( $vals[0][2] => 1 ); |
|
187
|
2
|
|
|
|
|
14
|
for (@vals[1..$#vals]) { |
|
188
|
|
|
|
|
|
|
# Can't be sequential unless they share a symbolic base expression |
|
189
|
6
|
50
|
|
|
|
12
|
$base_expr= undef, last |
|
190
|
|
|
|
|
|
|
unless $_->[1] eq $base_expr; |
|
191
|
6
|
|
|
|
|
9
|
$seen_ofs{$_->[2]}++; |
|
192
|
|
|
|
|
|
|
} |
|
193
|
2
|
|
|
|
|
5
|
my %info= ( |
|
194
|
|
|
|
|
|
|
vals => \@vals |
|
195
|
|
|
|
|
|
|
); |
|
196
|
2
|
50
|
|
|
|
4
|
if (defined $base_expr) { |
|
197
|
|
|
|
|
|
|
# Find the min/max |
|
198
|
2
|
|
|
|
|
18
|
my ($min, $max)= (min(keys %seen_ofs), max(keys %seen_ofs)); |
|
199
|
|
|
|
|
|
|
# Is it sequential? |
|
200
|
2
|
|
|
|
|
3
|
my ($is_seq, $is_nearly_seq, $gap); |
|
201
|
|
|
|
|
|
|
# don't iterate unless the range is reasonable |
|
202
|
2
|
50
|
|
|
|
6
|
if (($max - $min - @vals) <= $self->max_waste_factor * @vals) { |
|
203
|
2
|
|
|
|
|
2
|
$gap= 0; |
|
204
|
2
|
|
|
|
|
6
|
for ($min .. $max) { |
|
205
|
8
|
50
|
|
|
|
14
|
$gap++ unless $seen_ofs{$_}; |
|
206
|
|
|
|
|
|
|
} |
|
207
|
2
|
|
|
|
|
2
|
$is_seq= $gap == 0; |
|
208
|
2
|
|
|
|
|
3
|
$is_nearly_seq= $gap <= $self->max_waste_factor * ($max-$min+1-$gap); |
|
209
|
|
|
|
|
|
|
} |
|
210
|
2
|
|
|
|
|
4
|
$info{is_seq}= $is_seq; |
|
211
|
2
|
|
|
|
|
7
|
$info{is_nearly_seq}= $is_nearly_seq; |
|
212
|
2
|
|
|
|
|
5
|
$info{gap}= $gap; |
|
213
|
2
|
|
|
|
|
3
|
$info{min}= $min; |
|
214
|
2
|
|
|
|
|
3
|
$info{max}= $max; |
|
215
|
2
|
|
|
|
|
4
|
$info{base_expr}= $base_expr; |
|
216
|
|
|
|
|
|
|
} |
|
217
|
2
|
|
|
|
|
6
|
\%info |
|
218
|
|
|
|
|
|
|
}; |
|
219
|
|
|
|
|
|
|
} |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
|
|
222
|
0
|
|
|
0
|
1
|
0
|
sub generate_declaration($self, %options) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
223
|
0
|
|
|
|
|
0
|
return join "\n", $self->_generate_declaration_macros(\%options); |
|
224
|
|
|
|
|
|
|
} |
|
225
|
|
|
|
|
|
|
|
|
226
|
1
|
|
|
1
|
|
5
|
sub _generate_declaration_macros($self, $options) { |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
1
|
|
|
227
|
1
|
|
|
|
|
3
|
my @vals= $self->values; |
|
228
|
1
|
|
|
|
|
10
|
my $name_width= max map length($_->[0]), @vals; |
|
229
|
1
|
|
|
|
|
3
|
my $prefix= $self->macro_prefix; |
|
230
|
1
|
|
|
|
|
3
|
my $fmt= "#define $prefix%-${name_width}s %s"; |
|
231
|
1
|
|
|
|
|
9
|
return map sprintf($fmt, $_->[0], $_->[1]), @vals; |
|
232
|
|
|
|
|
|
|
} |
|
233
|
|
|
|
|
|
|
|
|
234
|
|
|
|
|
|
|
|
|
235
|
0
|
|
|
0
|
1
|
0
|
sub generate_static_tables($self, %options) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
236
|
0
|
|
|
|
|
0
|
return join "\n", _generate_enum_table($self, \%options); |
|
237
|
|
|
|
|
|
|
} |
|
238
|
|
|
|
|
|
|
|
|
239
|
1
|
|
|
1
|
|
6
|
sub _generate_enum_table($self, $options) { |
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
1
|
|
|
240
|
1
|
|
|
|
|
2
|
my $prefix= $self->prefix; |
|
241
|
1
|
|
|
|
|
3
|
my @names= map $prefix . $_->[0], $self->values; |
|
242
|
1
|
|
|
|
|
5
|
my $name_width= max map length, @names; |
|
243
|
1
|
|
|
|
|
4
|
my $indent= $self->_current_indent; |
|
244
|
1
|
|
|
|
|
3
|
my $fmt= $indent.$indent.'{ "%s",%*s %s },'; |
|
245
|
1
|
|
|
|
|
3
|
my @code= ( |
|
246
|
|
|
|
|
|
|
"const struct { const char *name; const ".$self->type." value; }", |
|
247
|
|
|
|
|
|
|
$indent . $self->value_table_var . "[] = {", |
|
248
|
|
|
|
|
|
|
(map sprintf($fmt, $_, $name_width-length, '', $_), @names), |
|
249
|
|
|
|
|
|
|
$indent . '};' |
|
250
|
|
|
|
|
|
|
); |
|
251
|
1
|
|
|
|
|
3
|
substr($code[-2], -1, 1, ''); # remove trailing comma |
|
252
|
1
|
|
|
|
|
22
|
return @code; |
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
|
|
256
|
0
|
|
|
0
|
1
|
0
|
sub generate_lookup_by_value($self, %options) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
257
|
0
|
|
|
|
|
0
|
return join "\n", $self->_generate_lookup_by_value_switch(\%options); |
|
258
|
|
|
|
|
|
|
} |
|
259
|
|
|
|
|
|
|
|
|
260
|
1
|
|
|
1
|
|
6
|
sub _generate_lookup_by_value_switch($self, $options) { |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
1
|
|
|
|
1
|
|
|
|
|
1
|
|
|
261
|
1
|
|
|
|
|
3
|
my @vals= $self->values; |
|
262
|
1
|
|
|
|
|
6
|
my $name_width= max map length($_->[0]), @vals; |
|
263
|
1
|
|
|
|
|
3
|
my $info= $self->_analysis; |
|
264
|
1
|
|
|
|
|
2
|
my $val_variable= 'value'; |
|
265
|
1
|
|
|
|
|
7
|
my $prefix= $self->macro_prefix; |
|
266
|
1
|
|
|
|
|
2
|
my $enum_table= $self->value_table_var; |
|
267
|
|
|
|
|
|
|
# Generate a switch() table to look them up |
|
268
|
1
|
|
|
|
|
4
|
my @code= "switch ($val_variable) {"; |
|
269
|
1
|
|
|
|
|
2
|
my $fmt= "case $prefix%s:%*s return ${enum_table}[%d].name;"; |
|
270
|
1
|
|
|
|
|
3
|
for (0..$#vals) { |
|
271
|
4
|
|
|
|
|
12
|
push @code, sprintf($fmt, $vals[$_][0], $name_width - length($vals[$_][0]), '', $_); |
|
272
|
|
|
|
|
|
|
} |
|
273
|
1
|
|
|
|
|
2
|
push @code, 'default: return NULL;', '}'; |
|
274
|
1
|
|
|
|
|
20
|
return @code; |
|
275
|
|
|
|
|
|
|
} |
|
276
|
|
|
|
|
|
|
|
|
277
|
|
|
|
|
|
|
|
|
278
|
0
|
|
|
0
|
1
|
0
|
sub generate_lookup_by_name($self, %options) { |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
279
|
0
|
|
|
|
|
0
|
return join "\n", $self->_generate_lookup_by_name_switch(\%options); |
|
280
|
|
|
|
|
|
|
} |
|
281
|
|
|
|
|
|
|
|
|
282
|
1
|
|
|
1
|
|
5
|
sub _generate_lookup_by_name_switch($self, $options) { |
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
2
|
|
|
|
1
|
|
|
|
|
2
|
|
|
283
|
1
|
|
|
|
|
2
|
my @vals= $self->values; |
|
284
|
1
|
|
|
|
|
3
|
my $info= $self->_analysis; |
|
285
|
1
|
|
|
|
|
2
|
my $caseless= $options->{caseless}; |
|
286
|
1
|
|
|
|
|
2
|
my $prefixless= $options->{prefixless}; |
|
287
|
1
|
|
|
|
|
3
|
my $prefixlen= length($self->macro_prefix); |
|
288
|
1
|
|
|
|
|
2
|
my $indent= $self->_current_indent; |
|
289
|
1
|
|
50
|
|
|
5
|
my $len_var= $options->{len_var} // 'len'; |
|
290
|
1
|
|
50
|
|
|
3
|
my $str_ptr= $options->{str_ptr} // 'str'; |
|
291
|
1
|
|
|
|
|
2
|
my $enum_table= $self->value_table_var; |
|
292
|
1
|
50
|
|
|
|
4
|
my $strcmp= $caseless? "strcasecmp" : "strcmp"; |
|
293
|
1
|
0
|
|
|
|
3
|
my $idx_type= @vals <= 0x7F? 'int8_t' |
|
|
|
0
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
294
|
|
|
|
|
|
|
: @vals <= 0x7FFF? 'int16_t' |
|
295
|
|
|
|
|
|
|
: @vals <= 0x7FFFFFFF? 'int32_t' |
|
296
|
|
|
|
|
|
|
: 'int64_t'; |
|
297
|
1
|
|
|
|
|
1
|
my @search_set; |
|
298
|
1
|
|
|
|
|
3
|
for (0..$#vals) { |
|
299
|
4
|
|
|
|
|
5
|
push @search_set, [ $self->macro_prefix . $vals[$_][0], $_ ]; |
|
300
|
4
|
50
|
|
|
|
7
|
push @search_set, [ $vals[$_][0], -$_ ] if $prefixless; |
|
301
|
|
|
|
|
|
|
} |
|
302
|
1
|
|
|
|
|
2
|
my %by_len; |
|
303
|
1
|
|
|
|
|
2
|
for (@search_set) { |
|
304
|
4
|
|
|
|
|
5
|
push @{ $by_len{length $_->[0]} }, $_; |
|
|
4
|
|
|
|
|
7
|
|
|
305
|
|
|
|
|
|
|
} |
|
306
|
1
|
|
|
|
|
4
|
my $longest= max(keys %by_len); |
|
307
|
1
|
|
|
|
|
4
|
my @code= ( |
|
308
|
|
|
|
|
|
|
"$idx_type test_el= 0;", |
|
309
|
|
|
|
|
|
|
("char str_buf[$longest+1];")x!!$caseless, |
|
310
|
|
|
|
|
|
|
"switch ($len_var) {", |
|
311
|
|
|
|
|
|
|
); |
|
312
|
|
|
|
|
|
|
# Generate one binary decision tree for each string length |
|
313
|
1
|
|
|
|
|
5
|
for (sort { $a <=> $b } keys %by_len) { |
|
|
1
|
|
|
|
|
4
|
|
|
314
|
2
|
|
|
|
|
3
|
my %pivot_pos; |
|
315
|
2
|
50
|
|
|
|
18
|
my @split_expr= $self->_binary_split($by_len{$_}, $caseless, $caseless? 'str_buf' : $str_ptr, \%pivot_pos); |
|
316
|
|
|
|
|
|
|
push @code, |
|
317
|
|
|
|
|
|
|
"case $_:", |
|
318
|
|
|
|
|
|
|
($caseless? ( |
|
319
|
|
|
|
|
|
|
map "${indent}str_buf[$_]= tolower(${str_ptr}[$_]);", |
|
320
|
2
|
50
|
|
|
|
14
|
sort { $a <=> $b } keys %pivot_pos |
|
|
0
|
|
|
|
|
0
|
|
|
321
|
|
|
|
|
|
|
) : ()), |
|
322
|
|
|
|
|
|
|
(map "$indent$_", @split_expr), |
|
323
|
|
|
|
|
|
|
"${indent}break;", |
|
324
|
|
|
|
|
|
|
} |
|
325
|
1
|
|
|
|
|
3
|
push @code, |
|
326
|
|
|
|
|
|
|
"default:", |
|
327
|
|
|
|
|
|
|
"${indent}return false;", |
|
328
|
|
|
|
|
|
|
"}"; |
|
329
|
|
|
|
|
|
|
# If allowing prefixless match, some test_el will be negative, meaning to |
|
330
|
|
|
|
|
|
|
# test str+prefixlen |
|
331
|
1
|
50
|
|
|
|
2
|
if ($prefixless) { |
|
332
|
0
|
|
|
|
|
0
|
push @code, |
|
333
|
|
|
|
|
|
|
"if (test_el < 0) {", |
|
334
|
|
|
|
|
|
|
"${indent}if ($strcmp($str_ptr, ${enum_table}[-test_el].name + $prefixlen) == 0) {", |
|
335
|
|
|
|
|
|
|
"${indent}${indent}if (value_out) *value_out= ${enum_table}[-test_el].value;", |
|
336
|
|
|
|
|
|
|
"${indent}${indent}return true;", |
|
337
|
|
|
|
|
|
|
"${indent}}", |
|
338
|
|
|
|
|
|
|
"${indent}return false;", |
|
339
|
|
|
|
|
|
|
"}"; |
|
340
|
|
|
|
|
|
|
} |
|
341
|
1
|
|
|
|
|
5
|
push @code, |
|
342
|
|
|
|
|
|
|
"if ($strcmp($str_ptr, ${enum_table}[test_el].name) == 0) {", |
|
343
|
|
|
|
|
|
|
"${indent}if (value_out) *value_out= ${enum_table}[test_el].value;", |
|
344
|
|
|
|
|
|
|
"${indent}return true;", |
|
345
|
|
|
|
|
|
|
"}", |
|
346
|
|
|
|
|
|
|
"return false;"; |
|
347
|
1
|
|
|
|
|
24
|
return @code; |
|
348
|
|
|
|
|
|
|
} |
|
349
|
|
|
|
|
|
|
|
|
350
|
6
|
|
|
6
|
|
6
|
sub _binary_split($self, $vals, $caseless, $str_var, $pivot_pos) { |
|
|
6
|
|
|
|
|
9
|
|
|
|
6
|
|
|
|
|
6
|
|
|
|
6
|
|
|
|
|
5
|
|
|
|
6
|
|
|
|
|
26
|
|
|
|
6
|
|
|
|
|
5
|
|
|
|
6
|
|
|
|
|
4
|
|
|
351
|
|
|
|
|
|
|
# Stop at length 1 |
|
352
|
6
|
100
|
|
|
|
18
|
return qq{test_el= $vals->[0][1];} |
|
353
|
|
|
|
|
|
|
if @$vals == 1; |
|
354
|
|
|
|
|
|
|
# Find a character comparison that splits the list roughly in half. |
|
355
|
2
|
|
|
|
|
3
|
my $goal= .5 * scalar @$vals; |
|
356
|
|
|
|
|
|
|
# Test every possible character and keep track of the best. |
|
357
|
2
|
|
|
|
|
3
|
my ($best_i, $best_ch, $best_less); |
|
358
|
2
|
|
|
|
|
6
|
for (my $i= 0; $i < length $vals->[0][0]; ++$i) { |
|
359
|
14
|
50
|
|
|
|
13
|
if (!$caseless) { |
|
360
|
14
|
|
|
|
|
40
|
for my $ch (uniqstr map substr($_->[0], $i, 1), @$vals) { |
|
361
|
17
|
|
|
|
|
32
|
my @less= grep substr($_->[0], $i, 1) lt $ch, @$vals; |
|
362
|
17
|
100
|
100
|
|
|
54
|
($best_i, $best_ch, $best_less)= ($i, $ch, \@less) |
|
363
|
|
|
|
|
|
|
if !defined $best_i || abs($goal - @less) < abs($goal - @$best_less); |
|
364
|
|
|
|
|
|
|
} |
|
365
|
|
|
|
|
|
|
} else { |
|
366
|
0
|
|
|
|
|
0
|
for my $ch (uniqstr map lc substr($_->[0], $i, 1), @$vals) { |
|
367
|
0
|
|
|
|
|
0
|
my @less= grep +(lc(substr($_->[0], $i, 1)) lt $ch), @$vals; |
|
368
|
0
|
0
|
0
|
|
|
0
|
($best_i, $best_ch, $best_less)= ($i, $ch, \@less) |
|
369
|
|
|
|
|
|
|
if !defined $best_i || abs($goal - @less) < abs($goal - @$best_less); |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
} |
|
372
|
|
|
|
|
|
|
} |
|
373
|
2
|
|
|
|
|
4
|
$pivot_pos->{$best_i}++; # inform caller of which chars were used |
|
374
|
|
|
|
|
|
|
# Binary split the things less than the pivot character |
|
375
|
2
|
|
|
|
|
13
|
my @less_src= $self->_binary_split($best_less, $caseless, $str_var, $pivot_pos); |
|
376
|
|
|
|
|
|
|
# Binary split the things greater-or-equal to the pivot character |
|
377
|
2
|
|
|
|
|
11
|
my %less= map +($_->[0] => 1), @$best_less; |
|
378
|
2
|
|
|
|
|
7
|
my @ge_src= $self->_binary_split([ grep !$less{$_->[0]}, @$vals ], $caseless, $str_var, $pivot_pos); |
|
379
|
2
|
|
|
|
|
4
|
my $indent= $self->_current_indent; |
|
380
|
|
|
|
|
|
|
return ( |
|
381
|
2
|
100
|
|
|
|
16
|
"if (${str_var}[$best_i] < '$best_ch') {", |
|
382
|
|
|
|
|
|
|
(map $indent.$_, @less_src), |
|
383
|
|
|
|
|
|
|
(@ge_src > 1 |
|
384
|
|
|
|
|
|
|
# combine "else { if" |
|
385
|
|
|
|
|
|
|
? ( '} else '.$ge_src[0], @ge_src[1..$#ge_src] ) |
|
386
|
|
|
|
|
|
|
# else { statement } |
|
387
|
|
|
|
|
|
|
: ( '} else {', (map $indent.$_, @ge_src), '}' ) |
|
388
|
|
|
|
|
|
|
) |
|
389
|
|
|
|
|
|
|
); |
|
390
|
|
|
|
|
|
|
} |
|
391
|
|
|
|
|
|
|
|
|
392
|
|
|
|
|
|
|
1; |
|
393
|
|
|
|
|
|
|
|
|
394
|
|
|
|
|
|
|
__END__ |