use v5.14; use warnings; =head1 NAME Attean::API::Parser - Parser role =head1 VERSION This document describes Attean::API::Parser version 0.035 =head1 DESCRIPTION The Attean::API::Parser role defines a common API for all parsers of typed objects from data (either a byte string or a filehandle). =head1 ATTRIBUTES The following attributes exist: =over 4 =item C<< handler >> A code reference that will be called during callback-variant parsing methods. This attribute has a default (no-op function), so specifying it is not necessary if using iterator- or list-variant parsing methods. =back =head1 REQUIRED METHODS The following methods are required by the L role: =over 4 =item C<< canonical_media_type >> Returns the canonical media type string for the format of this parser. =item C<< media_types >> Returns an ARRAY reference of media type strings that are acceptable as input to this parser. =item C<< handled_type >> Returns a L object representing the type of items that result from parsing. =item C<< file_extensions >> Returns an ARRAY reference of file extensions commonly associated with the media types supported by the parser (and returned by C<< media_types >>). File extensions should NOT include a leading dot. =cut use Type::Tiny::Role; package Attean::API::Parser 0.035 { use Types::Standard qw(CodeRef Bool); use Moo::Role; use Scalar::Util qw(blessed); use namespace::clean; has 'handler' => (is => 'rw', isa => CodeRef, default => sub { sub {} }); has 'lazy_iris' => (is => 'rw', isa => Bool, default => 0); requires 'canonical_media_type'; # => (is => 'ro', isa => 'Str', init_arg => undef); requires 'media_types'; # => (is => 'ro', isa => 'ArrayRef[Str]', init_arg => undef); requires 'handled_type'; # => (is => 'ro', isa => 'Type::Tiny', init_arg => undef); requires 'file_extensions'; # => (is => 'ro', isa => 'ArrayRef[Str]', init_arg => undef); =item C<< new_iri( value => $value ) >> Constructs and returns a new L object, respecting the parser's C attribute. =cut sub new_iri { my $self = shift; my %args; if ($self->lazy_iris) { $args{lazy} = 1; } else { $args{lazy} = 0; } if (scalar(@_) == 1) { $args{value} = shift; } else { %args = (%args, @_); } return Attean::IRI->new(%args); } =item C<< new_literal( value => $value, [ datatype => $dt, ] [ language => $lang ]) >> Constructs and returns a new L object. =cut sub new_literal { my $self = shift; my %args = @_; return Attean::Literal->new(%args); } sub file_extensions { return [] } } package Attean::API::AbbreviatingParser 0.035 { use Types::Standard qw(ConsumerOf InstanceOf Maybe); use Types::Namespace qw( NamespaceMap ); use Scalar::Util qw(blessed); use Moo::Role; with 'Attean::API::Parser'; has 'base' => (is => 'rw', isa => ConsumerOf['Attean::API::IRI'], coerce => sub { blessed($_[0]) ? Attean::IRI->new($_[0]->as_string) : Attean::IRI->new($_[0]) }, predicate => 'has_base'); has 'namespaces' => (is => 'ro', isa => Maybe[NamespaceMap]); } package Attean::API::CDTBlankNodeMappingParser 0.035 { use Types::Standard qw(HashRef ConsumerOf InstanceOf Maybe Bool); use Scalar::Util qw(blessed); use UUID::Tiny ':std'; use Data::Dumper; use Moo::Role; with 'Attean::API::Parser'; has 'blank_nodes' => (is => 'ro', isa => Maybe[HashRef[ConsumerOf['Attean::API::Blank']]]); has 'parse_id' => (is => 'rw', default => sub { unpack('H*', create_uuid()) }); has 'enable_cdt_rewriting' => (is => 'rw', isa => Bool, default => 1); foreach my $method (qw(parse_iter_from_io parse_iter_from_bytes parse_cb_from_io parse_cb_from_bytes)) { around $method => sub { my $orig = shift; my $self = $_[0]; $self->parse_id(unpack('H*', create_uuid())); my $term = $orig->(@_); return $term; }; } around 'new_literal' => sub { my $orig = shift; my $self = $_[0]; my $term = $orig->(@_); if ($self->enable_cdt_rewriting) { my $dt = $term->datatype(); if (blessed($dt) and ($dt->value eq 'http://w3id.org/awslabs/neptune/SPARQL-CDTs/Map' or $dt->value eq 'http://w3id.org/awslabs/neptune/SPARQL-CDTs/List')) { return AtteanX::Functions::CompositeLists::rewrite_lexical($term, $self->blank_nodes, $self->parse_id); } } return $term; }; } package Attean::API::PushParser 0.035 { use Moo::Role; with 'Attean::API::Parser'; requires 'parse_cb_from_io'; # parse_cb_from_io($io) requires 'parse_cb_from_bytes'; # parse_cb_from_bytes($data) sub parse_iter_from_io { my $self = shift; my @values = $self->parse_list_from_io(@_); if ($self->does('Attean::API::ResultParser') or $self->does('Attean::API::ResultOrTermParser')) { my %vars; foreach my $r (@values) { if ($r->does('Attean::API::Result')) { foreach my $v ($r->variables) { $vars{$v}++; } } } return Attean::ListIterator->new( variables => [keys %vars], values => \@values, item_type => $self->handled_type->role, ); } else { return Attean::ListIterator->new( values => \@values, item_type => $self->handled_type->role, ); } } sub parse_iter_from_bytes { my $self = shift; my @values = $self->parse_list_from_bytes(@_); if ($self->does('Attean::API::ResultParser') or $self->does('Attean::API::ResultOrTermParser')) { my %vars; foreach my $r (@values) { if ($r->does('Attean::API::Result')) { foreach my $v ($r->variables) { $vars{$v}++; } } } return Attean::ListIterator->new( variables => [keys %vars], values => \@values, item_type => $self->handled_type->role, ); } else { return Attean::ListIterator->new( values => \@values, item_type => $self->handled_type->role, ); } } sub parse_list_from_io { my $self = shift; my @values; $self->handler(sub { push(@values, shift); }); $self->parse_cb_from_io(@_); return @values; } sub parse_list_from_bytes { my $self = shift; my @values; $self->handler(sub { push(@values, shift); }); $self->parse_cb_from_bytes(@_); return @values; } } package Attean::API::PullParser 0.035 { use Moo::Role; with 'Attean::API::Parser'; requires 'parse_iter_from_io'; # $iter = parse_iter_from_io($io) requires 'parse_iter_from_bytes'; # $iter = parse_iter_from_bytes($data) sub parse_cb_from_io { my $self = shift; my $io = shift; my $handler = $self->handler; my $iter = $self->parse_iter_from_io($io); while (my $item = $iter->next) { $handler->( $item ) } } sub parse_cb_from_bytes { my $self = shift; my $data = shift; my $handler = $self->handler; my $iter = $self->parse_iter_from_bytes($data); while (defined(my $item = $iter->next)) { $handler->( $item ) } } sub parse_list_from_io { my $self = shift; my $io = shift; my $iter = $self->parse_iter_from_io($io); my @list; while (defined(my $item = $iter->next)) { push(@list, $item); } return @list; } sub parse_list_from_bytes { my $self = shift; my $data = shift; my $iter = $self->parse_iter_from_bytes($data); my @list; while (defined(my $item = $iter->next)) { push(@list, $item); } return @list; } } package Attean::API::AtOnceParser 0.035 { use Moo::Role; with 'Attean::API::Parser'; requires 'parse_list_from_io'; # @list = parse_list_from_io($io) requires 'parse_list_from_bytes'; # @list = parse_list_from_bytes($data) sub parse_cb_from_io { my $self = shift; my $io = shift; my $handler = $self->handler; my $iter = $self->parse_iter_from_io($io); while (my $item = $iter->next) { $handler->( $item ) } } sub parse_cb_from_bytes { my $self = shift; my $data = shift; my $handler = $self->handler; my $iter = $self->parse_iter_from_bytes($data); while (defined(my $item = $iter->next)) { $handler->( $item ) } } sub parse_iter_from_io { my $self = shift; my @values = $self->parse_list_from_io(@_); if ($self->does('Attean::API::ResultParser') or $self->does('Attean::API::ResultOrTermParser')) { my %vars; foreach my $r (@values) { if ($r->does('Attean::API::Result')) { foreach my $v ($r->variables) { $vars{$v}++; } } } return Attean::ListIterator->new( variables => [keys %vars], values => \@values, item_type => $self->handled_type->role, ); } else { return Attean::ListIterator->new( values => \@values, item_type => $self->handled_type->role, ); } } sub parse_iter_from_bytes { my $self = shift; my @values = $self->parse_list_from_bytes(@_); if ($self->does('Attean::API::ResultParser') or $self->does('Attean::API::ResultOrTermParser')) { my %vars; foreach my $r (@values) { if ($r->does('Attean::API::Result')) { foreach my $v ($r->variables) { $vars{$v}++; } } } return Attean::ListIterator->new( variables => [keys %vars], values => \@values, item_type => $self->handled_type->role, ); } else { return Attean::ListIterator->new( values => \@values, item_type => $self->handled_type->role, ); } } } package Attean::API::TermParser 0.035 { # Parser returns objects that conform to Attean::API::Term use Moo::Role; with 'Attean::API::Parser'; sub handled_type { state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::Term'); return $ITEM_TYPE; } } package Attean::API::TripleParser 0.035 { # Parser returns objects that conform to Attean::API::Triple use Moo::Role; with 'Attean::API::Parser'; sub handled_type { state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::Triple'); return $ITEM_TYPE; } } package Attean::API::QuadParser 0.035 { # Parser returns objects that conform to Attean::API::Quad use Moo::Role; with 'Attean::API::Parser'; sub handled_type { state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::Quad'); return $ITEM_TYPE; } } package Attean::API::MixedStatementParser 0.035 { # Parser returns objects that conform to either Attean::API::Triple or Attean::API::Quad use Moo::Role; with 'Attean::API::Parser'; sub handled_type { state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::TripleOrQuad'); return $ITEM_TYPE; } } package Attean::API::ResultOrTermParser 0.035 { # Parser returns objects that conform to either Attean::API::Result or Attean::API::Term use Moo::Role; with 'Attean::API::Parser'; sub handled_type { state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::ResultOrTerm'); return $ITEM_TYPE; } } package Attean::API::ResultParser 0.035 { # Parser returns objects that conform to Attean::API::Result use Moo::Role; with 'Attean::API::Parser'; sub handled_type { state $ITEM_TYPE = Type::Tiny::Role->new(role => 'Attean::API::Result'); return $ITEM_TYPE; } } 1; __END__ =back =head1 BUGS Please report any bugs or feature requests to through the GitHub web interface at L. =head1 SEE ALSO =head1 AUTHOR Gregory Todd Williams C<< >> =head1 COPYRIGHT Copyright (c) 2014--2022 Gregory Todd Williams. This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself. =cut