package Mouse::Util::TypeConstraints;
use Mouse::Util qw(does_role not_supported); # enables strict and warnings
-use Carp qw(confess);
+use Carp ();
use Scalar::Util ();
use Mouse::Meta::TypeConstraint;
)],
);
+our @CARP_NOT = qw(Mouse::Meta::Attribute);
+
my %TYPE;
# The root type
if($TYPE{$name}){
my $that = $TYPE{$name}->{package_defined_in} || __PACKAGE__;
- ($this eq $that) or confess(
+ ($this eq $that) or Carp::croak(
"The type constraint '$name' has already been created in $that and cannot be created again in $this"
);
}
my $type_name = shift;
my $type = find_type_constraint($type_name)
- or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
+ or Carp::croak("Cannot find type '$type_name', perhaps you forgot to load it.");
$type->_add_type_coercions(@_);
return;
}
sub _find_or_create_regular_type{
- my($spec) = @_;
+ my($spec, $create) = @_;
return $TYPE{$spec} if exists $TYPE{$spec};
- my $meta = Mouse::Util::get_metaclass_by_name($spec)
- or return undef;
+ my $meta = Mouse::Util::get_metaclass_by_name($spec);
+
+ if(!defined $meta){
+ return $create ? class_type($spec) : undef;
+ }
if(Mouse::Util::is_a_metarole($meta)){
return role_type($spec);
}
sub _find_or_create_union_type{
+ return if grep{ not defined } @_;
my @types = sort map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
my $name = join '|', @types;
}
# The type parser
-sub _parse_type{
- my($spec, $start) = @_;
-
- my @list;
- my $subtype;
- my $len = length $spec;
- my $i;
+# param : '[' type ']' | NOTHING
+sub _parse_param {
+ my($c) = @_;
- for($i = $start; $i < $len; $i++){
- my $char = substr($spec, $i, 1);
+ if($c->{spec} =~ s/^\[//){
+ my $type = _parse_type($c, 1);
- if($char eq '['){
- my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
- or return;
-
- ($i, $subtype) = _parse_type($spec, $i+1)
- or return;
- $start = $i+1; # reset
-
- push @list, _find_or_create_parameterized_type($base => $subtype);
- }
- elsif($char eq ']'){
- $len = $i+1;
- last;
+ if($c->{spec} =~ s/^\]//){
+ return $type;
}
- elsif($char eq '|'){
- my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
+ Carp::croak("Syntax error in type: missing right square bracket in '$c->{orig}'");
+ }
- if(!defined $type){
- # XXX: Mouse creates a new class type, but Moose does not.
- $type = class_type( substr($spec, $start, $i - $start) );
- }
+ return undef;
+}
- push @list, $type;
+# name : [\w.:]+
+sub _parse_name {
+ my($c, $create) = @_;
- ($i, $subtype) = _parse_type($spec, $i+1)
- or return;
+ if($c->{spec} =~ s/\A ([\w.:]+) //xms){
+ return _find_or_create_regular_type($1, $create);
+ }
+ Carp::croak("Syntax error in type: expect type name near '$c->{spec}' in '$c->{orig}'");
+}
- $start = $i+1; # reset
+# single_type : name param
+sub _parse_single_type {
+ my($c, $create) = @_;
- push @list, $subtype;
- }
- }
- if($i - $start){
- my $type = _find_or_create_regular_type( substr $spec, $start, $i - $start );
+ my $type = _parse_name($c, $create);
+ my $param = _parse_param($c);
- if(defined $type){
- push @list, $type;
+ if(defined $type){
+ if(defined $param){
+ return _find_or_create_parameterized_type($type, $param);
}
- elsif($start != 0) {
- # RT #50421
- # create a new class type
- push @list, class_type( substr $spec, $start, $i - $start );
+ else {
+ return $type;
}
}
-
- if(@list == 0){
- return;
- }
- elsif(@list == 1){
- return ($len, $list[0]);
+ elsif(defined $param){
+ Carp::croak("Undefined type with parameter [$param] in '$c->{orig}'");
}
else{
- return ($len, _find_or_create_union_type(@list));
+ return undef;
+ }
+}
+
+# type : single_type ('|' single_type)*
+sub _parse_type {
+ my($c, $create) = @_;
+
+ my $type = _parse_single_type($c, $create);
+ if($c->{spec}){ # can be an union type
+ my @types;
+ while($c->{spec} =~ s/^\|//){
+ push @types, _parse_single_type($c, $create);
+ }
+ if(@types){
+ return _find_or_create_union_type($type, @types);
+ }
}
+ return $type;
}
$spec =~ s/\s+//g;
return $TYPE{$spec} || do{
- my($pos, $type) = _parse_type($spec, 0);
+ my $context = {
+ spec => $spec,
+ orig => $spec,
+ };
+ my $type = _parse_type($context);
+
+ if($context->{spec}){
+ Carp::croak("Syntax error: extra elements '$context->{spec}' in '$context->{orig}'");
+ }
$type;
};
}