From Code to Community: Sponsoring The Perl and Raku Conference 2025 Learn more

use strict;
use Scalar::Util ('blessed');
sub new
{
my $class = shift;
my $desc = shift;
my $this =
{
'DESCRIPTION' => $desc,
'POSSIBILITIES' => {},
};
bless $this, $class;
return $this;
}
sub new_from_scalar
{
my $class = shift;
my $value = shift;
my $this = $class->new("scalar($value)");
$this->add_possibility('SCALAR', $value);
return $this;
}
sub add_possibility
{
my $this = shift;
my $type = shift;
my $value = shift;
if($type eq 'SCALAR')
{
if(blessed($value) && $value->isa('App::RecordStream::DomainLanguage::Snippet'))
{
return $this->add_possibility('SNIPPET', $value);
}
}
push @{$this->{'POSSIBILITIES'}->{$type} ||= []}, $value;
# sketchy upgrade...
if($type eq "SCALAR")
{
$this->add_possibility('VALUATION', App::RecordStream::DomainLanguage::Valuation::KeySpec->new($value));
$this->add_possibility('SNIPPET', App::RecordStream::DomainLanguage::Snippet->new($value));
}
}
sub get_possibilities
{
my $this = shift;
my $type = shift;
return @{$this->{'POSSIBILITIES'}->{$type} || []};
}
sub get_possible_pairs
{
my $this = shift;
my @ret;
for my $type (keys(%{$this->{'POSSIBILITIES'}}))
{
for my $value (@{$this->{'POSSIBILITIES'}->{$type}})
{
push @ret, [$type, $value];
}
}
return @ret;
}
sub get_description
{
my $this = shift;
return $this->{'DESCRIPTION'};
}
sub _force
{
my $this = shift;
my $type = shift;
my $ar = $this->{'POSSIBILITIES'}->{$type} || [];
my $ct = @$ar;
if($ct != 1)
{
die "Cannot use '" . $this->{'DESCRIPTION'} . "' as $type, $ct possibilities";
}
return $ar->[0];
}
# TODO: amling, fake isa?
# We can pretend to be an aggregator in a pinch
sub initial
{
return shift->_force('AGGREGATOR')->initial(@_);
}
sub combine
{
return shift->_force('AGGREGATOR')->combine(@_);
}
sub squish
{
return shift->_force('AGGREGATOR')->squish(@_);
}
# We can pretend to be a deaggregator in a pinch
sub deaggregate
{
return shift->_force('DEAGGREGATOR')->deaggregate(@_);
}
# We can pretend to be a clumper in a pinch
sub clumper_begin
{
return shift->_force('CLUMPER')->clumper_begin(@_);
}
sub clumper_push_record
{
return shift->_force('CLUMPER')->clumper_push_record(@_);
}
sub clumper_end
{
return shift->_force('CLUMPER')->clumper_end(@_);
}
# We can pretend to be a valuation in a pinch
sub evaluate_record
{
return shift->_force('VALUATION')->evaluate_record(@_);
}
sub cast_or_die
{
my $type = shift;
my $obj = shift;
if($type eq 'AGGREGATOR')
{
return cast_agg_or_die($obj);
}
elsif($type eq 'DEAGGREGATOR')
{
return cast_deagg_or_die($obj);
}
elsif($type eq 'CLUMPER')
{
return cast_clumper_or_die($obj);
}
elsif($type eq 'VALUATION')
{
return cast_valuation_or_die($obj);
}
elsif($type eq 'SCALAR')
{
return cast_scalar_or_die($obj);
}
else
{
die "Bad type $type?";
}
}
sub cast_valuation_or_die
{
my $obj = shift;
if(ref($obj) && ref($obj) eq "CODE")
{
return App::RecordStream::DomainLanguage::Valuation::Sub->new($obj);
}
if(blessed($obj) && $obj->isa('App::RecordStream::DomainLanguage::Value'))
{
my @val = $obj->get_possibilities('VALUATION');
if(@val > 1)
{
die "Multiple valuations for " . $obj->get_description();
}
if(@val == 1)
{
return $val[0];
}
}
if(blessed($obj) && $obj->isa('App::RecordStream::Aggregator::Aggregation'))
{
die "Aggregation found where valuation expected";
}
if(blessed($obj) && $obj->isa('App::RecordStream::DomainLanguage::Valuation'))
{
return $obj;
}
die "Unknown found where valuation expected";
}
sub cast_agg_or_die
{
my $obj = shift;
if(blessed($obj) && $obj->isa('App::RecordStream::DomainLanguage::Value'))
{
my @agg = $obj->get_possibilities('AGGREGATOR');
if(@agg > 1)
{
die "Multiple aggregators for " . $obj->get_description();
}
if(@agg == 1)
{
return $agg[0];
}
my @scalar = $obj->get_possibilities('SCALAR');
if(@scalar > 1)
{
die "No aggregators and multiple scalars for " . $obj->get_description();
}
if(@scalar == 1)
{
$obj = $scalar[0];
}
else
{
die "No usable possibilities for " . $obj->get_description();
}
}
if(blessed($obj) && $obj->isa('App::RecordStream::Aggregator::Aggregation'))
{
return $obj;
}
if(blessed($obj) && $obj->isa('App::RecordStream::DomainLanguage::Valuation'))
{
die "Valuation found where aggregator expected";
}
# running out of ideas here
return App::RecordStream::Aggregator::Internal::Constant->new($obj);
}
sub cast_deagg_or_die
{
my $obj = shift;
if(blessed($obj) && $obj->isa('App::RecordStream::DomainLanguage::Value'))
{
my @deagg = $obj->get_possibilities('DEAGGREGATOR');
if(@deagg > 1)
{
die "Multiple deaggregators for " . $obj->get_description();
}
if(@deagg == 1)
{
return $deagg[0];
}
die "No usable possibilities for " . $obj->get_description();
}
if(blessed($obj) && $obj->isa('App::RecordStream::Deaggregator::Base'))
{
return $obj;
}
my $s = "unknown";
if(blessed($obj))
{
$s = ref($obj);
}
die "Could not turn $s into a deaggregator";
}
sub cast_clumper_or_die
{
my $obj = shift;
if(blessed($obj) && $obj->isa('App::RecordStream::DomainLanguage::Value'))
{
my @clumper = $obj->get_possibilities('CLUMPER');
if(@clumper > 1)
{
die "Multiple clumpers for " . $obj->get_description();
}
if(@clumper == 1)
{
return $clumper[0];
}
die "No usable possibilities for " . $obj->get_description();
}
if(blessed($obj) && $obj->isa('App::RecordStream::Clumper::Base'))
{
return $obj;
}
my $s = "unknown";
if(blessed($obj))
{
$s = ref($obj);
}
die "Could not turn $s into a clumper";
}
sub cast_scalar_or_die
{
my $obj = shift;
if(blessed($obj) && $obj->isa('App::RecordStream::DomainLanguage::Value'))
{
my @scalar = $obj->get_possibilities('SCALAR');
if(@scalar > 1)
{
die "Multiple scalar values for " . $obj->get_description();
}
if(@scalar == 1)
{
return $scalar[0];
}
die "No scalar possibilities for " . $obj->get_description();
}
if(blessed($obj) && $obj->isa('App::RecordStream::Aggregator::Aggregation'))
{
die "Aggregator found where scalar expected";
}
if(blessed($obj) && $obj->isa('App::RecordStream::DomainLanguage::Valuation'))
{
die "Valuation found where scalar expected";
}
return $obj;
}
1;