/usr/local/CPAN/hub-standard/Hub/Parse/StandardParser.pm
package Hub::Parse::StandardParser;
use strict;
use Hub qw/:lib/;
push our @ISA, qw(Hub::Parse::Parser);
our %EVALUATORS;
sub get_evaluator {
return defined $EVALUATORS{$_[1]}
? $EVALUATORS{$_[1]}
: &Hub::Parse::Parser::get_evaluator(@_);
}
$EVALUATORS{'into'} = sub {
my ($self, $params, $result) = @_;
my ($outer_str, $fields, $pos, $text, $parents, $valdata) = @$params;
my ($opts, %directive) = Hub::opts($fields);
$result->{'value'} = '';
push @$parents, \%directive;
};
$EVALUATORS{'use'} = sub {
my ($self, $params, $result) = @_;
my ($outer_str, $fields, $pos, $text, $parents, $valdata) = @$params;
my ($opts, %directive) = Hub::hashopts($fields);
$result->{'value'} = '';
my $h = $self->get_value($directive{'use'}, $valdata, $fields);
unless (ref($h)) {
warn "Cannot use item '$h'" . $self->get_hint($$pos, $text)
if $$Hub{'/sys/ENV/DEBUG'};
}
$h = { $directive{'as'} => $h } if $directive{'as'};
unshift @$valdata, $h;
};
$EVALUATORS{'define'} = sub {
my ($self, $params, $result) = @_;
my ($outer_str, $fields, $pos, $text, $parents, $valdata) = @$params;
my ($opts, %directive) = Hub::hashopts($fields);
$result->{'value'} = '';
my $varname = $directive{'define'};
my ($end_p, $block) =
$self->_get_block($$pos + length($outer_str), $text, 'define');
$$result{'width'} = $end_p - $$pos;
$directive{'as'} ||= 'HASH';
my $data = ();
if ($directive{'as'} =~ /^(DATA|HASH)$/i) {
$data = Hub::hparse($block);
} elsif ($directive{'as'} =~ /^(LIST|ARRAY)$/i) {
$data = Hub::hparse($block, -as_array => 1);
} elsif ($directive{'as'} =~ /^(TEXT|SCALAR)$/i) {
$data = $block;
}
push @$valdata, defined $varname ? {$varname, $data} : $data;
};
$EVALUATORS{'if'} = sub {
my ($self, $params, $result) = @_;
my ($outer_str, $fields, $pos, $text, $parents, $valdata) = @$params;
$result->{'value'} = '';
my ($opts, @params) = Hub::opts($fields);
shift @params; # drop 'if' part
my $true = 0; # default to false
if (defined $params[2]) {
# This is a two-part evaluation
my $l = $self->get_value($params[0], \@$valdata);
my $r = $self->get_value($params[2], \@$valdata);
if (defined $l && defined $r) {
$true = Hub::compare($params[1], $l, $r);
} elsif (!defined $l && !defined $r) {
$true = 1;
}
} else {
# This boolean condition
my $v = $self->get_value($params[0], \@$valdata);
$true = defined $v
? Hub::is_bipolar($v)
? 1
: isa($v, 'ARRAY')
? @$v
: isa($v, 'HASH')
? scalar(keys %$v)
: ref($v) eq 'SCALAR'
? $$v
: $v
: 0;
}
my ($end_p, $block) =
$self->_get_block($$pos + length($outer_str), $text, 'if');
$$result{'width'} = $end_p - $$pos;
my ($if,$else) = $self->_split_if_else($block);
# Logical not
$true = !$true if $$opts{'not'};
# Replace block with logical portion
$$result{'value'} = $true ? $if : $else;
};
$EVALUATORS{'foreach'} = sub {
my ($self, $params, $result) = @_;
my ($outer_str, $fields, $pos, $text, $parents, $valdata) = @$params;
# Parse parameters (we delete the internally removed parameters
# so that the others may be passed to get_value.)
my ($opts, %directive) = Hub::opts($fields);
$result->{'value'} = '';
my $varname = $directive{'foreach'};
delete $directive{'foreach'};
die "Missing variable name parameter" . $self->get_hint($$pos, $text)
unless defined $varname;
my $in = $directive{'in'};
delete $directive{'in'};
die "Missing 'in' parameter" . $self->get_hint($$pos, $text)
unless defined $in;
my $sort = $$opts{'sort'} || 0;
my ($end_p, $block) =
$self->_get_block($$pos + length($outer_str), $text, 'foreach');
$$result{'width'} = $end_p - $$pos;
# Get the data for the sub-template
my $data = $self->get_value($in, \@$valdata, $fields);
if (defined $data && !ref($data)) {
if ($$opts{'split'}) {
$data = [split($$opts{'split'}, $data)];
} elsif ($$opts{'split_hash'}) {
my %hash_data = split($$opts{'split_hash'}, $data);
$data = \%hash_data;
}
}
my @items = ();
if (isa($data, 'HASH')) {
my @keys = keys %$data;
if ($sort) {
my $comparator = $sort eq '1' ? 'cmp' : $sort;
@keys = sort {
Hub::sort_compare($comparator, $self->_to_string($a), $self->_to_string($b))
} keys %$data;
}
@keys = grep { substr($_, 0, 1) ne '.' } @keys;
for (@keys) {
push @items, {
$varname => {
'name' => $self->_to_string($_),
'value' => $$data{$_},
}
}
}
} elsif (isa($data, 'ARRAY')) {
my $comparator = $sort eq '1' ? 'cmp' : $sort;
for ($sort ? sort {
Hub::sort_compare($comparator, $self->_to_string($a), $self->_to_string($b))
} @$data : @$data) {
push @items, { $varname => $self->_to_string($_), };
}
} elsif (defined $data) {
push @items, { $varname => $data, };
}
# Populate the sub-template for each datum
my $idx = 0;
my @text_results = ();
foreach my $item (@items) {
my $item_text = $self->_populate(-text => $block,
$item, @$valdata, {
'.idx' => $idx,
'.num' => ($idx + 1),
'.total' => scalar(@items),
'.pen' => $#items, # penultimate
});
$self->{'*depth'}--; # our call to _populate is not stepping deeper
push @text_results, $item_text
if defined $item_text && ref($item_text) eq 'SCALAR';
$idx++;
}
my $num_joins = 0;
for(my $i = 0; $i < @text_results; $i++) {
my $item_text = $text_results[$i];
if ($$item_text) {
if ($$opts{'joint'} && $num_joins > 0) {
$$result{'value'} .= $$opts{'joint'};
}
$$result{'value'} .= $$item_text;
$num_joins++;
}
}
# This worked everywhere, but with the foreach loop on
# dev.livesite.net/custom-fonts. The idea is to skip
# over the entire foreach section after it is parsed so that
# it doesn't get reparsed.
# $$result{'goto'} = $$pos + length($$result{'value'});
};
$EVALUATORS{'end'} = sub {
my ($self, $params, $result) = @_;
my ($outer_str, $fields, $pos, $text, $parents, $valdata) = @$params;
my ($opts, %directive) = Hub::opts($fields);
$result->{'value'} = '';
shift @$valdata if $directive{'end'} eq 'use';
};
1;