MarpaX-Role-Parameterized-ResourceIdentifier
view release on metacpan or search on metacpan
lib/MarpaX/Role/Parameterized/ResourceIdentifier/BNF.pm view on Meta::CPAN
# ---------------------------------------------------
use constant {
URI_CONVERTED => 0,
IRI_CONVERTED => 1,
_MAX_CONVERTER => 1,
_COUNT_CONVERTER => 2
};
#
# Depending on the "spec" attribute we will call one
# converter only. If spec is "uri" we call conversion to iri.
# If spec is "iri" we call conversion to uri. Though there
# remains two possible converters:
#
our @converter_names = qw/uri_converter iri_converter/;
# -----
# Other
# -----
our @ucs_mime_name = map { find_encoding($_)->mime_name } qw/UTF-8 UTF-16 UTF-16BE UTF-16LE UTF-32 UTF-32BE UTF-32LE/;
# ------------------------------------------------------------
# Explicit slots for all supported attributes in input
# scheme is explicitely ignored, it is handled only by _top
# ------------------------------------------------------------
has input => ( is => 'rwp', isa => StringLike );
has has_recognized_scheme => ( is => 'rw', isa => Bool, default => sub { !!0 } );
has is_character_normalized => ( is => 'rwp', isa => Bool, default => sub { !!1 } );
#
# Implementations should 'around' the folllowings
#
has pct_encoded => ( is => 'ro', isa => Str|Undef, lazy => 1, builder => 'build_pct_encoded' );
has unreserved => ( is => 'ro', isa => RegexpRef|Undef, lazy => 1, builder => 'build_unreserved' );
has reserved => ( is => 'ro', isa => RegexpRef|Undef, lazy => 1, builder => 'build_reserved' );
has default_port => ( is => 'ro', isa => Int|Undef, lazy => 1, builder => 'build_default_port' );
has reg_name_convert_as_domain_name => ( is => 'ro', isa => Bool, lazy => 1, builder => 'build_reg_name_convert_as_domain_name' );
has current_location => ( is => 'ro', isa => Str|Undef, lazy => 1, builder => 'build_current_location' );
has parent_location => ( is => 'ro', isa => Str|Undef, lazy => 1, builder => 'build_parent_location' );
has separator_location => ( is => 'ro', isa => Str|Undef, lazy => 1, builder => 'build_separator_location' );
__PACKAGE__->_generate_attributes('normalizer', @normalizer_names);
__PACKAGE__->_generate_attributes('converter', @converter_names);
# ------------------------------------------------------------------------------------------------
# Internal slots: one for the raw parse, one for the normalized value, one for the converted value
# ------------------------------------------------------------------------------------------------
has _orig_arg => ( is => 'rw', isa => Any ); # For cloning
has _structs => ( is => 'rw', isa => ArrayRef[Object] );
use constant {
_RAW_STRUCT => 0,
_NORMALIZED_STRUCT => 1,
_ESCAPED_STRUCT => 2,
_UNESCAPED_STRUCT => 3,
_CONVERTED_STRUCT => 4,
_MAX_STRUCTS => 4,
_COUNT_STRUCTS => 5
};
#
# Just a helper for me
#
has _indice_description => ( is => 'ro', isa => ArrayRef[Str], default => sub {
[
'Raw structure ',
'Normalized structure',
'Escaped structure',
'Unescaped structure',
'Converted structure '
]
}
);
#
# Generic helpers that will always work
#
sub raw { $_[0]->{_structs}->[ _RAW_STRUCT]->{$_[1] // 'output'} }
sub raw_scheme { $_[0]->raw('scheme') }
sub raw_opaque { $_[0]->raw('opaque') }
sub raw_fragment { $_[0]->raw('fragment') }
sub normalized { $_[0]->{_structs}->[_NORMALIZED_STRUCT]->{$_[1] // 'output'} }
sub normalized_scheme { $_[0]->normalized('scheme') }
sub normalized_opaque { $_[0]->normalized('opaque') }
sub normalized_fragment { $_[0]->normalized('fragment') }
sub escaped { $_[0]->{_structs}->[ _ESCAPED_STRUCT]->{$_[1] // 'output'} }
sub escaped_scheme { $_[0]->escaped('scheme') }
sub escaped_opaque { $_[0]->escaped('opaque') }
sub escaped_fragment { $_[0]->escaped('fragment') }
sub unescaped { $_[0]->{_structs}->[ _UNESCAPED_STRUCT]->{$_[1] // 'output'} }
sub unescaped_scheme { $_[0]->unescaped('scheme') }
sub unescaped_opaque { $_[0]->unescaped('opaque') }
sub unescaped_fragment { $_[0]->unescaped('fragment') }
sub converted { $_[0]->{_structs}->[ _CONVERTED_STRUCT]->{$_[1] // 'output'} }
sub converted_scheme { $_[0]->converted('scheme') }
sub converted_opaque { $_[0]->converted('opaque') }
sub converted_fragment { $_[0]->converted('fragment') }
#
# Let's be always URI compatible for the canonical method
#
sub canonical { goto &normalized }
#
# as_string returns the perl string
#
sub as_string { goto &raw }
# =======================================================================
# We want parsing to happen immedately AFTER object was built and then at
# every input reconstruction
# =======================================================================
our $check_BUILDARGS = compile(StringLike|HashRef);
our $check_BUILDARGS_Dict = compile(slurpy Dict[
input => Optional[StringLike],
octets => Optional[Bytes],
encoding => Optional[Str],
decode_strategy => Optional[Any],
is_character_normalized => Optional[Bool],
reg_name_convert_as_domain_name => Optional[Bool],
current_location => Optional[Str],
parent_location => Optional[Str],
separator_location => Optional[Str]
lib/MarpaX/Role/Parameterized/ResourceIdentifier/BNF.pm view on Meta::CPAN
# path, then return a string consisting of "/" concatenated with the
# reference's path; otherwise,
#
if (defined($base->{authority}) && ! length($base->{path})) {
return $ref->{separator_location} . $ref->{path};
}
#
# return a string consisting of the reference's path component
# appended to all but the last segment of the base URI's path (i.e.,
# excluding any characters after the right-most "/" in the base URI
# path, or excluding the entire base URI path if it does not contain
# any "/" characters).
#
else {
my $base_path = $base->{path};
if ($base_path !~ /\//) {
$base_path = '';
} else {
$base_path =~ s/\/[^\/]*\z/\//;
}
return $base_path . $ref->{path};
}
}
sub _recompose {
my ($class, $T) = @_;
#
# https://tools.ietf.org/html/rfc3986
#
# 5.3. Component Recomposition
#
# We are compatiblee with both common and generic syntax:
# - the common case can have only scheme, path (== opaque) and fragment
# - the generic case can have scheme, authority, path, query and fragment
#
my $result = '';
$result .= $T->{scheme} . ':' if (defined $T->{scheme});
if (defined $T->{opaque}) {
$result .= $T->{opaque};
} else {
$result .= '//' . $T->{authority} if (defined $T->{authority});
$result .= $T->{path};
$result .= '?' . $T->{query} if (defined $T->{query});
}
$result .= '#' . $T->{fragment} if (defined $T->{fragment});
$result
}
# =============================================================================
# Internal class methods
# =============================================================================
sub _generate_attributes {
my ($class, $type, @names) = @_;
#
# The lazy builders that implementation should around
#
foreach (@names) {
my $builder = "build_$_";
has $_ => (is => 'ro', isa => HashRef[CodeRef],
lazy => 1,
builder => $builder,
handles_via => 'Hash',
handles => {
"get_$_" => 'get',
"set_$_" => 'set',
"exists_$_" => 'exists',
"delete_$_" => 'delete',
"kv_$_" => 'kv',
"keys_$_" => 'keys',
"values_$_" => 'values',
}
);
}
my $_type_names = "_${type}_names";
my $_type_wrapper = "_${type}_wrapper";
my $_type_wrapper_call_lazy_builder = "_${type}_wrapper_call_lazy_builder";
#
# Just a convenient thing for us
#
has $_type_names => (is => 'ro', isa => ArrayRef[Str|Undef], default => sub { \@names });
#
# The important thing is these wrappers:
# - the one using accessors so that we are sure builders are executed
# - the one without the accessors for performance
#
has $_type_wrapper => (is => 'ro', isa => ArrayRef[CodeRef|Undef],
# lazy => 1, Not lazy and this is INTENTIONAL
handles_via => 'Array',
handles => {
"_get_$type" => 'get'
},
default => sub {
$_[0]->_build_impl_sub(0, @names)
}
);
has $_type_wrapper_call_lazy_builder => (is => 'ro', isa => ArrayRef[CodeRef|Undef],
# lazy => 1, Not lazy and this is INTENTIONAL
handles_via => 'Array',
handles => {
"_get_${type}_call_lazy_builder" => 'get'
},
default => sub {
$_[0]->_build_impl_sub(1, @names)
}
);
}
# =============================================================================
# Internal instance methods
# =============================================================================
sub _build_impl_sub {
my ($self, $call_builder, @names) = @_;
my @array = ();
foreach my $name (@names) {
my $exists = "exists_$name";
my $getter = "get_$name";
#
# We KNOW in advance that we are talking with a hash. So no need to
# to do extra calls. The $exists and $getter variables are intended
# for the outside world.
# The inlined version using these accessors is:
my $inlined_call_lazy_builder = <<INLINED_CALL_LAZY_BUILDER;
# my (\$self, \$criteria, \$value) = \@_;
#
# This is intentionnaly doing NOTHING, but call the builders -;
#
\$_[0]->$exists(\$_[1])
INLINED_CALL_LAZY_BUILDER
# The inlined version using direct perl op is:
my $inlined_without_accessors = <<INLINED_WITHOUT_ACCESSORS;
# my (\$self, \$criteria, \$value) = \@_;
#
# At run-time, in particular Protocol-based normalizers,
# the callbacks can be altered
#
exists(\$_[0]->{$name}->{\$_[1]}) ? goto \$_[0]->{$name}->{\$_[1]} : \$_[2]
INLINED_WITHOUT_ACCESSORS
if ($call_builder) {
push(@array,eval "sub {$inlined_call_lazy_builder}") ## no critic
} else {
push(@array,eval "sub {$inlined_without_accessors}") ## no critic
}
}
\@array
}
BEGIN {
#
# Marpa internal optimisation: we do not want the closures to be rechecked every time
# we call $r->value(). This is a static information, although determined at run-time
# the first time $r->value() is called on a recognizer.
#
no warnings 'redefine';
sub Marpa::R2::Recognizer::registrations {
my $recce = shift;
( run in 2.293 seconds using v1.01-cache-2.11-cpan-39bf76dae61 )