| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Object::PadX::Enum 0.02; |
|
2
|
|
|
|
|
|
|
|
|
3
|
14
|
|
|
14
|
|
1773134
|
use v5.22; |
|
|
14
|
|
|
|
|
81
|
|
|
4
|
8
|
|
|
8
|
|
57
|
use warnings; |
|
|
8
|
|
|
|
|
17
|
|
|
|
8
|
|
|
|
|
509
|
|
|
5
|
|
|
|
|
|
|
|
|
6
|
8
|
|
|
8
|
|
50
|
use Carp; |
|
|
8
|
|
|
|
|
18
|
|
|
|
8
|
|
|
|
|
651
|
|
|
7
|
8
|
|
|
8
|
|
5457
|
use Object::Pad 0.825 (); |
|
|
8
|
|
|
|
|
106752
|
|
|
|
8
|
|
|
|
|
1159
|
|
|
8
|
8
|
|
|
8
|
|
68
|
use Object::Pad::MOP::Class qw( :experimental(mop) ); |
|
|
8
|
|
|
|
|
17
|
|
|
|
8
|
|
|
|
|
59
|
|
|
9
|
|
|
|
|
|
|
|
|
10
|
|
|
|
|
|
|
# Loaded for its XS keyword registrations |
|
11
|
|
|
|
|
|
|
require XSLoader; |
|
12
|
|
|
|
|
|
|
XSLoader::load( __PACKAGE__, our $VERSION ); |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
=encoding UTF-8 |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=for highlighter language=perl |
|
17
|
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
=head1 NAME |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
C - syntactic sugar for enum-like singleton-bearing C classes |
|
21
|
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
=head1 SYNOPSIS |
|
23
|
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
use Object::PadX::Enum; |
|
25
|
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
enum Raptor { |
|
27
|
|
|
|
|
|
|
item VELOCIRAPTOR ( max_speed_kmh => 60, max_weight_kg => 15, max_height_cm => 50 ); |
|
28
|
|
|
|
|
|
|
item DEINONYCHUS ( max_speed_kmh => 50, max_weight_kg => 80, max_height_cm => 87 ); |
|
29
|
|
|
|
|
|
|
item UTAHRAPTOR ( max_speed_kmh => 35, max_weight_kg => 500, max_height_cm => 150 ); |
|
30
|
|
|
|
|
|
|
item MICRORAPTOR ( max_speed_kmh => 40, max_weight_kg => 1, max_height_cm => 30 ); |
|
31
|
|
|
|
|
|
|
item DROMAEOSAURUS ( max_speed_kmh => 60, max_weight_kg => 15, max_height_cm => 50 ); |
|
32
|
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
field $max_speed_kmh :param :reader; |
|
34
|
|
|
|
|
|
|
field $max_weight_kg :param :reader; |
|
35
|
|
|
|
|
|
|
field $max_height_cm :param :reader; |
|
36
|
|
|
|
|
|
|
|
|
37
|
|
|
|
|
|
|
method speed_per_kg { return $max_speed_kmh / $max_weight_kg } |
|
38
|
|
|
|
|
|
|
method speed_per_cm { return $max_speed_kmh / $max_height_cm } |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
method fastest :common { |
|
41
|
|
|
|
|
|
|
my ( $top ) = sort { $b->max_speed_kmh <=> $a->max_speed_kmh } $class->values; |
|
42
|
|
|
|
|
|
|
return $top; |
|
43
|
|
|
|
|
|
|
} |
|
44
|
|
|
|
|
|
|
} |
|
45
|
|
|
|
|
|
|
|
|
46
|
|
|
|
|
|
|
say Raptor->VELOCIRAPTOR->max_speed_kmh; # 60 |
|
47
|
|
|
|
|
|
|
say Raptor->DEINONYCHUS->speed_per_kg; # 0.625 |
|
48
|
|
|
|
|
|
|
say Raptor->from_ordinal(2)->name; # UTAHRAPTOR |
|
49
|
|
|
|
|
|
|
say Raptor->from_name("MICRORAPTOR")->speed_per_cm; # 1.33333333333333 |
|
50
|
|
|
|
|
|
|
say 'Fastest in absolute terms: ', Raptor->fastest->name; # VELOCIRAPTOR or DROMAEOSAURUS (tie) |
|
51
|
|
|
|
|
|
|
|
|
52
|
|
|
|
|
|
|
=head1 DESCRIPTION |
|
53
|
|
|
|
|
|
|
|
|
54
|
|
|
|
|
|
|
C adds two keywords on top of L: |
|
55
|
|
|
|
|
|
|
|
|
56
|
|
|
|
|
|
|
=over 4 |
|
57
|
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
=item * C |
|
59
|
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
Declares a class (using L's C machinery) and auto-injects |
|
61
|
|
|
|
|
|
|
C<$ordinal :reader> and C fields. The C reader returns the |
|
62
|
|
|
|
|
|
|
identifier under which the singleton was declared (e.g. C<"RED">). Inside the |
|
63
|
|
|
|
|
|
|
block, all normal C constructs (C, C, C, |
|
64
|
|
|
|
|
|
|
...) are available, plus the C- keyword.
|
|
65
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
The following class-level attributes are accepted: |
|
67
|
|
|
|
|
|
|
|
|
68
|
|
|
|
|
|
|
=over 4 |
|
69
|
|
|
|
|
|
|
|
|
70
|
|
|
|
|
|
|
=item C<:isa(CLASS)>, C<:isa(CLASS VERSION)> |
|
71
|
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
=item C<:extends(CLASS)>, C<:extends(CLASS VERSION)> |
|
73
|
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
Declares a superclass; equivalent to L's C<:isa>. The package is |
|
75
|
|
|
|
|
|
|
loaded automatically. If a VERSION is given, C<< CLASS->VERSION(VERSION) >> is |
|
76
|
|
|
|
|
|
|
called to enforce it. |
|
77
|
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
An C may inherit from another C. Fields, methods, roles and |
|
79
|
|
|
|
|
|
|
C phasers from the parent are inherited normally. The parent's |
|
80
|
|
|
|
|
|
|
B are I inherited: the child has its own ordinal-zero-based item |
|
81
|
|
|
|
|
|
|
sequence, and accessing a parent item name on the child raises an error. The |
|
82
|
|
|
|
|
|
|
child's C, C and C see only the child's |
|
83
|
|
|
|
|
|
|
items. A parent enum must be finalized (i.e. its declaration must have |
|
84
|
|
|
|
|
|
|
already executed at runtime) before a child enum that inherits from it; in |
|
85
|
|
|
|
|
|
|
practice this is satisfied by normal source ordering and C |
|
86
|
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
=item C<:does(ROLE)>, C<:does(ROLE VERSION)> |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
Composes a role into the enum class. May be repeated for multiple roles. The |
|
90
|
|
|
|
|
|
|
role package is loaded automatically. |
|
91
|
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
=back |
|
93
|
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
The class attributes C<:abstract>, C<:strict>, C<:repr> and C<:lexical_new> |
|
95
|
|
|
|
|
|
|
are not supported. C<:abstract> is semantically incompatible with C-
|
|
96
|
|
|
|
|
|
|
(singletons cannot be constructed for an abstract class); the others have no |
|
97
|
|
|
|
|
|
|
public L entry point and would require reaching into |
|
98
|
|
|
|
|
|
|
private Object::Pad internals. |
|
99
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
=item * C-
|
|
101
|
|
|
|
|
|
|
|
|
102
|
|
|
|
|
|
|
Declares a named singleton instance of the enclosing C. C is the |
|
103
|
|
|
|
|
|
|
key/value list passed to the auto-generated constructor; the parentheses (and |
|
104
|
|
|
|
|
|
|
the arg list) are optional, so C- is equivalent to C
- .
|
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
=back |
|
107
|
|
|
|
|
|
|
|
|
108
|
|
|
|
|
|
|
After the C block closes, the following class-level methods are |
|
109
|
|
|
|
|
|
|
installed on the enum class for each declared singleton C: |
|
110
|
|
|
|
|
|
|
|
|
111
|
|
|
|
|
|
|
$singleton = ClassName->NAME; # the named singleton |
|
112
|
|
|
|
|
|
|
@all = ClassName->values; # all singletons in declaration order |
|
113
|
|
|
|
|
|
|
$byord = ClassName->from_ordinal(0); |
|
114
|
|
|
|
|
|
|
$byname = ClassName->from_name("RED"); |
|
115
|
|
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
Direct construction via C<< ClassName->new(...) >> is blocked after the |
|
117
|
|
|
|
|
|
|
C block closes; the only ways to obtain a singleton are the per-item |
|
118
|
|
|
|
|
|
|
accessor, C, and C. Subclasses (whether plain |
|
119
|
|
|
|
|
|
|
C or another C) may still call C on themselves; the block |
|
120
|
|
|
|
|
|
|
applies only to direct invocation on the enum class itself. |
|
121
|
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
=head1 CAVEATS |
|
123
|
|
|
|
|
|
|
|
|
124
|
|
|
|
|
|
|
=over 4 |
|
125
|
|
|
|
|
|
|
|
|
126
|
|
|
|
|
|
|
=item * |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
User Cs require explicit C<:param> if you intend to set them via |
|
129
|
|
|
|
|
|
|
C- args. C does I inject C<:param> automatically.
|
|
130
|
|
|
|
|
|
|
|
|
131
|
|
|
|
|
|
|
=item * |
|
132
|
|
|
|
|
|
|
|
|
133
|
|
|
|
|
|
|
Singletons are constructed at the runtime of the compilation unit that |
|
134
|
|
|
|
|
|
|
contains the C declaration, after that unit's C phase. They |
|
135
|
|
|
|
|
|
|
are therefore not visible from earlier C/C blocks of the |
|
136
|
|
|
|
|
|
|
same unit. Normal runtime code (including code inside C and |
|
137
|
|
|
|
|
|
|
C blocks executed during main runtime) sees them as expected. |
|
138
|
|
|
|
|
|
|
|
|
139
|
|
|
|
|
|
|
=item * |
|
140
|
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
C-level C<:abstract>, C<:strict>, C<:repr> and C<:lexical_new> are not |
|
142
|
|
|
|
|
|
|
supported. See the description of the C keyword above for the rationale; |
|
143
|
|
|
|
|
|
|
C<:isa> and C<:does> I supported. |
|
144
|
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
=item * |
|
146
|
|
|
|
|
|
|
|
|
147
|
|
|
|
|
|
|
The names C, C, C, C and C are |
|
148
|
|
|
|
|
|
|
reserved and must not be used as C- names.
|
|
149
|
|
|
|
|
|
|
|
|
150
|
|
|
|
|
|
|
=back |
|
151
|
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
=cut |
|
153
|
|
|
|
|
|
|
|
|
154
|
|
|
|
|
|
|
# Per-class state captured during compilation. |
|
155
|
|
|
|
|
|
|
# $Pending{$class} = { meta => $meta, items => [ [ $name, \@args, $line ], ... ], seen => { $name => 1 } } |
|
156
|
|
|
|
|
|
|
my %Pending; |
|
157
|
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
# Permanent per-class registry of finalized enum item names, in declaration |
|
159
|
|
|
|
|
|
|
# order. Populated by `_finalize_enum`. Queried by descendant enum finalizes |
|
160
|
|
|
|
|
|
|
# (to shadow inherited item accessors) and could be useful for introspection |
|
161
|
|
|
|
|
|
|
# in the future. Keys are class names; values are arrayrefs of item names. |
|
162
|
|
|
|
|
|
|
my %EnumItems; |
|
163
|
|
|
|
|
|
|
|
|
164
|
|
|
|
|
|
|
my %RESERVED_ITEM_NAMES = map { $_ => 1 } qw( |
|
165
|
|
|
|
|
|
|
values from_ordinal from_name ordinal name |
|
166
|
|
|
|
|
|
|
new BUILD DOES META |
|
167
|
|
|
|
|
|
|
); |
|
168
|
|
|
|
|
|
|
|
|
169
|
|
|
|
|
|
|
sub import { |
|
170
|
20
|
|
|
20
|
|
555079
|
my $class = shift; |
|
171
|
20
|
|
|
|
|
60
|
my $caller = caller; |
|
172
|
|
|
|
|
|
|
|
|
173
|
20
|
|
|
|
|
88
|
$^H{ 'Object::PadX::Enum/enum' } = 1; |
|
174
|
20
|
|
|
|
|
67
|
$^H{ 'Object::PadX::Enum/item' } = 1; |
|
175
|
|
|
|
|
|
|
|
|
176
|
20
|
|
|
|
|
98
|
Object::Pad->import_into( $caller ); |
|
177
|
|
|
|
|
|
|
} |
|
178
|
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
# Attributes that have a documented public-MOP entry point. |
|
180
|
|
|
|
|
|
|
my %ENUM_ATTR_HANDLERS = ( |
|
181
|
|
|
|
|
|
|
isa => \&_attr_isa, |
|
182
|
|
|
|
|
|
|
extends => \&_attr_isa, |
|
183
|
|
|
|
|
|
|
does => \&_attr_does, |
|
184
|
|
|
|
|
|
|
); |
|
185
|
|
|
|
|
|
|
|
|
186
|
|
|
|
|
|
|
# Attributes that exist on Object::Pad's `class` keyword but are deliberately |
|
187
|
|
|
|
|
|
|
# rejected here. The message explains why so users aren't left guessing. |
|
188
|
|
|
|
|
|
|
my %ENUM_ATTR_REJECTED = ( |
|
189
|
|
|
|
|
|
|
abstract => "':abstract' is incompatible with enum: singleton values cannot be constructed for an abstract class", |
|
190
|
|
|
|
|
|
|
strict => "':strict' is not supported on enum (no public Object::Pad MOP entry point); declare a plain 'class' instead", |
|
191
|
|
|
|
|
|
|
repr => "':repr' is not supported on enum (no public Object::Pad MOP entry point); declare a plain 'class' instead", |
|
192
|
|
|
|
|
|
|
lexical_new => "':lexical_new' is not supported on enum (no public Object::Pad MOP entry point); declare a plain 'class' instead", |
|
193
|
|
|
|
|
|
|
); |
|
194
|
|
|
|
|
|
|
|
|
195
|
|
|
|
|
|
|
# Load $pkg via `require`, mirroring Object::Pad's :isa/:does autoload. Returns |
|
196
|
|
|
|
|
|
|
# silently on success; croaks on failure. |
|
197
|
|
|
|
|
|
|
sub _require_package { |
|
198
|
14
|
|
|
14
|
|
26
|
my ( $pkg, $for ) = @_; |
|
199
|
|
|
|
|
|
|
|
|
200
|
|
|
|
|
|
|
# Skip require for packages already defined inline (no .pm needed). |
|
201
|
8
|
|
|
8
|
|
4155
|
no strict 'refs'; |
|
|
8
|
|
|
|
|
17
|
|
|
|
8
|
|
|
|
|
19414
|
|
|
202
|
14
|
100
|
|
|
|
20
|
keys %{ "${pkg}::" } and return; |
|
|
14
|
|
|
|
|
75
|
|
|
203
|
|
|
|
|
|
|
|
|
204
|
1
|
|
|
|
|
8
|
( my $file = "$pkg.pm" ) =~ s{::}{/}g; |
|
205
|
1
|
50
|
|
|
|
3
|
eval { require $file; 1 } |
|
|
1
|
|
|
|
|
465
|
|
|
|
0
|
|
|
|
|
0
|
|
|
206
|
|
|
|
|
|
|
or croak "Failed to load package '$pkg' for $for: $@"; |
|
207
|
|
|
|
|
|
|
|
|
208
|
0
|
|
|
|
|
0
|
return; |
|
209
|
|
|
|
|
|
|
} |
|
210
|
|
|
|
|
|
|
|
|
211
|
|
|
|
|
|
|
# Parse "Pkg" or "Pkg VER" into ($pkg, $ver). $ver is undef when absent. |
|
212
|
|
|
|
|
|
|
sub _split_versioned_pkg { |
|
213
|
14
|
|
|
14
|
|
30
|
my ( $raw, $attr_name ) = @_; |
|
214
|
|
|
|
|
|
|
|
|
215
|
14
|
50
|
33
|
|
|
71
|
defined $raw && length $raw |
|
216
|
|
|
|
|
|
|
or croak "Attribute ':$attr_name' requires a value"; |
|
217
|
|
|
|
|
|
|
|
|
218
|
14
|
|
|
|
|
50
|
my ( $pkg, $ver, $extra ) = split /\s+/, $raw, 3; |
|
219
|
14
|
50
|
|
|
|
34
|
defined $extra |
|
220
|
|
|
|
|
|
|
and croak "Attribute ':$attr_name($raw)' has too many parts; expected 'PACKAGE' or 'PACKAGE VERSION'"; |
|
221
|
|
|
|
|
|
|
|
|
222
|
14
|
|
|
|
|
45
|
return ( $pkg, $ver ); |
|
223
|
|
|
|
|
|
|
} |
|
224
|
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub _attr_isa { |
|
226
|
11
|
|
|
11
|
|
23
|
my ( $state, $value ) = @_; |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
exists $state->{ isa } |
|
229
|
11
|
100
|
|
|
|
181
|
and croak "Multiple ':isa' / ':extends' attributes on enum '$state->{name}'"; |
|
230
|
|
|
|
|
|
|
|
|
231
|
10
|
|
|
|
|
28
|
my ( $pkg, $ver ) = _split_versioned_pkg( $value, 'isa' ); |
|
232
|
10
|
|
|
|
|
44
|
_require_package( $pkg, "':isa($pkg)' on enum '$state->{name}'" ); |
|
233
|
9
|
100
|
|
|
|
77
|
defined $ver and $pkg->VERSION( $ver ); |
|
234
|
|
|
|
|
|
|
|
|
235
|
8
|
|
|
|
|
19
|
$state->{ isa } = $pkg; |
|
236
|
8
|
|
|
|
|
22
|
return; |
|
237
|
|
|
|
|
|
|
} |
|
238
|
|
|
|
|
|
|
|
|
239
|
|
|
|
|
|
|
sub _attr_does { |
|
240
|
4
|
|
|
4
|
|
9
|
my ( $state, $value ) = @_; |
|
241
|
|
|
|
|
|
|
|
|
242
|
4
|
|
|
|
|
9
|
my ( $pkg, $ver ) = _split_versioned_pkg( $value, 'does' ); |
|
243
|
4
|
|
|
|
|
16
|
_require_package( $pkg, "':does($pkg)' on enum '$state->{name}'" ); |
|
244
|
4
|
50
|
|
|
|
8
|
defined $ver and $pkg->VERSION( $ver ); |
|
245
|
|
|
|
|
|
|
|
|
246
|
4
|
|
|
|
|
6
|
push @{ $state->{ roles } }, $pkg; |
|
|
4
|
|
|
|
|
10
|
|
|
247
|
4
|
|
|
|
|
10
|
return; |
|
248
|
|
|
|
|
|
|
} |
|
249
|
|
|
|
|
|
|
|
|
250
|
|
|
|
|
|
|
# Called by XS at compile-time when `enum NAME ATTRS? {` is encountered. |
|
251
|
|
|
|
|
|
|
sub _begin_enum { |
|
252
|
28
|
|
|
28
|
|
3941
|
my ( $name, $attrs ) = @_; |
|
253
|
|
|
|
|
|
|
|
|
254
|
28
|
50
|
|
|
|
119
|
exists $Pending{ $name } |
|
255
|
|
|
|
|
|
|
and croak "Cannot declare enum '$name'; already being defined"; |
|
256
|
|
|
|
|
|
|
|
|
257
|
28
|
|
|
|
|
130
|
my $state = { name => $name, roles => [] }; |
|
258
|
28
|
|
50
|
|
|
72
|
for my $pair ( @{ $attrs // [] } ) { |
|
|
28
|
|
|
|
|
133
|
|
|
259
|
19
|
|
|
|
|
52
|
my ( $attr, $value ) = @$pair; |
|
260
|
|
|
|
|
|
|
|
|
261
|
19
|
100
|
|
|
|
62
|
if ( my $msg = $ENUM_ATTR_REJECTED{ $attr } ) { |
|
262
|
3
|
|
|
|
|
510
|
croak "$msg (enum '$name')"; |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
|
|
265
|
16
|
100
|
|
|
|
211
|
my $handler = $ENUM_ATTR_HANDLERS{ $attr } |
|
266
|
|
|
|
|
|
|
or croak "Unrecognised attribute ':$attr' on enum '$name'"; |
|
267
|
|
|
|
|
|
|
|
|
268
|
15
|
|
|
|
|
35
|
$handler->( $state, $value ); |
|
269
|
|
|
|
|
|
|
} |
|
270
|
|
|
|
|
|
|
|
|
271
|
21
|
|
|
|
|
64
|
my @begin_args = ( $name ); |
|
272
|
|
|
|
|
|
|
exists $state->{ isa } |
|
273
|
21
|
100
|
|
|
|
83
|
and push @begin_args, ( isa => $state->{ isa } ); |
|
274
|
|
|
|
|
|
|
|
|
275
|
21
|
|
|
|
|
91
|
my $meta = Object::Pad::MOP::Class->begin_class( @begin_args ); |
|
276
|
|
|
|
|
|
|
|
|
277
|
21
|
|
|
|
|
1613
|
$meta->add_role( $_ ) for @{ $state->{ roles } }; |
|
|
21
|
|
|
|
|
104
|
|
|
278
|
|
|
|
|
|
|
|
|
279
|
|
|
|
|
|
|
# $ordinal and $_name are reader-only (not :param) so user item args cannot |
|
280
|
|
|
|
|
|
|
# override them; both are stamped after construction in _finalize_enum. |
|
281
|
21
|
|
|
14
|
|
1191
|
$meta->add_field( '$ordinal', reader => 'ordinal' ); |
|
|
14
|
|
|
|
|
64
|
|
|
|
14
|
|
|
|
|
84
|
|
|
282
|
21
|
|
|
8
|
|
905
|
$meta->add_field( '$_name', reader => 'name' ); |
|
|
8
|
|
|
|
|
96
|
|
|
|
8
|
|
|
|
|
78
|
|
|
283
|
|
|
|
|
|
|
|
|
284
|
21
|
|
|
|
|
126
|
$Pending{ $name } = { meta => $meta, items => [], seen => {} }; |
|
285
|
|
|
|
|
|
|
|
|
286
|
21
|
|
|
|
|
22597
|
return; |
|
287
|
|
|
|
|
|
|
} |
|
288
|
|
|
|
|
|
|
|
|
289
|
|
|
|
|
|
|
# Called at runtime, in source order, for each `item NAME(args)` statement. |
|
290
|
|
|
|
|
|
|
sub _register_item { |
|
291
|
34
|
|
|
34
|
|
1509789
|
my ( $class, $name, $line, @args ) = @_; |
|
292
|
|
|
|
|
|
|
|
|
293
|
34
|
50
|
|
|
|
151
|
my $entry = $Pending{ $class } |
|
294
|
|
|
|
|
|
|
or croak "Internal error: item '$name' for unknown enum '$class' at line $line"; |
|
295
|
|
|
|
|
|
|
|
|
296
|
33
|
100
|
|
|
|
368
|
$entry->{ seen }{ $name } |
|
297
|
|
|
|
|
|
|
and croak "Duplicate item '$name' in enum '$class' at line $line"; |
|
298
|
|
|
|
|
|
|
|
|
299
|
32
|
100
|
|
|
|
450
|
$RESERVED_ITEM_NAMES{ $name } |
|
300
|
|
|
|
|
|
|
and croak "item name '$name' is reserved in enum '$class' at line $line"; |
|
301
|
|
|
|
|
|
|
|
|
302
|
33
|
|
|
|
|
11543
|
push @{ $entry->{ items } }, [ $name, \@args, $line ]; |
|
|
33
|
|
|
|
|
129
|
|
|
303
|
30
|
|
|
|
|
101
|
$entry->{ seen }{ $name } = 1; |
|
304
|
|
|
|
|
|
|
|
|
305
|
30
|
|
|
|
|
102
|
return; |
|
306
|
|
|
|
|
|
|
} |
|
307
|
|
|
|
|
|
|
|
|
308
|
|
|
|
|
|
|
# Called at runtime, once, after all item statements for the enum have run. |
|
309
|
|
|
|
|
|
|
sub _finalize_enum { |
|
310
|
18
|
|
|
18
|
|
90
|
my ( $class ) = @_; |
|
311
|
|
|
|
|
|
|
|
|
312
|
18
|
50
|
|
|
|
78
|
my $entry = delete $Pending{ $class } |
|
313
|
|
|
|
|
|
|
or croak "Internal error: _finalize_enum on unknown enum '$class'"; |
|
314
|
|
|
|
|
|
|
|
|
315
|
18
|
|
|
|
|
64
|
my $meta = $entry->{ meta }; |
|
316
|
18
|
|
|
|
|
261
|
my $ord_field = $meta->get_field( '$ordinal' ); |
|
317
|
18
|
|
|
|
|
74
|
my $name_field = $meta->get_field( '$_name' ); |
|
318
|
18
|
|
|
|
|
39
|
my @ordered; |
|
319
|
|
|
|
|
|
|
|
|
320
|
18
|
|
|
|
|
38
|
my $n = 0; |
|
321
|
18
|
|
|
|
|
46
|
for my $item ( @{ $entry->{ items } } ) { |
|
|
18
|
|
|
|
|
60
|
|
|
322
|
29
|
|
|
|
|
76
|
my ( $name, $args, $line ) = @$item; |
|
323
|
|
|
|
|
|
|
|
|
324
|
29
|
|
|
|
|
50
|
my $instance = eval { $class->new( @$args ) }; |
|
|
29
|
|
|
|
|
299
|
|
|
325
|
29
|
50
|
|
|
|
310
|
$@ and croak "Failed to construct enum value '$name' of '$class' at line $line: $@"; |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
# Stamp the ordinal and name after construction so they aren't user-facing :params. |
|
328
|
29
|
|
|
|
|
107
|
$ord_field->value( $instance ) = $n; |
|
329
|
29
|
|
|
|
|
87
|
$name_field->value( $instance ) = $name; |
|
330
|
|
|
|
|
|
|
|
|
331
|
29
|
|
|
|
|
74
|
push @ordered, [ $name, $instance ]; |
|
332
|
29
|
|
|
|
|
68
|
$n++; |
|
333
|
|
|
|
|
|
|
} |
|
334
|
|
|
|
|
|
|
|
|
335
|
8
|
|
|
8
|
|
74
|
no strict 'refs'; |
|
|
8
|
|
|
|
|
40
|
|
|
|
8
|
|
|
|
|
475
|
|
|
336
|
8
|
|
|
8
|
|
64
|
no warnings 'redefine'; |
|
|
8
|
|
|
|
|
20
|
|
|
|
8
|
|
|
|
|
11690
|
|
|
337
|
|
|
|
|
|
|
|
|
338
|
18
|
|
|
|
|
70
|
my %own_names; |
|
339
|
18
|
|
|
|
|
45
|
for my $pair ( @ordered ) { |
|
340
|
29
|
|
|
|
|
69
|
my ( $name, $instance ) = @$pair; |
|
341
|
29
|
|
|
|
|
79
|
$own_names{ $name } = 1; |
|
342
|
29
|
|
|
51
|
|
160
|
*{ "${class}::${name}" } = sub { $instance }; |
|
|
29
|
|
|
|
|
217
|
|
|
|
48
|
|
|
|
|
9730
|
|
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
|
|
345
|
18
|
|
|
|
|
125
|
*{ "${class}::values" } = sub { |
|
346
|
5
|
|
|
5
|
|
35
|
return map { $_->[1] } @ordered; |
|
|
11
|
|
|
|
|
28
|
|
|
347
|
18
|
|
|
|
|
74
|
}; |
|
348
|
|
|
|
|
|
|
|
|
349
|
18
|
|
|
|
|
93
|
*{ "${class}::from_ordinal" } = sub { |
|
350
|
6
|
|
|
6
|
|
23
|
my ( undef, $idx ) = @_; |
|
351
|
6
|
50
|
|
|
|
27
|
defined $idx or return undef; |
|
352
|
6
|
100
|
100
|
|
|
53
|
$idx >= 0 && $idx < @ordered or return undef; |
|
353
|
4
|
|
|
|
|
23
|
return $ordered[ $idx ][ 1 ]; |
|
354
|
18
|
|
|
|
|
124
|
}; |
|
355
|
|
|
|
|
|
|
|
|
356
|
18
|
|
|
|
|
140
|
*{ "${class}::from_name" } = sub { |
|
357
|
6
|
|
|
6
|
|
1424
|
my ( undef, $want ) = @_; |
|
358
|
6
|
50
|
|
|
|
24
|
defined $want or return undef; |
|
359
|
6
|
|
|
|
|
44
|
for my $pair ( @ordered ) { |
|
360
|
13
|
100
|
|
|
|
78
|
return $pair->[1] if $pair->[0] eq $want; |
|
361
|
|
|
|
|
|
|
} |
|
362
|
2
|
|
|
|
|
11
|
return undef; |
|
363
|
18
|
|
|
|
|
158
|
}; |
|
364
|
|
|
|
|
|
|
|
|
365
|
|
|
|
|
|
|
# Shadow ancestor enum items not redefined locally. A child enum inherits |
|
366
|
|
|
|
|
|
|
# fields/methods from a parent enum but loses the parent's items: accessing |
|
367
|
|
|
|
|
|
|
# a parent item name on the child raises a clear error rather than |
|
368
|
|
|
|
|
|
|
# returning the parent's singleton via MRO. |
|
369
|
18
|
|
|
|
|
141
|
require mro; |
|
370
|
18
|
|
|
|
|
95
|
my $linear = mro::get_linear_isa( $class ); |
|
371
|
18
|
|
|
|
|
36
|
my %shadowed; |
|
372
|
18
|
|
|
|
|
47
|
for my $ancestor ( @$linear ) { |
|
373
|
44
|
100
|
|
|
|
146
|
next if $ancestor eq $class; |
|
374
|
26
|
100
|
|
|
|
97
|
my $ancestor_items = $EnumItems{ $ancestor } or next; |
|
375
|
4
|
|
|
|
|
9
|
for my $aname ( @$ancestor_items ) { |
|
376
|
8
|
100
|
|
|
|
53
|
next if $own_names{ $aname }; |
|
377
|
7
|
50
|
|
|
|
16
|
next if $shadowed{ $aname }; |
|
378
|
7
|
|
|
|
|
18
|
$shadowed{ $aname } = $ancestor; |
|
379
|
7
|
|
|
|
|
19
|
my $msg = "'$aname' is not an item of '$class' (inherited from '$ancestor', shadowed)"; |
|
380
|
7
|
|
|
3
|
|
27
|
*{ "${class}::${aname}" } = sub { croak $msg }; |
|
|
7
|
|
|
|
|
65
|
|
|
|
3
|
|
|
|
|
2559
|
|
|
381
|
|
|
|
|
|
|
} |
|
382
|
|
|
|
|
|
|
} |
|
383
|
|
|
|
|
|
|
|
|
384
|
|
|
|
|
|
|
# Register before installing the `new` override so any descendant enum |
|
385
|
|
|
|
|
|
|
# whose finalize runs later (and which calls our `new` via MRO) sees us in |
|
386
|
|
|
|
|
|
|
# the registry. |
|
387
|
18
|
|
|
|
|
51
|
$EnumItems{ $class } = [ map { $_->[0] } @ordered ]; |
|
|
29
|
|
|
|
|
100
|
|
|
388
|
|
|
|
|
|
|
|
|
389
|
|
|
|
|
|
|
# Block external construction. Capture the original Object::Pad-generated |
|
390
|
|
|
|
|
|
|
# `new` so subclass enums (and plain subclasses) can pass through during |
|
391
|
|
|
|
|
|
|
# their own construction; only direct calls on the enum class itself are |
|
392
|
|
|
|
|
|
|
# rejected. |
|
393
|
18
|
|
|
|
|
43
|
my @item_names = map { $_->[0] } @ordered; |
|
|
29
|
|
|
|
|
69
|
|
|
394
|
18
|
|
|
|
|
34
|
my $orig_new = \&{ "${class}::new" }; |
|
|
18
|
|
|
|
|
92
|
|
|
395
|
|
|
|
|
|
|
|
|
396
|
18
|
|
|
|
|
48
|
my $new_msg = "Cannot construct new instances of enum class '$class' directly"; |
|
397
|
18
|
100
|
|
|
|
62
|
if ( @item_names ) { |
|
398
|
16
|
|
|
|
|
68
|
$new_msg .= '; use one of: ' . join( ', ', @item_names ); |
|
399
|
16
|
|
|
|
|
43
|
$new_msg .= " (or ${class}->from_name / ${class}->from_ordinal)"; |
|
400
|
|
|
|
|
|
|
} |
|
401
|
|
|
|
|
|
|
|
|
402
|
18
|
|
|
|
|
116
|
*{ "${class}::new" } = sub { |
|
403
|
5
|
|
|
5
|
|
18642
|
my $invocant = shift; |
|
404
|
5
|
50
|
|
|
|
29
|
$invocant ne $class |
|
405
|
|
|
|
|
|
|
and return $invocant->$orig_new( @_ ); |
|
406
|
5
|
|
|
|
|
835
|
croak $new_msg; |
|
407
|
18
|
|
|
|
|
127
|
}; |
|
408
|
|
|
|
|
|
|
|
|
409
|
18
|
|
|
|
|
229
|
return; |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
0x55AA; |