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

use strict;
sub new
{
my $class = shift;
my $code = shift;
my $vars = shift;
$code = App::RecordStream::Executor->transform_code($code);
$code = _transform_angles($code);
my $this =
{
'CODE' => $code,
'VARS' => $vars,
};
bless $this, $class;
return $this;
}
sub evaluate_as
{
my $this = shift;
my $type = shift;
my $vars = shift || {};
my $executor = App::RecordStream::DomainLanguage::Executor->new();
$executor->import_registry();
for my $var (%{$this->{'VARS'}})
{
for my $ref (@{$this->{'VARS'}->{$var}})
{
$executor->set_ref($var, $ref);
}
}
for my $var (keys(%$vars))
{
if(0)
{
}
elsif($var =~ /^\$(.*)$/)
{
$executor->set_scalar($1, $vars->{$var});
}
else
{
die "Bad var for snippet: '$var'";
}
}
my $result = $executor->exec($this->{'CODE'});
return App::RecordStream::DomainLanguage::Value::cast_or_die($type, $result);
}
sub _transform_angles
{
my $code = shift;
my $pos = 0;
my $out = '';
while(1)
{
my $top_level_entrance = index($code, '<<', $pos);
if($top_level_entrance == -1)
{
$out .= substr($code, $pos);
last;
}
my $level = 1;
my $pos2 = $top_level_entrance + 2;
my $top_level_exit;
while(1)
{
my $next_enter = index($code, '<<', $pos2);
my $next_exit = index($code, '>>', $pos2);
if($next_enter != -1 && ($next_exit == -1 || $next_enter < $next_exit))
{
++$level;
$pos2 = $next_enter + 2;
next;
}
if($next_exit != -1 && ($next_enter == -1 || $next_exit < $next_enter))
{
--$level;
if($level == 0)
{
$top_level_exit = $next_exit;
last;
}
$pos2 = $next_enter + 2;
next;
}
die "Unbalanced << and >> in snippet: $code";
}
$out .= substr($code, $pos, $top_level_entrance - $pos);
$out .= _quote_snippet(substr($code, $top_level_entrance + 2, $top_level_exit - $top_level_entrance - 2));
$pos = $top_level_exit + 2;
}
return $out;
}
sub _quote_snippet
{
my $code = shift;
my @vars;
if($code =~ s/^([a-zA-Z_][a-zA-Z_0-9]*(,[a-zA-Z_][a-zA-Z_0-9]*)*)\|//)
{
@vars = split(/,/, $1);
}
# Could not get typeglobs to work. References go in, references come out,
# you can't explain that...
return "snip(App::RecordStream::DomainLanguage::Snippet->new('$code', {" . join(", ", map { "'$_' => [\\\$$_, \\\@$_, \\\%$_]" } @vars) . "}))";
}
1;