| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
package Aion::Type; |
|
2
|
|
|
|
|
|
|
# Базовый класс для типов и преобразователей |
|
3
|
7
|
|
|
7
|
|
129168
|
use common::sense; |
|
|
7
|
|
|
|
|
10
|
|
|
|
7
|
|
|
|
|
32
|
|
|
4
|
7
|
|
|
7
|
|
462
|
use warnings FATAL => 'recursion'; |
|
|
7
|
|
|
|
|
9
|
|
|
|
7
|
|
|
|
|
337
|
|
|
5
|
|
|
|
|
|
|
#use warnings 'recursion'; |
|
6
|
|
|
|
|
|
|
|
|
7
|
7
|
|
|
7
|
|
358
|
use Aion::Meta::Util qw//; |
|
|
7
|
|
|
|
|
31
|
|
|
|
7
|
|
|
|
|
169
|
|
|
8
|
7
|
|
|
7
|
|
2106
|
use Aion::Type::Lim; |
|
|
7
|
|
|
|
|
18
|
|
|
|
7
|
|
|
|
|
200
|
|
|
9
|
7
|
|
|
7
|
|
33
|
use List::Util qw//; |
|
|
7
|
|
|
|
|
7
|
|
|
|
7
|
|
|
|
|
77
|
|
|
10
|
7
|
|
|
7
|
|
17
|
use Scalar::Util qw//; |
|
|
7
|
|
|
|
|
8
|
|
|
|
7
|
|
|
|
|
1720
|
|
|
11
|
|
|
|
|
|
|
|
|
12
|
207
|
|
|
207
|
1
|
538
|
sub true {1} |
|
13
|
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
use overload |
|
15
|
|
|
|
|
|
|
"fallback" => 1, |
|
16
|
|
|
|
|
|
|
"&{}" => sub { |
|
17
|
2
|
|
|
2
|
|
454
|
my ($self) = @_; |
|
18
|
2
|
|
|
2
|
|
7
|
sub { $self->test } |
|
19
|
2
|
|
|
|
|
13
|
}, |
|
20
|
|
|
|
|
|
|
'""' => "stringify", |
|
21
|
45
|
|
|
45
|
|
4020
|
"|" => sub { Aion::Types::Union([@_[0, 1]]) }, |
|
22
|
99
|
|
|
99
|
|
2295
|
"&" => sub { Aion::Types::Intersection([@_[0, 1]]) }, |
|
23
|
466
|
|
|
466
|
|
7782
|
"~" => sub { Aion::Types::Exclude([shift]) }, |
|
24
|
|
|
|
|
|
|
"~~" => "include", |
|
25
|
|
|
|
|
|
|
">>" => "coerce", |
|
26
|
|
|
|
|
|
|
"eq" => "identical", |
|
27
|
|
|
|
|
|
|
"ne" => "distinct", |
|
28
|
0
|
|
|
0
|
|
0
|
"lt" => sub {die "lt do'nt used!"}, |
|
29
|
0
|
|
|
0
|
|
0
|
"gt" => sub {die "gt do'nt used!"}, |
|
30
|
0
|
|
|
0
|
|
0
|
"le" => sub {die "le do'nt used!"}, |
|
31
|
0
|
|
|
0
|
|
0
|
"ge" => sub {die "ge do'nt used!"}, |
|
32
|
7
|
|
|
|
|
143
|
"cmp" => "compare", |
|
33
|
|
|
|
|
|
|
"<=>" => "compare", |
|
34
|
|
|
|
|
|
|
"==" => "equals", |
|
35
|
|
|
|
|
|
|
"!=" => "differs", |
|
36
|
|
|
|
|
|
|
">=" => "superset", |
|
37
|
|
|
|
|
|
|
"<=" => "subset", |
|
38
|
|
|
|
|
|
|
">" => "superproper", |
|
39
|
|
|
|
|
|
|
"<" => "subproper", |
|
40
|
7
|
|
|
7
|
|
38
|
; |
|
|
7
|
|
|
|
|
9
|
|
|
41
|
|
|
|
|
|
|
|
|
42
|
|
|
|
|
|
|
Aion::Meta::Util::create_getters(qw/name args as/); |
|
43
|
|
|
|
|
|
|
Aion::Meta::Util::create_accessors(qw/message/); |
|
44
|
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
$Aion::Type::SELF = __PACKAGE__->new( |
|
46
|
|
|
|
|
|
|
is_param_args => __PACKAGE__->new(name => "Argument_ARGS", is_param => -1024), |
|
47
|
|
|
|
|
|
|
is_param => -256, |
|
48
|
|
|
|
|
|
|
name => 'Argument_SELF', |
|
49
|
|
|
|
|
|
|
args => [ |
|
50
|
|
|
|
|
|
|
__PACKAGE__->new(name => "Argument_A", is_param => 1), |
|
51
|
|
|
|
|
|
|
__PACKAGE__->new(name => "Argument_B", is_param => 2), |
|
52
|
|
|
|
|
|
|
__PACKAGE__->new(name => "Argument_C", is_param => 3), |
|
53
|
|
|
|
|
|
|
__PACKAGE__->new(name => "Argument_D", is_param => 4), |
|
54
|
|
|
|
|
|
|
], |
|
55
|
|
|
|
|
|
|
N => __PACKAGE__->new(name => "Argument_N", is_param => -1), |
|
56
|
|
|
|
|
|
|
M => __PACKAGE__->new(name => "Argument_M", is_param => -2), |
|
57
|
|
|
|
|
|
|
); |
|
58
|
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
# конструктор |
|
60
|
|
|
|
|
|
|
# * name (Str) — Имя типа. |
|
61
|
|
|
|
|
|
|
# * as (Object[Aion::Type]) — наследуемый тип. |
|
62
|
|
|
|
|
|
|
# * args (ArrayRef) — Список аргументов. |
|
63
|
|
|
|
|
|
|
# * init (ArrayRef[CodeRef]) — Инициализатор типа. |
|
64
|
|
|
|
|
|
|
# * test (CodeRef) — Чекер. |
|
65
|
|
|
|
|
|
|
# * a_test (CodeRef) — Используется для проверки типа с аргументами, если аргументы не указаны, то используется test. |
|
66
|
|
|
|
|
|
|
# * coerce (ArrayRef) — Массив преобразователей в этот тип: [Type => sub {}]. Общий для экземплятов параметрического типа. |
|
67
|
|
|
|
|
|
|
# * subset (CodeRef) - Проверка на подмножество типа A типу B. |
|
68
|
|
|
|
|
|
|
# * message (CodeRef) — Сообщение об ошибке. |
|
69
|
|
|
|
|
|
|
# * title (Str) — Заголовок. |
|
70
|
|
|
|
|
|
|
# * description (Str) — Описание. |
|
71
|
|
|
|
|
|
|
# * example (Any) — Пример. |
|
72
|
|
|
|
|
|
|
# * is_option (Bool) – это Option[A]. |
|
73
|
|
|
|
|
|
|
# * is_wantarray (Bool) – это Wantarray[A, S]. |
|
74
|
|
|
|
|
|
|
# * ally (Bool) – вступать в союз для объединения ветвей наследования при пересечении типов. |
|
75
|
|
|
|
|
|
|
sub new { |
|
76
|
3416
|
|
|
3416
|
1
|
238630
|
my $cls = shift; |
|
77
|
3416
|
|
|
|
|
10712
|
my $self = bless {@_}, $cls; |
|
78
|
3416
|
|
100
|
|
|
6850
|
$self->{test} //= \&test; |
|
79
|
3416
|
|
100
|
|
|
5187
|
$self->{coerce} //= []; |
|
80
|
3416
|
|
|
|
|
6090
|
$self |
|
81
|
|
|
|
|
|
|
} |
|
82
|
|
|
|
|
|
|
|
|
83
|
|
|
|
|
|
|
# Клонировать тип |
|
84
|
|
|
|
|
|
|
sub clone { |
|
85
|
293
|
|
|
293
|
1
|
319
|
my $self = shift; |
|
86
|
293
|
|
|
|
|
1317
|
$self = bless { %$self, @_ }, ref $self; |
|
87
|
293
|
|
|
|
|
530
|
delete @$self{qw/key as_test_cache/}; |
|
88
|
293
|
|
|
|
|
1935
|
$self |
|
89
|
|
|
|
|
|
|
} |
|
90
|
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
# Инициализировать тип |
|
92
|
|
|
|
|
|
|
sub init { |
|
93
|
2647
|
|
|
2647
|
1
|
3254
|
my ($self) = @_; |
|
94
|
|
|
|
|
|
|
|
|
95
|
|
|
|
|
|
|
# Есть параметрические типы – не инициализируем |
|
96
|
2647
|
100
|
100
|
6832
|
|
7427
|
return $self if $self->{args} && List::Util::first { UNIVERSAL::isa($_, __PACKAGE__) && exists $_->{is_param} } @{$self->{args}}; |
|
|
6832
|
100
|
|
|
|
15104
|
|
|
|
2639
|
|
|
|
|
5260
|
|
|
97
|
|
|
|
|
|
|
|
|
98
|
2635
|
|
|
|
|
5757
|
local $Aion::Type::SELF = $self; |
|
99
|
2635
|
|
|
|
|
2744
|
$_->() for @{$self->{init}}; |
|
|
2635
|
|
|
|
|
5466
|
|
|
100
|
|
|
|
|
|
|
|
|
101
|
2634
|
|
|
|
|
19440
|
$self |
|
102
|
|
|
|
|
|
|
} |
|
103
|
|
|
|
|
|
|
|
|
104
|
|
|
|
|
|
|
#@category strings |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# Строковое представление |
|
107
|
|
|
|
|
|
|
sub stringify { |
|
108
|
17160
|
|
|
17160
|
1
|
327164
|
my ($self) = @_; |
|
109
|
|
|
|
|
|
|
|
|
110
|
17160
|
|
|
|
|
16022
|
my @args = map Aion::Meta::Util::val_to_str($_), @{$self->{args}}; |
|
|
17160
|
|
|
|
|
25500
|
|
|
111
|
|
|
|
|
|
|
|
|
112
|
|
|
|
|
|
|
$self->is_union? join "", "( ", join(" | ", @args), " )": |
|
113
|
|
|
|
|
|
|
$self->is_intersection? join "", "( ", join(" & ", @args), " )": |
|
114
|
|
|
|
|
|
|
$self->is_exclude? "~$args[0]": |
|
115
|
17160
|
100
|
|
|
|
21357
|
join("", $self->{name}, @args? ("[", join(", ", @args), "]") : ()); |
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
116
|
|
|
|
|
|
|
} |
|
117
|
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# Сообщение об ошибке |
|
119
|
|
|
|
|
|
|
sub detail { |
|
120
|
15
|
|
|
15
|
1
|
76
|
(my $self, local $_, my $name) = @_; |
|
121
|
15
|
|
|
|
|
23
|
local $Aion::Type::SELF = $self; |
|
122
|
15
|
100
|
|
|
|
40
|
$self->{message}? do { local $self->{property} = $name; $self->{message}->() }: |
|
|
3
|
|
|
|
|
8
|
|
|
|
3
|
|
|
|
|
9
|
|
|
123
|
|
|
|
|
|
|
"$name must have the type $self. The it is ${\ |
|
124
|
12
|
|
|
|
|
37
|
Aion::Meta::Util::val_to_str($_) |
|
125
|
|
|
|
|
|
|
}!" |
|
126
|
|
|
|
|
|
|
} |
|
127
|
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Преобразовать значение в строку |
|
129
|
|
|
|
|
|
|
sub val_to_str { |
|
130
|
1
|
|
|
1
|
1
|
2
|
my ($self, $val) = @_; |
|
131
|
1
|
|
|
|
|
3
|
Aion::Meta::Util::val_to_str($val) |
|
132
|
|
|
|
|
|
|
} |
|
133
|
|
|
|
|
|
|
|
|
134
|
|
|
|
|
|
|
#@category test |
|
135
|
|
|
|
|
|
|
|
|
136
|
|
|
|
|
|
|
# Строит кеш для вызова только для примитивного типа |
|
137
|
|
|
|
|
|
|
sub _build_as_test_cache { |
|
138
|
384
|
|
|
384
|
|
536
|
my ($self) = @_; |
|
139
|
|
|
|
|
|
|
|
|
140
|
384
|
|
|
|
|
429
|
my @as; |
|
141
|
384
|
|
|
|
|
920
|
for(my $i = $self->{as}; $i; $i = $i->{as}) { |
|
142
|
1148
|
100
|
|
|
|
1467
|
return "" if $i->is_set_theoretic; |
|
143
|
1029
|
100
|
|
|
|
3162
|
unshift @as, $i if $i->{test} != \&true; |
|
144
|
|
|
|
|
|
|
} |
|
145
|
|
|
|
|
|
|
|
|
146
|
265
|
|
|
|
|
743
|
\@as; |
|
147
|
|
|
|
|
|
|
} |
|
148
|
|
|
|
|
|
|
|
|
149
|
|
|
|
|
|
|
# Это - примитивный тип, то есть тот, в иерархии которого нет множественно-теоритических операторов |
|
150
|
|
|
|
|
|
|
sub is_primitive { |
|
151
|
2
|
|
|
2
|
1
|
4
|
my ($self) = @_; |
|
152
|
2
|
|
66
|
|
|
10
|
!!($self->{as_test_cache} //= $self->_build_as_test_cache); |
|
153
|
|
|
|
|
|
|
} |
|
154
|
|
|
|
|
|
|
|
|
155
|
|
|
|
|
|
|
# Тестировать значение в $_ |
|
156
|
|
|
|
|
|
|
sub test { |
|
157
|
1500
|
|
|
1500
|
1
|
2465
|
my ($self) = @_; |
|
158
|
|
|
|
|
|
|
|
|
159
|
1500
|
100
|
100
|
|
|
3960
|
if($self->{as_test_cache} //= $self->_build_as_test_cache) { |
|
160
|
1177
|
|
|
|
|
1211
|
local $Aion::Type::SELF; |
|
161
|
1177
|
|
|
|
|
1251
|
for $Aion::Type::SELF (@{$self->{as_test_cache}}) { |
|
|
1177
|
|
|
|
|
2137
|
|
|
162
|
2028
|
100
|
|
|
|
3740
|
return "" unless $Aion::Type::SELF->{test}->(); |
|
163
|
|
|
|
|
|
|
} |
|
164
|
|
|
|
|
|
|
} else { |
|
165
|
323
|
100
|
66
|
|
|
589
|
return "" if $self->{as} && !$self->{as}->test; |
|
166
|
|
|
|
|
|
|
} |
|
167
|
|
|
|
|
|
|
|
|
168
|
1421
|
|
|
|
|
2402
|
local $Aion::Type::SELF = $self; |
|
169
|
1421
|
|
|
|
|
2625
|
$self->{test}->(); |
|
170
|
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
|
|
172
|
|
|
|
|
|
|
# Является элементом множества описываемого типом |
|
173
|
|
|
|
|
|
|
sub include { |
|
174
|
731
|
|
|
731
|
1
|
7452
|
(my $self, local $_) = @_; |
|
175
|
731
|
|
|
|
|
1610
|
$self->test |
|
176
|
|
|
|
|
|
|
} |
|
177
|
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
# Не является элементом множества описываемого типом |
|
179
|
|
|
|
|
|
|
sub exclude { |
|
180
|
59
|
|
|
59
|
1
|
905
|
(my $self, local $_) = @_; |
|
181
|
59
|
|
|
|
|
95
|
!$self->test |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
# Валидировать значение в параметре |
|
185
|
|
|
|
|
|
|
sub validate { |
|
186
|
160
|
|
|
160
|
1
|
757
|
(my $self, local $_, my $name) = @_; |
|
187
|
160
|
100
|
|
|
|
278
|
die $self->detail($_, $name) unless $self->test; |
|
188
|
147
|
|
|
|
|
2308
|
$_ |
|
189
|
|
|
|
|
|
|
} |
|
190
|
|
|
|
|
|
|
|
|
191
|
|
|
|
|
|
|
# Преобразовать значение в параметре и вернуть преобразованное |
|
192
|
|
|
|
|
|
|
sub coerce { |
|
193
|
30
|
|
|
30
|
1
|
1427
|
local ($Aion::Type::SELF, $_) = @_; |
|
194
|
|
|
|
|
|
|
|
|
195
|
30
|
|
|
|
|
55
|
for my $coerce (@{$Aion::Type::SELF->{coerce}}) { |
|
|
30
|
|
|
|
|
84
|
|
|
196
|
34
|
100
|
|
|
|
89
|
return $coerce->[1]() if $coerce->[0]->test; |
|
197
|
|
|
|
|
|
|
} |
|
198
|
|
|
|
|
|
|
$_ |
|
199
|
3
|
|
|
|
|
38
|
} |
|
200
|
|
|
|
|
|
|
|
|
201
|
|
|
|
|
|
|
#@category compare |
|
202
|
|
|
|
|
|
|
|
|
203
|
|
|
|
|
|
|
#my $_any; my $_none; |
|
204
|
3
|
|
|
3
|
1
|
15
|
sub Any() { *Any = \&Aion::Types::Any; &Any } |
|
|
3
|
|
|
|
|
57
|
|
|
205
|
3
|
|
|
3
|
1
|
11
|
sub None() { *None = \&Aion::Types::None; &None } |
|
|
3
|
|
|
|
|
95
|
|
|
206
|
|
|
|
|
|
|
|
|
207
|
|
|
|
|
|
|
# refaddr coerce => минимальная нижняя граница. У Range она -Inf, а у остальных – 0 |
|
208
|
|
|
|
|
|
|
our %range_lbound; |
|
209
|
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
# Определяет, что тип – множественно-теоретический оператор |
|
211
|
|
|
|
|
|
|
my $set_theoretic = [qw/Union Intersection Exclude/]; |
|
212
|
2638
|
|
|
2638
|
1
|
6056
|
sub is_set_theoretic { shift->{name} ~~ $set_theoretic } |
|
213
|
22193
|
|
|
22193
|
1
|
41494
|
sub is_union { shift->{name} eq 'Union' } |
|
214
|
27005
|
|
|
27005
|
1
|
43090
|
sub is_intersection { shift->{name} eq 'Intersection' } |
|
215
|
42882
|
|
|
42882
|
1
|
92216
|
sub is_exclude { shift->{name} eq 'Exclude' } |
|
216
|
23244
|
|
|
23244
|
1
|
36460
|
sub is_enum { shift->{name} eq 'Enum' } |
|
217
|
304
|
|
|
304
|
1
|
648
|
sub is_range_type { exists $range_lbound{Scalar::Util::refaddr shift->{coerce}} } |
|
218
|
60
|
|
|
60
|
1
|
225
|
sub range_lbound { $range_lbound{Scalar::Util::refaddr shift->{coerce}} } |
|
219
|
28
|
|
|
28
|
1
|
43
|
sub is_range { shift->range_lbound == '-Inf' } |
|
220
|
|
|
|
|
|
|
|
|
221
|
|
|
|
|
|
|
# Формирует ключ с отсортированными типизированными параметрами |
|
222
|
|
|
|
|
|
|
sub typed_sorted_args_key { |
|
223
|
996
|
|
|
996
|
1
|
1041
|
my ($self) = @_; |
|
224
|
996
|
|
|
|
|
974
|
my $coerceaddr = Scalar::Util::refaddr $self->{coerce}; |
|
225
|
996
|
|
|
|
|
907
|
join "-", $coerceaddr, join(",", map { join ":", length($_), $_ } sort map $_->key, @{$self->{args}}); |
|
|
2753
|
|
|
|
|
6211
|
|
|
|
996
|
|
|
|
|
1512
|
|
|
226
|
|
|
|
|
|
|
} |
|
227
|
|
|
|
|
|
|
|
|
228
|
|
|
|
|
|
|
# Формирует ключ с отсортированными нетипизированными параметрами |
|
229
|
|
|
|
|
|
|
sub sorted_args_key { |
|
230
|
28
|
|
|
28
|
1
|
31
|
my ($self) = @_; |
|
231
|
28
|
|
|
|
|
33
|
my $coerceaddr = Scalar::Util::refaddr $self->{coerce}; |
|
232
|
28
|
|
|
|
|
32
|
join "-", $coerceaddr, join(",", map { join ":", length($_), $_ } sort @{$self->{args}}); |
|
|
55
|
|
|
|
|
278
|
|
|
|
28
|
|
|
|
|
68
|
|
|
233
|
|
|
|
|
|
|
} |
|
234
|
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# Возвращает уникальный ключ для типа, использующийся в хешах и сравнения |
|
236
|
|
|
|
|
|
|
# Должен быть заменён на созданные типы |
|
237
|
|
|
|
|
|
|
my %keyfn; |
|
238
|
|
|
|
|
|
|
my $undefined = []; |
|
239
|
|
|
|
|
|
|
sub key { |
|
240
|
30328
|
|
|
30328
|
1
|
29802
|
my ($self) = @_; |
|
241
|
30328
|
|
66
|
|
|
57593
|
$self->{key} //= do { |
|
242
|
1447
|
|
|
|
|
1474
|
my $coerceaddr = Scalar::Util::refaddr $self->{coerce}; |
|
243
|
1447
|
|
|
|
|
1624
|
my $keyfn = $keyfn{$coerceaddr}; |
|
244
|
|
|
|
|
|
|
$keyfn |
|
245
|
|
|
|
|
|
|
? $keyfn->($self) |
|
246
|
|
|
|
|
|
|
: join "-", $coerceaddr, exists $self->{args} && @{$self->{args}} || exists $self->{N} || exists $self->{M} |
|
247
|
|
|
|
|
|
|
? join(",", map { |
|
248
|
498
|
100
|
33
|
|
|
1136
|
my $key = UNIVERSAL::isa($_, __PACKAGE__)? $_->key: "" . ($_ // $undefined); |
|
249
|
498
|
|
|
|
|
2144
|
join ":", length($key), $key |
|
250
|
1447
|
100
|
66
|
|
|
2220
|
} @{$self->{args}}) |
|
|
389
|
100
|
|
|
|
492
|
|
|
251
|
|
|
|
|
|
|
: (); |
|
252
|
|
|
|
|
|
|
}; |
|
253
|
|
|
|
|
|
|
} |
|
254
|
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# Устанавливает/возвращает функцию построения ключа для типа как класса |
|
256
|
|
|
|
|
|
|
sub keyfn { |
|
257
|
23
|
|
|
23
|
1
|
66
|
my ($self, $fn) = @_; |
|
258
|
23
|
50
|
|
|
|
45
|
if(@_>1) { |
|
259
|
23
|
|
|
|
|
60
|
$keyfn{Scalar::Util::refaddr $self->{coerce}} = $fn; |
|
260
|
23
|
|
|
|
|
38
|
$self |
|
261
|
|
|
|
|
|
|
} else { |
|
262
|
0
|
|
|
|
|
0
|
$keyfn{Scalar::Util::refaddr $self->{coerce}}; |
|
263
|
|
|
|
|
|
|
} |
|
264
|
|
|
|
|
|
|
} |
|
265
|
|
|
|
|
|
|
|
|
266
|
|
|
|
|
|
|
# Возвращает цепочку предков |
|
267
|
|
|
|
|
|
|
sub asen { |
|
268
|
16
|
|
|
16
|
1
|
28
|
my ($self) = @_; |
|
269
|
16
|
|
|
|
|
41
|
my @as; |
|
270
|
16
|
|
|
|
|
42
|
for(my $i=$self->{as}; $i; $i = $i->{as}) { unshift @as, $i } |
|
|
56
|
|
|
|
|
126
|
|
|
271
|
16
|
50
|
33
|
|
|
305
|
unshift @as, Any unless @as && $as[0] eq Any; |
|
272
|
|
|
|
|
|
|
@as |
|
273
|
16
|
|
|
|
|
42
|
} |
|
274
|
|
|
|
|
|
|
|
|
275
|
|
|
|
|
|
|
# Ключ для сравнения типов в <=> и cmp |
|
276
|
|
|
|
|
|
|
sub ckey { |
|
277
|
18
|
|
|
18
|
1
|
29
|
my ($self) = @_; |
|
278
|
18
|
|
66
|
|
|
71
|
$self->{ckey} //= join " <- ", map $_->stringify, $self->asen, $self; |
|
279
|
|
|
|
|
|
|
} |
|
280
|
|
|
|
|
|
|
|
|
281
|
|
|
|
|
|
|
# Сравнение для сортировки |
|
282
|
|
|
|
|
|
|
sub compare { |
|
283
|
9
|
|
|
9
|
1
|
21
|
my ($self, $other) = @_; |
|
284
|
9
|
|
|
|
|
28
|
$self->ckey cmp $other->ckey; |
|
285
|
|
|
|
|
|
|
} |
|
286
|
|
|
|
|
|
|
|
|
287
|
|
|
|
|
|
|
# A потомок B |
|
288
|
|
|
|
|
|
|
sub instanceof { |
|
289
|
6
|
|
|
6
|
1
|
939
|
my ($self, $name) = @_; |
|
290
|
|
|
|
|
|
|
|
|
291
|
6
|
|
|
|
|
12
|
my @S = $self; |
|
292
|
6
|
|
|
|
|
15
|
while(@S) { |
|
293
|
16
|
|
|
|
|
21
|
my $x = pop @S; |
|
294
|
16
|
100
|
|
|
|
42
|
return 1 if $x->{name} eq $name; |
|
295
|
11
|
100
|
|
|
|
17
|
if($x->is_intersection) { push @S, @{$x->{args}} } |
|
|
3
|
100
|
|
|
|
6
|
|
|
|
3
|
|
|
|
|
7
|
|
|
296
|
|
|
|
|
|
|
elsif($x->is_set_theoretic) {} |
|
297
|
7
|
100
|
|
|
|
20
|
else { push @S, $x->{as} if $x->{as} } |
|
298
|
|
|
|
|
|
|
} |
|
299
|
|
|
|
|
|
|
|
|
300
|
|
|
|
|
|
|
"" |
|
301
|
1
|
|
|
|
|
3
|
} |
|
302
|
|
|
|
|
|
|
|
|
303
|
|
|
|
|
|
|
# A потомок B |
|
304
|
|
|
|
|
|
|
sub is_descendant { |
|
305
|
33
|
|
|
33
|
1
|
43
|
my ($self, $other, $is_strict) = @_; |
|
306
|
|
|
|
|
|
|
|
|
307
|
33
|
100
|
33
|
|
|
78
|
return 1 if $is_strict && $self eq $other |
|
|
|
|
66
|
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
308
|
|
|
|
|
|
|
|| !$is_strict && $self->like($other); |
|
309
|
|
|
|
|
|
|
|
|
310
|
29
|
100
|
|
|
|
32
|
if ($self->is_intersection) { |
|
311
|
2
|
|
|
3
|
|
5
|
return List::Util::any { $_->is_descendant($other, $is_strict) } @{$self->{args}}; |
|
|
3
|
|
|
|
|
5
|
|
|
|
2
|
|
|
|
|
4
|
|
|
312
|
|
|
|
|
|
|
} |
|
313
|
27
|
100
|
|
|
|
33
|
if ($self->is_union) { |
|
314
|
3
|
|
|
6
|
|
8
|
return List::Util::all { $_->is_descendant($other, $is_strict) } @{$self->{args}}; |
|
|
6
|
|
|
|
|
10
|
|
|
|
3
|
|
|
|
|
8
|
|
|
315
|
|
|
|
|
|
|
} |
|
316
|
24
|
50
|
|
|
|
24
|
if ($self->is_exclude) { |
|
317
|
0
|
0
|
|
|
|
0
|
return $self->{args}[0]->is_descendant($other->is_exclude? $other->{args}[0]: ~$other, $is_strict); |
|
318
|
|
|
|
|
|
|
} |
|
319
|
24
|
100
|
|
|
|
31
|
return $self->{as}->is_descendant($other, $is_strict) if $self->{as}; |
|
320
|
|
|
|
|
|
|
|
|
321
|
2
|
|
|
|
|
13
|
"" |
|
322
|
|
|
|
|
|
|
} |
|
323
|
|
|
|
|
|
|
|
|
324
|
|
|
|
|
|
|
# Сравнивает по прототипам |
|
325
|
|
|
|
|
|
|
sub like { |
|
326
|
34
|
|
|
34
|
1
|
35
|
my ($self, $other) = @_; |
|
327
|
34
|
50
|
66
|
0
|
|
37
|
return List::Util::all { $_->[0]->like($_->[1]) } List::Util::zip $self->{args}, $other->{args} if $self->is_intersection && $other->is_intersection; |
|
|
0
|
|
|
|
|
0
|
|
|
328
|
34
|
50
|
66
|
0
|
|
37
|
return List::Util::any { $_->[0]->like($_->[1]) } List::Util::zip $self->{args}, $other->{args} if $self->is_union && $other->is_union; |
|
|
0
|
|
|
|
|
0
|
|
|
329
|
34
|
50
|
33
|
|
|
36
|
return $self->{args}[0]->like($other->{args}[0]) if $self->is_exclude && $other->is_exclude; |
|
330
|
34
|
100
|
66
|
|
|
38
|
return "" if $self->is_set_theoretic || $other->is_set_theoretic; |
|
331
|
29
|
|
|
|
|
87
|
$self->{coerce} == $other->{coerce}; |
|
332
|
|
|
|
|
|
|
} |
|
333
|
|
|
|
|
|
|
|
|
334
|
|
|
|
|
|
|
# Тождество |
|
335
|
|
|
|
|
|
|
sub identical { |
|
336
|
225
|
|
|
225
|
1
|
5195
|
my ($self, $other) = @_; |
|
337
|
|
|
|
|
|
|
|
|
338
|
225
|
100
|
|
|
|
1361
|
return 1 if Scalar::Util::refaddr $self == Scalar::Util::refaddr $other; |
|
339
|
|
|
|
|
|
|
return "" unless UNIVERSAL::isa($other, __PACKAGE__) |
|
340
|
159
|
100
|
100
|
|
|
1919
|
&& $self->{coerce} == $other->{coerce}; |
|
341
|
|
|
|
|
|
|
|
|
342
|
46
|
|
|
|
|
133
|
$self->key eq $other->key |
|
343
|
|
|
|
|
|
|
} |
|
344
|
|
|
|
|
|
|
|
|
345
|
|
|
|
|
|
|
# Нетождественно |
|
346
|
|
|
|
|
|
|
sub distinct { |
|
347
|
2
|
|
|
2
|
1
|
406
|
my ($self, $other) = @_; |
|
348
|
2
|
|
|
|
|
5
|
!$self->identical($other); |
|
349
|
|
|
|
|
|
|
} |
|
350
|
|
|
|
|
|
|
|
|
351
|
|
|
|
|
|
|
# Превращает выражение в ДНФ |
|
352
|
56
|
|
|
56
|
|
131
|
sub _simplify { shift->_unfolding->_pushing->_distribute } |
|
353
|
|
|
|
|
|
|
|
|
354
|
|
|
|
|
|
|
# Упрощает выражение |
|
355
|
|
|
|
|
|
|
# TODO: использовать алгоритм Espresso для свёртки DNF |
|
356
|
|
|
|
|
|
|
sub simplify { |
|
357
|
2
|
|
|
2
|
1
|
16
|
my ($self) = @_; |
|
358
|
|
|
|
|
|
|
|
|
359
|
2
|
100
|
|
|
|
6
|
$self->_simplify eq None? None: $self; |
|
360
|
|
|
|
|
|
|
} |
|
361
|
|
|
|
|
|
|
|
|
362
|
|
|
|
|
|
|
# A as B as C <=> A & B & C |
|
363
|
|
|
|
|
|
|
sub _unfolding { |
|
364
|
449
|
|
|
449
|
|
563
|
my ($self) = @_; |
|
365
|
|
|
|
|
|
|
|
|
366
|
449
|
|
|
|
|
427
|
my @u; |
|
367
|
449
|
|
|
|
|
717
|
for(my $i=$self; $i; $i = $i->{as}) { |
|
368
|
1419
|
100
|
|
|
|
1598
|
unshift(@u, $i->clone(args => [map $_->_unfolding, @{$i->{args}}])), last if $i->is_set_theoretic; |
|
|
222
|
|
|
|
|
473
|
|
|
369
|
1197
|
100
|
|
|
|
3191
|
unshift @u, $i if $i->{test} != \&true; |
|
370
|
|
|
|
|
|
|
} |
|
371
|
|
|
|
|
|
|
|
|
372
|
449
|
100
|
|
|
|
5213
|
@u == 0? Any: |
|
|
|
100
|
|
|
|
|
|
|
373
|
|
|
|
|
|
|
@u == 1? $u[0]: Aion::Types::Intersection(\@u); |
|
374
|
|
|
|
|
|
|
} |
|
375
|
|
|
|
|
|
|
|
|
376
|
|
|
|
|
|
|
# Проталкивание исключений к термам, заодно уменьшает размерность с приведением |
|
377
|
|
|
|
|
|
|
sub _pushing { |
|
378
|
1147
|
|
|
1147
|
|
1215
|
my ($self) = @_; |
|
379
|
|
|
|
|
|
|
|
|
380
|
1147
|
100
|
|
|
|
1324
|
if($self->is_exclude) { |
|
381
|
449
|
|
|
|
|
525
|
my $inner = $self->{args}[0]; |
|
382
|
|
|
|
|
|
|
# ~(~A) => A |
|
383
|
449
|
100
|
|
|
|
591
|
return $inner->{args}[0]->_pushing if $inner->is_exclude; |
|
384
|
|
|
|
|
|
|
# ~(A | B) => ~A & ~B |
|
385
|
448
|
100
|
|
|
|
624
|
return _intersection(map { (~$_)->_pushing } @{$inner->{args}}) if $inner->is_union; |
|
|
60
|
|
|
|
|
84
|
|
|
|
30
|
|
|
|
|
46
|
|
|
386
|
|
|
|
|
|
|
# ~(A & B) => ~A | ~B |
|
387
|
418
|
100
|
|
|
|
496
|
return _union(map { (~$_)->_pushing } @{$inner->{args}}) if $inner->is_intersection; |
|
|
339
|
|
|
|
|
437
|
|
|
|
116
|
|
|
|
|
155
|
|
|
388
|
|
|
|
|
|
|
# Range[A, B] => Range[-Inf, Invert[A]] | Range[Invert[B], Inf] |
|
389
|
302
|
100
|
|
|
|
370
|
if($inner->is_range_type) { |
|
390
|
25
|
|
|
|
|
31
|
my ($min, $max) = @{$inner->{args}}; |
|
|
25
|
|
|
|
|
64
|
|
|
391
|
25
|
100
|
|
|
|
50
|
if($inner->is_range) { |
|
392
|
11
|
50
|
33
|
|
|
39
|
return None if $min == '-Inf' && $max == 'Inf'; |
|
393
|
11
|
50
|
|
|
|
23
|
return $inner->clone(args => [Aion::Type::Lim->from($max)->inc, 'Inf']) if $min == '-Inf'; |
|
394
|
11
|
100
|
|
|
|
41
|
return $inner->clone(args => ['-Inf', Aion::Type::Lim->from($min)->dec]) if $max == 'Inf'; |
|
395
|
7
|
|
|
|
|
37
|
return $inner->clone(args => ['-Inf', Aion::Type::Lim->from($min)->dec]) | $inner->clone(args => [Aion::Type::Lim->from($max)->inc, 'Inf']); |
|
396
|
|
|
|
|
|
|
} |
|
397
|
|
|
|
|
|
|
|
|
398
|
14
|
100
|
100
|
|
|
49
|
return None if $min == 0 && $max == 'Inf'; |
|
399
|
13
|
100
|
|
|
|
36
|
return $inner->clone(args => [$max+1, 'Inf']) if $min == 0; |
|
400
|
5
|
100
|
|
|
|
15
|
return $inner->clone(args => [0, $min-1]) if $max == 'Inf'; |
|
401
|
4
|
|
|
|
|
12
|
return $inner->clone(args => [0, $min-1]) | $inner->clone(args => [$max+1, 'Inf']); |
|
402
|
|
|
|
|
|
|
} |
|
403
|
277
|
|
|
|
|
559
|
return $self; |
|
404
|
|
|
|
|
|
|
} |
|
405
|
|
|
|
|
|
|
|
|
406
|
698
|
100
|
|
|
|
802
|
return _intersection(map $_->_pushing, @{$self->{args}}) if $self->is_intersection; |
|
|
225
|
|
|
|
|
437
|
|
|
407
|
473
|
100
|
|
|
|
569
|
return _union(map $_->_pushing, @{$self->{args}}) if $self->is_union; |
|
|
46
|
|
|
|
|
126
|
|
|
408
|
|
|
|
|
|
|
|
|
409
|
427
|
|
|
|
|
712
|
$self |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
|
|
412
|
|
|
|
|
|
|
# Сжимает в ДНФ |
|
413
|
|
|
|
|
|
|
sub _distribute { |
|
414
|
886
|
|
|
886
|
|
869
|
my ($self) = @_; |
|
415
|
|
|
|
|
|
|
|
|
416
|
|
|
|
|
|
|
# (A|B) & (C|D|E) & F => (A&C&F) | (A&D&F) | (A&E&F) | (B&C&F) | (B&D&F) | (B&E&F) |
|
417
|
886
|
100
|
|
|
|
1001
|
if($self->is_intersection) { |
|
418
|
124
|
100
|
|
|
|
137
|
my @disjuncts = map { my $x = $_->_distribute; $x->is_union? [@{$x->{args}}]: [$x] } @{$self->{args}}; |
|
|
443
|
|
|
|
|
570
|
|
|
|
443
|
|
|
|
|
580
|
|
|
|
121
|
|
|
|
|
449
|
|
|
|
124
|
|
|
|
|
210
|
|
|
419
|
|
|
|
|
|
|
|
|
420
|
|
|
|
|
|
|
my $dnf = List::Util::reduce { |
|
421
|
443
|
|
|
443
|
|
508
|
[ map { my $p = $_; map { [@$p, $_] } @$b } @$a ] |
|
|
1716
|
|
|
|
|
1359
|
|
|
|
1716
|
|
|
|
|
1532
|
|
|
|
3615
|
|
|
|
|
5641
|
|
|
422
|
124
|
|
|
|
|
660
|
} [[]], @disjuncts; |
|
423
|
|
|
|
|
|
|
|
|
424
|
124
|
|
|
|
|
510
|
return _union(map _intersection(@$_), @$dnf); |
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
|
|
427
|
762
|
100
|
|
|
|
886
|
return _union(map $_->_distribute, @{$self->{args}}) if $self->is_union; |
|
|
121
|
|
|
|
|
279
|
|
|
428
|
|
|
|
|
|
|
|
|
429
|
641
|
|
|
|
|
863
|
$self |
|
430
|
|
|
|
|
|
|
} |
|
431
|
|
|
|
|
|
|
|
|
432
|
|
|
|
|
|
|
# Объединение интервалов |
|
433
|
|
|
|
|
|
|
sub _union_ranges { |
|
434
|
51
|
|
|
51
|
|
69
|
my ($ranges) = @_; |
|
435
|
|
|
|
|
|
|
|
|
436
|
|
|
|
|
|
|
# Отсекаем пустые |
|
437
|
51
|
|
|
|
|
191
|
my @ranges = grep $_->{args}[0] <= $_->{args}[1], @$ranges; |
|
438
|
|
|
|
|
|
|
|
|
439
|
|
|
|
|
|
|
# Сортируем в порядке возрастания нижней границы |
|
440
|
51
|
|
|
|
|
119
|
(my $range, @ranges) = sort { $a->{args}[0] <=> $b->{args}[0] } @ranges; |
|
|
22
|
|
|
|
|
63
|
|
|
441
|
|
|
|
|
|
|
|
|
442
|
|
|
|
|
|
|
@ranges = map { |
|
443
|
51
|
|
|
|
|
70
|
my ($min1, $max1) = @{$range->{args}}; |
|
|
22
|
|
|
|
|
23
|
|
|
|
22
|
|
|
|
|
43
|
|
|
444
|
22
|
|
|
|
|
29
|
my ($min2, $max2) = @{$_->{args}}; |
|
|
22
|
|
|
|
|
37
|
|
|
445
|
22
|
50
|
|
|
|
39
|
if($max1 > $min2) { $range = $range->clone(args => [$min1, List::Util::max($max1, $max2)]); () } |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
446
|
22
|
|
|
|
|
32
|
else { my $arange = $range; $range = $_; $arange } |
|
|
22
|
|
|
|
|
23
|
|
|
|
22
|
|
|
|
|
49
|
|
|
447
|
|
|
|
|
|
|
} @ranges; |
|
448
|
51
|
|
|
|
|
83
|
push @ranges, $range; |
|
449
|
|
|
|
|
|
|
|
|
450
|
51
|
100
|
|
|
|
88
|
if(@ranges == 1) { |
|
451
|
29
|
|
|
|
|
28
|
my ($min, $max) = @{$range->{args}}; |
|
|
29
|
|
|
|
|
50
|
|
|
452
|
29
|
50
|
66
|
|
|
49
|
return Any if $min == $range->range_lbound && $max == 'Inf'; |
|
453
|
|
|
|
|
|
|
} |
|
454
|
|
|
|
|
|
|
|
|
455
|
|
|
|
|
|
|
@ranges |
|
456
|
51
|
|
|
|
|
184
|
} |
|
457
|
|
|
|
|
|
|
|
|
458
|
|
|
|
|
|
|
# Обрабатывает пересечение границ однотипных диапазонов |
|
459
|
|
|
|
|
|
|
sub _intersection_ranges($) { |
|
460
|
1318
|
|
|
1318
|
|
1569
|
my ($ranges) = @_; |
|
461
|
|
|
|
|
|
|
|
|
462
|
|
|
|
|
|
|
# Пустой диапазон - это None |
|
463
|
1318
|
50
|
|
|
|
3210
|
return None if 0 == grep $_->{args}[0] <= $_->{args}[1], @$ranges; |
|
464
|
|
|
|
|
|
|
|
|
465
|
|
|
|
|
|
|
# Сортируем в порядке возрастания нижней границы |
|
466
|
1318
|
|
|
|
|
1978
|
my ($range, @ranges) = sort { $a->{args}[0] <=> $b->{args}[0] } @$ranges; |
|
|
78
|
|
|
|
|
206
|
|
|
467
|
|
|
|
|
|
|
|
|
468
|
1318
|
|
|
|
|
1522
|
for my $arange (@ranges) { |
|
469
|
|
|
|
|
|
|
# Если хотя бы у одного нет пересечений – это None |
|
470
|
78
|
|
|
|
|
84
|
my ($min1, $max1) = @{$range->{args}}; |
|
|
78
|
|
|
|
|
139
|
|
|
471
|
78
|
|
|
|
|
86
|
my ($min2, $max2) = @{$arange->{args}}; |
|
|
78
|
|
|
|
|
141
|
|
|
472
|
78
|
|
|
|
|
166
|
my $max = List::Util::min($max1, $max2); |
|
473
|
78
|
100
|
|
|
|
149
|
return None if $min2 > $max; |
|
474
|
25
|
|
|
|
|
63
|
$range = $range->clone(args => [$min2, $max]); |
|
475
|
|
|
|
|
|
|
} |
|
476
|
|
|
|
|
|
|
|
|
477
|
|
|
|
|
|
|
$range |
|
478
|
1265
|
|
|
|
|
3928
|
} |
|
479
|
|
|
|
|
|
|
|
|
480
|
|
|
|
|
|
|
# Объединение перечислений |
|
481
|
|
|
|
|
|
|
sub _union_enums($,$) { |
|
482
|
3
|
|
|
3
|
|
8
|
my ($enums, $exclude_enums) = @_; |
|
483
|
|
|
|
|
|
|
|
|
484
|
3
|
|
|
|
|
28
|
my %enum = map {($_=>$_)} map @{$_->{args}}, @$enums; |
|
|
8
|
|
|
|
|
16
|
|
|
|
6
|
|
|
|
|
15
|
|
|
485
|
3
|
50
|
|
|
|
25
|
return $enums->[0]->clone(args => [sort values %enum])->init unless @$exclude_enums; |
|
486
|
|
|
|
|
|
|
|
|
487
|
0
|
|
|
|
|
0
|
my $first_exclude_enum = shift(@$exclude_enums); |
|
488
|
0
|
|
|
|
|
0
|
my %exclude_enum = map {($_=>$_)} @{$first_exclude_enum->{args}}; |
|
|
0
|
|
|
|
|
0
|
|
|
|
0
|
|
|
|
|
0
|
|
|
489
|
0
|
|
|
|
|
0
|
for my $exclude_enum (@$exclude_enums) { |
|
490
|
0
|
|
|
|
|
0
|
delete @exclude_enum{grep { !($_ ~~ $exclude_enum->{args}) } keys %exclude_enum}; |
|
|
0
|
|
|
|
|
0
|
|
|
491
|
0
|
0
|
|
|
|
0
|
return Any unless keys %exclude_enum; |
|
492
|
|
|
|
|
|
|
} |
|
493
|
|
|
|
|
|
|
|
|
494
|
0
|
|
|
|
|
0
|
delete @exclude_enum{keys %enum}; |
|
495
|
|
|
|
|
|
|
|
|
496
|
0
|
0
|
|
|
|
0
|
return Any unless keys %exclude_enum; |
|
497
|
|
|
|
|
|
|
|
|
498
|
0
|
|
|
|
|
0
|
~$first_exclude_enum->clone(args => [sort values %exclude_enum])->init; |
|
499
|
|
|
|
|
|
|
} |
|
500
|
|
|
|
|
|
|
|
|
501
|
|
|
|
|
|
|
# Пересечение перечислений |
|
502
|
|
|
|
|
|
|
sub _intersection_enums($,$) { |
|
503
|
16
|
|
|
16
|
|
23
|
my ($enums, $exclude_enums) = @_; |
|
504
|
|
|
|
|
|
|
|
|
505
|
16
|
|
|
|
|
28
|
my %exclude_enum = map {($_=>$_)} map @{$_->{args}}, @$exclude_enums; |
|
|
29
|
|
|
|
|
55
|
|
|
|
15
|
|
|
|
|
33
|
|
|
506
|
16
|
100
|
|
|
|
47
|
return ~$exclude_enums->[0]->clone(args => [sort values %exclude_enum])->init unless @$enums; |
|
507
|
|
|
|
|
|
|
|
|
508
|
14
|
|
|
|
|
22
|
my $first_enum = shift(@$enums); |
|
509
|
14
|
|
|
|
|
16
|
my %enum = map {($_=>$_)} @{$first_enum->{args}}; |
|
|
30
|
|
|
|
|
67
|
|
|
|
14
|
|
|
|
|
22
|
|
|
510
|
|
|
|
|
|
|
|
|
511
|
14
|
|
|
|
|
25
|
for my $enum (@$enums) { |
|
512
|
3
|
|
|
|
|
7
|
delete @enum{grep { !($_ ~~ $enum->{args}) } keys %enum}; |
|
|
7
|
|
|
|
|
19
|
|
|
513
|
3
|
100
|
|
|
|
11
|
return None unless keys %enum; |
|
514
|
|
|
|
|
|
|
} |
|
515
|
|
|
|
|
|
|
|
|
516
|
13
|
|
|
|
|
28
|
delete @enum{keys %exclude_enum}; |
|
517
|
|
|
|
|
|
|
|
|
518
|
13
|
100
|
|
|
|
122
|
return None unless keys %enum; |
|
519
|
|
|
|
|
|
|
|
|
520
|
5
|
|
|
|
|
14
|
$first_enum->clone(args => [sort values %enum])->init; |
|
521
|
|
|
|
|
|
|
} |
|
522
|
|
|
|
|
|
|
|
|
523
|
|
|
|
|
|
|
# Обрабатывает пересечение границ диапазонов |
|
524
|
|
|
|
|
|
|
sub _ranges_bag(@) { |
|
525
|
2685
|
|
|
2685
|
|
2673
|
my $ranges_fn = shift; |
|
526
|
2685
|
|
|
|
|
2598
|
my $enums_fn = shift; |
|
527
|
2685
|
|
|
|
|
4826
|
my %bag; my @any; my @enums; my @exclude_enums; |
|
|
2685
|
|
|
|
|
0
|
|
|
|
2685
|
|
|
|
|
0
|
|
|
528
|
2685
|
|
|
|
|
3177
|
for my $candidate (@_) { |
|
529
|
18377
|
|
|
|
|
18887
|
my $addr = Scalar::Util::refaddr $candidate->{coerce}; |
|
530
|
18377
|
100
|
100
|
|
|
25527
|
if(exists $range_lbound{$addr}) { push @{$bag{$addr}}, $candidate } |
|
|
1469
|
100
|
|
|
|
1351
|
|
|
|
1469
|
100
|
|
|
|
2961
|
|
|
531
|
23
|
|
|
|
|
58
|
elsif($candidate->is_enum) { push @enums, $candidate } |
|
532
|
15
|
|
|
|
|
32
|
elsif($candidate->is_exclude && $candidate->{args}[0]->is_enum) { push @exclude_enums, $candidate->{args}[0] } |
|
533
|
16870
|
|
|
|
|
20416
|
else { push @any, $candidate } |
|
534
|
|
|
|
|
|
|
} |
|
535
|
|
|
|
|
|
|
|
|
536
|
2685
|
100
|
100
|
|
|
8328
|
return @any, @enums || @exclude_enums? $enums_fn->(\@enums, \@exclude_enums): (), map $ranges_fn->($_), values %bag; |
|
537
|
|
|
|
|
|
|
} |
|
538
|
|
|
|
|
|
|
|
|
539
|
|
|
|
|
|
|
# Создание пересечения с приведением |
|
540
|
|
|
|
|
|
|
sub _intersection(@) { |
|
541
|
2278
|
100
|
|
2278
|
|
3432
|
my %x = map {($_->key => $_)} _ranges_bag \&_intersection_ranges, \&_intersection_enums, map { $_->is_intersection? @{$_->{args}}: $_ } @_; |
|
|
14920
|
|
|
|
|
15508
|
|
|
|
8606
|
|
|
|
|
9036
|
|
|
|
2731
|
|
|
|
|
5229
|
|
|
542
|
|
|
|
|
|
|
# ~Any & A = ~Any |
|
543
|
2278
|
100
|
|
|
|
4957
|
return None if exists $x{None->key}; |
|
544
|
|
|
|
|
|
|
# Any & A = A |
|
545
|
2216
|
|
|
|
|
34547
|
delete $x{Any->key}; |
|
546
|
|
|
|
|
|
|
# Intersection[A] = A |
|
547
|
2216
|
100
|
|
|
|
3211
|
return (values %x)[0] if 1 == keys %x; |
|
548
|
|
|
|
|
|
|
# Intersection[] = Any |
|
549
|
2183
|
100
|
|
|
|
2758
|
return Any if 0 == keys %x; |
|
550
|
|
|
|
|
|
|
# A & ~A = ~Any |
|
551
|
2182
|
100
|
|
6985
|
|
7813
|
return None if List::Util::first { $_->is_exclude && exists $x{$_->{args}[0]->key} } values %x; |
|
|
6985
|
100
|
|
|
|
8069
|
|
|
552
|
988
|
|
|
|
|
12478
|
Aion::Types::Intersection([values %x]); |
|
553
|
|
|
|
|
|
|
} |
|
554
|
|
|
|
|
|
|
|
|
555
|
|
|
|
|
|
|
# Создание объединения с приведением |
|
556
|
|
|
|
|
|
|
sub _union(@) { |
|
557
|
407
|
100
|
|
407
|
|
703
|
my %x = map {($_->key => $_)} _ranges_bag \&_union_ranges, \&_union_enums, map { $_->is_union? @{$_->{args}}: $_ } @_; |
|
|
3360
|
|
|
|
|
3590
|
|
|
|
2841
|
|
|
|
|
2929
|
|
|
|
78
|
|
|
|
|
186
|
|
|
558
|
|
|
|
|
|
|
# Any | A = Any |
|
559
|
407
|
50
|
|
|
|
6816
|
return Any if exists $x{Any->key}; |
|
560
|
|
|
|
|
|
|
# ~Any | A = A |
|
561
|
407
|
|
|
|
|
705
|
delete $x{None->key}; |
|
562
|
|
|
|
|
|
|
# Union[A] = A |
|
563
|
407
|
100
|
|
|
|
834
|
return (values %x)[0] if 1 == keys %x; |
|
564
|
|
|
|
|
|
|
# Union[] = None |
|
565
|
352
|
100
|
|
|
|
500
|
return None if 0 == keys %x; |
|
566
|
|
|
|
|
|
|
# A | ~A = Any |
|
567
|
332
|
100
|
|
1859
|
|
1400
|
return Any if List::Util::first { $_->is_exclude && exists $x{$_->{args}[0]->key} } values %x; |
|
|
1859
|
50
|
|
|
|
2117
|
|
|
568
|
332
|
|
|
|
|
4868
|
Aion::Types::Union([values %x]); |
|
569
|
|
|
|
|
|
|
} |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
# A <= B <=> A & ~B = ∅ |
|
572
|
|
|
|
|
|
|
sub subset { |
|
573
|
63
|
|
|
63
|
1
|
800
|
my ($self, $other) = @_; |
|
574
|
|
|
|
|
|
|
|
|
575
|
63
|
100
|
100
|
|
|
153
|
return 1 if $self eq $other or $other eq Any; |
|
576
|
|
|
|
|
|
|
|
|
577
|
50
|
|
|
|
|
133
|
($self & ~$other)->_simplify eq None; |
|
578
|
|
|
|
|
|
|
} |
|
579
|
|
|
|
|
|
|
|
|
580
|
|
|
|
|
|
|
# A < B (Строгое включение: подтип, но не равен) = A <= B && !(B <= A) |
|
581
|
|
|
|
|
|
|
sub subproper { |
|
582
|
13
|
|
|
13
|
1
|
1061
|
my ($self, $other) = @_; |
|
583
|
13
|
100
|
|
|
|
35
|
$self->subset($other) && !$other->subset($self); |
|
584
|
|
|
|
|
|
|
} |
|
585
|
|
|
|
|
|
|
|
|
586
|
|
|
|
|
|
|
# A >= B = B <= A |
|
587
|
|
|
|
|
|
|
sub superset { |
|
588
|
0
|
|
|
0
|
1
|
0
|
my ($self, $other) = @_; |
|
589
|
0
|
|
|
|
|
0
|
$other->subset($self); |
|
590
|
|
|
|
|
|
|
} |
|
591
|
|
|
|
|
|
|
|
|
592
|
|
|
|
|
|
|
# A > B = B < A |
|
593
|
|
|
|
|
|
|
sub superproper { |
|
594
|
1
|
|
|
1
|
1
|
3
|
my ($self, $other) = @_; |
|
595
|
1
|
|
|
|
|
3
|
$other->subproper($self); |
|
596
|
|
|
|
|
|
|
} |
|
597
|
|
|
|
|
|
|
|
|
598
|
|
|
|
|
|
|
# A == B (Эквивалентность типов: A является подтипом B И B является подтипом A) = A <= B && B <= A |
|
599
|
|
|
|
|
|
|
sub equals { |
|
600
|
7
|
|
|
7
|
1
|
18
|
my ($self, $other) = @_; |
|
601
|
7
|
100
|
33
|
|
|
18
|
$self eq $other || $self->subset($other) && $other->subset($self); |
|
602
|
|
|
|
|
|
|
} |
|
603
|
|
|
|
|
|
|
|
|
604
|
|
|
|
|
|
|
# A != B |
|
605
|
|
|
|
|
|
|
sub differs { |
|
606
|
0
|
|
|
0
|
1
|
0
|
my ($self, $other) = @_; |
|
607
|
0
|
|
|
|
|
0
|
!$self->equals($other); |
|
608
|
|
|
|
|
|
|
} |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
# Пересекаются |
|
611
|
|
|
|
|
|
|
sub joint { |
|
612
|
2
|
|
|
2
|
1
|
5
|
my ($self, $other) = @_; |
|
613
|
2
|
|
|
|
|
5
|
!$self->disjoint($other); |
|
614
|
|
|
|
|
|
|
} |
|
615
|
|
|
|
|
|
|
|
|
616
|
|
|
|
|
|
|
# Не пересекаются |
|
617
|
|
|
|
|
|
|
sub disjoint { |
|
618
|
4
|
|
|
4
|
1
|
7
|
my ($self, $other) = @_; |
|
619
|
4
|
|
|
|
|
8
|
($self & $other)->_simplify eq None; |
|
620
|
|
|
|
|
|
|
} |
|
621
|
|
|
|
|
|
|
|
|
622
|
|
|
|
|
|
|
#@category swagger |
|
623
|
|
|
|
|
|
|
|
|
624
|
|
|
|
|
|
|
# Заголовок |
|
625
|
|
|
|
|
|
|
sub title { |
|
626
|
0
|
|
|
0
|
1
|
0
|
my ($self, $title) = @_; |
|
627
|
0
|
0
|
|
|
|
0
|
if(@_ == 1) { |
|
628
|
|
|
|
|
|
|
$self->{title} |
|
629
|
0
|
|
|
|
|
0
|
} else { |
|
630
|
0
|
|
|
|
|
0
|
bless {%$self, title => $title}, ref $self |
|
631
|
|
|
|
|
|
|
} |
|
632
|
|
|
|
|
|
|
} |
|
633
|
|
|
|
|
|
|
|
|
634
|
|
|
|
|
|
|
# Описание |
|
635
|
|
|
|
|
|
|
sub description { |
|
636
|
0
|
|
|
0
|
1
|
0
|
my ($self, $description) = @_; |
|
637
|
0
|
0
|
|
|
|
0
|
if(@_ == 1) { |
|
638
|
|
|
|
|
|
|
$self->{description} |
|
639
|
0
|
|
|
|
|
0
|
} else { |
|
640
|
0
|
|
|
|
|
0
|
bless {%$self, description => $description}, ref $self |
|
641
|
|
|
|
|
|
|
} |
|
642
|
|
|
|
|
|
|
} |
|
643
|
|
|
|
|
|
|
|
|
644
|
|
|
|
|
|
|
# Описание |
|
645
|
|
|
|
|
|
|
sub example { |
|
646
|
0
|
|
|
0
|
1
|
0
|
my ($self, $description) = @_; |
|
647
|
0
|
0
|
|
|
|
0
|
if(@_ == 1) { |
|
648
|
|
|
|
|
|
|
$self->{example} |
|
649
|
0
|
|
|
|
|
0
|
} else { |
|
650
|
0
|
|
|
|
|
0
|
bless {%$self, example => $description}, ref $self |
|
651
|
|
|
|
|
|
|
} |
|
652
|
|
|
|
|
|
|
} |
|
653
|
|
|
|
|
|
|
|
|
654
|
|
|
|
|
|
|
#@category makers |
|
655
|
|
|
|
|
|
|
|
|
656
|
|
|
|
|
|
|
# Создаёт функцию для типа |
|
657
|
|
|
|
|
|
|
sub make { |
|
658
|
333
|
|
|
333
|
1
|
509
|
my ($self, $pkg) = @_; |
|
659
|
|
|
|
|
|
|
|
|
660
|
333
|
100
|
|
|
|
583
|
die "init_where won't work in $self->{name}" if $self->{init}; |
|
661
|
|
|
|
|
|
|
|
|
662
|
332
|
|
|
|
|
449
|
my $var = "\$$self->{name}"; |
|
663
|
|
|
|
|
|
|
|
|
664
|
332
|
|
|
|
|
479
|
my $code = "package $pkg { |
|
665
|
|
|
|
|
|
|
my $var = \$self; |
|
666
|
|
|
|
|
|
|
sub $self->{name} () { $var } |
|
667
|
|
|
|
|
|
|
}"; |
|
668
|
332
|
|
|
2738
|
1
|
38002
|
eval $code; |
|
|
2738
|
|
|
4
|
1
|
12147
|
|
|
|
4
|
|
|
25
|
1
|
3959
|
|
|
|
25
|
|
|
8
|
1
|
6157
|
|
|
|
8
|
|
|
15
|
1
|
7828
|
|
|
|
15
|
|
|
3
|
1
|
3136
|
|
|
|
3
|
|
|
37
|
1
|
5166
|
|
|
|
37
|
|
|
35
|
1
|
3393
|
|
|
|
35
|
|
|
18
|
1
|
192
|
|
|
|
18
|
|
|
4
|
1
|
3133
|
|
|
|
4
|
|
|
3
|
1
|
4161
|
|
|
|
3
|
|
|
5
|
1
|
3549
|
|
|
|
5
|
|
|
4
|
1
|
6335
|
|
|
|
4
|
|
|
2
|
1
|
4768
|
|
|
|
2
|
|
|
6
|
1
|
3572
|
|
|
|
6
|
|
|
19
|
1
|
6556
|
|
|
|
19
|
|
|
4
|
1
|
9190
|
|
|
|
4
|
|
|
2
|
1
|
3991
|
|
|
|
2
|
|
|
105
|
1
|
4342
|
|
|
|
105
|
|
|
42
|
1
|
238316
|
|
|
|
42
|
|
|
6
|
1
|
282
|
|
|
|
6
|
|
|
42
|
1
|
8948
|
|
|
|
42
|
|
|
9
|
1
|
7745
|
|
|
|
9
|
|
|
3
|
1
|
7456
|
|
|
|
3
|
|
|
82
|
1
|
3601
|
|
|
|
82
|
|
|
7
|
0
|
285131
|
|
|
|
7
|
|
|
17
|
0
|
51
|
|
|
|
17
|
|
|
3
|
1
|
4701
|
|
|
|
3
|
|
|
13
|
1
|
3585
|
|
|
|
13
|
|
|
4
|
1
|
9479
|
|
|
|
4
|
|
|
9
|
1
|
4201
|
|
|
|
9
|
|
|
108
|
1
|
3840
|
|
|
|
108
|
|
|
6
|
1
|
6092
|
|
|
|
6
|
|
|
2
|
1
|
7812
|
|
|
|
2
|
|
|
4
|
1
|
4472
|
|
|
|
4
|
|
|
182
|
1
|
3922
|
|
|
|
182
|
|
|
2
|
1
|
346795
|
|
|
|
2
|
|
|
2
|
1
|
3185
|
|
|
|
2
|
|
|
10
|
1
|
3156
|
|
|
|
10
|
|
|
17
|
1
|
5309
|
|
|
|
17
|
|
|
15
|
1
|
6707
|
|
|
|
15
|
|
|
18
|
1
|
9164
|
|
|
|
18
|
|
|
3
|
1
|
6262
|
|
|
|
3
|
|
|
2
|
1
|
3554
|
|
|
|
2
|
|
|
19
|
1
|
3131
|
|
|
|
19
|
|
|
7
|
1
|
3569
|
|
|
|
7
|
|
|
2
|
|
5888
|
|
|
|
2
|
|
|
|
|
2789
|
|
|
669
|
332
|
100
|
|
|
|
1734
|
die if $@; |
|
670
|
|
|
|
|
|
|
|
|
671
|
331
|
|
|
|
|
3220
|
$self |
|
672
|
|
|
|
|
|
|
} |
|
673
|
|
|
|
|
|
|
|
|
674
|
|
|
|
|
|
|
# Создаёт функцию для типа c аргументом |
|
675
|
|
|
|
|
|
|
sub make_arg { |
|
676
|
219
|
|
|
219
|
1
|
369
|
my ($self, $pkg, $is_arg) = @_; |
|
677
|
|
|
|
|
|
|
|
|
678
|
219
|
|
|
|
|
313
|
my $hash = "%$self->{name}"; |
|
679
|
219
|
100
|
|
|
|
374
|
my $proto = $is_arg? '$': ''; |
|
680
|
|
|
|
|
|
|
|
|
681
|
219
|
100
|
|
|
|
318
|
if($is_arg) { |
|
682
|
211
|
100
|
|
|
|
310
|
my $init = $self->{init}? '->init': ''; |
|
683
|
211
|
|
|
|
|
340
|
my $code = "package $pkg { |
|
684
|
|
|
|
|
|
|
my $hash = %\$self; |
|
685
|
|
|
|
|
|
|
sub $self->{name} (\$) { Aion::Type->new($hash, args => \$_[0])$init } |
|
686
|
|
|
|
|
|
|
}"; |
|
687
|
211
|
|
|
15
|
0
|
30304
|
eval $code; |
|
|
15
|
|
|
4
|
0
|
14788
|
|
|
|
4
|
|
|
4
|
0
|
5484
|
|
|
|
4
|
|
|
7
|
0
|
28
|
|
|
|
7
|
|
|
2
|
0
|
37
|
|
|
|
2
|
|
|
47
|
0
|
3325
|
|
|
|
47
|
|
|
470
|
0
|
19195
|
|
|
|
470
|
|
|
6
|
0
|
1275
|
|
|
|
6
|
|
|
5
|
0
|
6670
|
|
|
|
5
|
|
|
3
|
0
|
6046
|
|
|
|
3
|
|
|
1349
|
0
|
5106
|
|
|
|
1349
|
|
|
5
|
0
|
3698
|
|
|
|
5
|
|
|
9
|
0
|
35
|
|
|
|
9
|
|
|
29
|
0
|
4373
|
|
|
|
29
|
|
|
29
|
0
|
8105
|
|
|
|
29
|
|
|
19
|
0
|
17796
|
|
|
|
19
|
|
|
5
|
0
|
8337
|
|
|
|
5
|
|
|
6
|
0
|
34
|
|
|
|
6
|
|
|
3
|
0
|
42
|
|
|
|
3
|
|
|
4
|
0
|
3943
|
|
|
|
4
|
|
|
15
|
0
|
23
|
|
|
|
15
|
|
|
3
|
0
|
11226
|
|
|
|
3
|
|
|
121
|
0
|
4740
|
|
|
|
121
|
|
|
11
|
0
|
22404
|
|
|
|
11
|
|
|
3
|
0
|
5407
|
|
|
|
3
|
|
|
4
|
0
|
6405
|
|
|
|
4
|
|
|
24
|
0
|
3577
|
|
|
|
24
|
|
|
387
|
0
|
4690
|
|
|
|
387
|
|
|
1
|
0
|
1234
|
|
|
|
1
|
|
|
|
|
5
|
|
|
688
|
211
|
50
|
|
|
|
923
|
die if $@; |
|
689
|
211
|
|
|
|
|
4240
|
return $self; |
|
690
|
|
|
|
|
|
|
} |
|
691
|
|
|
|
|
|
|
|
|
692
|
8
|
|
|
|
|
20
|
my $code = "package $pkg { |
|
693
|
|
|
|
|
|
|
my $hash = %\$self; |
|
694
|
|
|
|
|
|
|
sub $self->{name} () { Aion::Type->new($hash)->init } |
|
695
|
|
|
|
|
|
|
}"; |
|
696
|
8
|
|
|
8
|
1
|
1114
|
eval $code; |
|
|
8
|
|
|
|
|
4463
|
|
|
697
|
8
|
100
|
|
|
|
326
|
die if $@; |
|
698
|
|
|
|
|
|
|
|
|
699
|
7
|
|
|
|
|
25
|
$self |
|
700
|
|
|
|
|
|
|
} |
|
701
|
|
|
|
|
|
|
|
|
702
|
|
|
|
|
|
|
# Создаёт функцию для типа c аргументом или без. |
|
703
|
|
|
|
|
|
|
# init вызывается только для типа с аргументами. Без аргументов возвращается один и тот же тип |
|
704
|
|
|
|
|
|
|
sub make_maybe_arg { |
|
705
|
74
|
|
|
74
|
1
|
144
|
my ($self, $pkg) = @_; |
|
706
|
|
|
|
|
|
|
|
|
707
|
74
|
|
|
|
|
109
|
my $var = "\$$self->{name}"; |
|
708
|
74
|
|
|
|
|
107
|
my $hash = "%$self->{name}"; |
|
709
|
74
|
100
|
|
|
|
125
|
my $init = $self->{init}? '->init': ''; |
|
710
|
|
|
|
|
|
|
|
|
711
|
74
|
|
|
|
|
165
|
my $code = "package $pkg; |
|
712
|
|
|
|
|
|
|
|
|
713
|
|
|
|
|
|
|
my $var = \$self; |
|
714
|
|
|
|
|
|
|
my $hash = %\$self; |
|
715
|
|
|
|
|
|
|
|
|
716
|
|
|
|
|
|
|
sub $self->{name} (;\$) { |
|
717
|
|
|
|
|
|
|
\@_==0? $var: |
|
718
|
|
|
|
|
|
|
Aion::Type->new( |
|
719
|
|
|
|
|
|
|
$hash, |
|
720
|
|
|
|
|
|
|
args => \$_[0], |
|
721
|
|
|
|
|
|
|
test => ${var}->{a_test}, |
|
722
|
|
|
|
|
|
|
)$init |
|
723
|
|
|
|
|
|
|
} |
|
724
|
|
|
|
|
|
|
"; |
|
725
|
74
|
100
|
|
16
|
0
|
11173
|
eval $code or die; |
|
|
16
|
100
|
|
37
|
1
|
5817
|
|
|
|
37
|
100
|
|
30
|
0
|
4738
|
|
|
|
30
|
100
|
|
23
|
0
|
13914
|
|
|
|
23
|
100
|
|
52
|
0
|
5050
|
|
|
|
52
|
100
|
|
13
|
0
|
7872
|
|
|
|
13
|
100
|
|
4
|
0
|
6907
|
|
|
|
4
|
100
|
|
4
|
0
|
4302
|
|
|
|
4
|
100
|
|
14
|
0
|
4243
|
|
|
|
14
|
100
|
|
19
|
0
|
9455
|
|
|
|
19
|
100
|
|
|
|
4343
|
|
|
726
|
|
|
|
|
|
|
|
|
727
|
73
|
|
|
|
|
9625
|
$self |
|
728
|
|
|
|
|
|
|
} |
|
729
|
|
|
|
|
|
|
|
|
730
|
|
|
|
|
|
|
|
|
731
|
|
|
|
|
|
|
1; |
|
732
|
|
|
|
|
|
|
|
|
733
|
|
|
|
|
|
|
__END__ |