Aion
view release on metacpan or search on metacpan
lib/Aion/Type.pm view on Meta::CPAN
package Aion::Type;
# ÐазовÑй клаÑÑ Ð´Ð»Ñ Ñипов и пÑеобÑазоваÑелей
use common::sense;
use Aion::Meta::Util qw//;
use Scalar::Util qw/looks_like_number/;
use overload
"fallback" => 1,
"&{}" => sub {
my ($self) = @_;
sub { $self->test }
}, # ЧÑÐ¾Ð±Ñ Ñип мог бÑÑÑ Ð²Ñполнен, как ÑÑнкÑиÑ
'""' => "stringify", # ÐÑобÑажаÑÑ Ñип в ÑÑейÑбеке в ÑÑÑоковом пÑедÑÑавлении
"|" => sub {
my ($type1, $type2) = @_;
__PACKAGE__->new(name => "Union", args => [$type1, $type2], test => sub { $type1->test || $type2->test });
},
"&" => sub {
my ($type1, $type2) = @_;
__PACKAGE__->new(name => "Intersection", args => [$type1, $type2], test => sub { $type1->test && $type2->test });
},
"~" => sub {
my ($type1) = @_;
__PACKAGE__->new(name => "Exclude", args => [$type1], test => sub { !$type1->test });
},
"~~" => "include",
"eq" => "equal",
"ne" => "nonequal",
">>" => "coerce",
;
Aion::Meta::Util::create_getters(qw/name args as me/);
Aion::Meta::Util::create_accessors(qw/message/);
$Aion::Type::SELF = {
A => __PACKAGE__->new(name => "Argument_A"),
B => __PACKAGE__->new(name => "Argument_B"),
C => __PACKAGE__->new(name => "Argument_C"),
D => __PACKAGE__->new(name => "Argument_D"),
N => __PACKAGE__->new(name => "Argument_N"),
M => __PACKAGE__->new(name => "Argument_M"),
};
# конÑÑÑÑкÑоÑ
# * name (Str) â ÐÐ¼Ñ Ñипа.
# * as (Object[Aion::Type]) â наÑледÑемÑй Ñип.
# * args (ArrayRef) â СпиÑок аÑгÑменÑов.
# * init (CodeRef) â ÐниÑиализаÑÐ¾Ñ Ñипа.
# * test (CodeRef) â ЧекеÑ.
# * a_test (CodeRef) â ÐÑполÑзÑеÑÑÑ Ð´Ð»Ñ Ð¿ÑовеÑки Ñипа Ñ Ð°ÑгÑменÑами, еÑли аÑгÑменÑÑ Ð½Ðµ ÑказанÑ, Ñо иÑполÑзÑеÑÑÑ test.
# * coerce (ArrayRef) â ÐаÑÑив пÑеобÑазоваÑелей в ÑÑÐ¾Ñ Ñип: [Type => sub {}].
# * message (CodeRef) â СообÑение об оÑибке.
# * title (Str) â Ðаголовок.
# * description (Str) â ÐпиÑание.
# * example (Any) â ÐÑимеÑ.
# * me (Str) â ТолÑко Ð´Ð»Ñ Ñипа Me: Ð¿Ð°ÐºÐµÑ Ð² коÑоÑом он бÑл обÑÑвлен.
sub new {
my $cls = shift;
bless {@_}, $cls;
}
# СÑÑоковое пÑедÑÑавление
sub stringify {
my ($self) = @_;
my @args = map {
UNIVERSAL::isa($_, __PACKAGE__)?
$_->stringify:
Aion::Meta::Util::val_to_str($_)
} @{$self->{args}};
$self->{name} eq "Union"? join "", "( ", join(" | ", @args), " )":
$self->{name} eq "Intersection"? join "", "( ", join(" & ", @args), " )":
$self->{name} eq "Exclude"? (
@args == 1? join "", "~", @args:
join "", "~( ", join(" | ", @args), " )"
):
join("", $self->{name}, @args? ("[", join(", ", @args), "]") : ());
}
sub equal {
my ($self, $type) = @_;
return 1 if Scalar::Util::refaddr $self == Scalar::Util::refaddr $type;
return "" unless UNIVERSAL::isa($type, __PACKAGE__);
return "" unless $self->{name} eq $type->{name};
return "" unless @{$self->{args}} == @{$type->{args}};
return "" unless $self->{as} && $self->{as}->equal($type->{as})
|| !$self->{as} && !$type->{as};
my $i = 0;
for my $arg (@{$self->{args}}) {
return "" unless $arg eq $type->{args}[$i++];
}
return 1;
}
sub nonequal {
my ($self, $type) = @_;
!$self->equal($type)
}
# ТеÑÑиÑоваÑÑ Ð·Ð½Ð°Ñение в $_
sub test {
my ($self) = @_;
local $Aion::Type::SELF = $self;
my $ok = $self->{test}->();
$ok
}
# ÐниÑиализиÑоваÑÑ Ñип
sub init {
my ($self) = @_;
local $Aion::Type::SELF = $self;
$self->{init}->();
$self
}
lib/Aion/Type.pm view on Meta::CPAN
$self->test
}
# Ðе ÑвлÑеÑÑÑ ÑлеменÑом множеÑÑва опиÑÑваемого Ñипом
sub exclude {
(my $self, local $_) = @_;
!$self->test
}
# СообÑение об оÑибке
sub detail {
(my $self, local $_, my $name) = @_;
local $Aion::Type::SELF = $self;
local $Aion::Type::SELF->{N} = $name;
$self->{message}? $self->{message}->():
"$name must have the type $self. The it is ${\
Aion::Meta::Util::val_to_str($_)
}!"
}
# ÐалидиÑоваÑÑ Ð·Ð½Ð°Ñение в паÑамеÑÑе
sub validate {
(my $self, local $_, my $name) = @_;
die $self->detail($_, $name) if !$self->test;
$_
}
# ÐÑеобÑазоваÑÑ Ð·Ð½Ð°Ñение в ÑÑÑокÑ
sub val_to_str {
my ($self, $val) = @_;
Aion::Meta::Util::val_to_str($val)
}
# ÐÑеобÑазоваÑÑ Ð·Ð½Ð°Ñение в паÑамеÑÑе и веÑнÑÑÑ Ð¿ÑеобÑазованное
sub coerce {
(my $self, local $_) = @_;
local $Aion::Type::SELF = $self;
for my $coerce (@{$self->{coerce}}) {
return $coerce->[1]() if $coerce->[0]{test}();
}
$_
}
# ÐпÑеделÑеÑ, ÑÑо Ñип ÑвлÑеÑÑÑ Ð¿Ð¾Ð´Ñипом дÑÑгого Ñипа
sub instanceof {
my ($self, $name) = @_;
$name = $name->{name} if ref $name;
for(my $type = $self; $type; $type = $type->{as}) {
return 1 if $type->{name} eq $name;
}
""
}
# Ðаголовок
sub title {
my ($self, $title) = @_;
if(@_ == 1) {
$self->{title}
} else {
bless {%$self, title => $title}, ref $self
}
}
# ÐпиÑание
sub description {
my ($self, $description) = @_;
if(@_ == 1) {
$self->{description}
} else {
bless {%$self, description => $description}, ref $self
}
}
# ÐпиÑание
sub example {
my ($self, $description) = @_;
if(@_ == 1) {
$self->{example}
} else {
bless {%$self, example => $description}, ref $self
}
}
# СоздаÑÑ ÑÑнкÑÐ¸Ñ Ð´Ð»Ñ Ñипа
sub make {
my ($self, $pkg) = @_;
die "init_where won't work in $self" if $self->{init};
my $var = "\$$self->{name}";
my $code = "package $pkg {
my $var = \$self;
sub $self->{name} () { $var }
}";
eval $code;
die if $@;
$self
}
# СоздаÑÑ ÑÑнкÑÐ¸Ñ Ð´Ð»Ñ Ñипа c аÑгÑменÑом
sub make_arg {
my ($self, $pkg, $proto) = @_;
my $var = "\$$self->{name}";
my $init = $self->{init}? "->init": "";
$proto //= '$';
my $code = "package $pkg {
my $var = \$self;
sub $self->{name} ($proto) {
Aion::Type->new(
%$var,
args => \$_[0],
)$init
}
}";
eval $code;
die if $@;
$self
}
# СоздаÑÑ ÑÑнкÑÐ¸Ñ Ð´Ð»Ñ Ñипа c аÑгÑменÑом или без
sub make_maybe_arg {
my ($self, $pkg) = @_;
my $var = "\$$self->{name}";
my $init = $self->{init}? "->init": "";
my $code = "package $pkg;
my $var = \$self;
sub $self->{name} (;\$) {
\@_==0? $var:
Aion::Type->new(
( run in 2.981 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )