FunctionalPerl

 view release on metacpan or  search on metacpan

lib/PXML/Util.pm  view on Meta::CPAN


sub pxml_deferred_map {
    @_ >= 2 and @_ <= 4 or fp_croak_arity "2-4";

    # Elementfn receives (element, up-list, inferior-map), where
    # up-list is a linked list to the parents and inferior-map is a
    # function of one argument that will do the same as the
    # pxml_deferred_map call (but keep the up-list).  Otherfn is
    # called for leafs (non-sequences) and receives (value, up-list).

    my ($v, $elementfn, $maybe_otherfn, $maybe_uplist) = @_;

    my $make_inferior_map = sub {
        my ($uplist) = @_;
        sub {
            pxml_deferred_map($_[0], $elementfn, $maybe_otherfn, $uplist)
        }
    };

    my $uplist = $maybe_uplist // null;

    if (my $r = ref $v) {
        my $uplist2 = cons $v, $uplist;
        if (blessed($v) and $v->isa("PXML::Element")) {

            # XX TCO?
            &$elementfn($v, $uplist, &$make_inferior_map($uplist2))
        } else {
            stream_map(&$make_inferior_map($uplist), stream_mixed_flatten($v))
        }
    } else {
        $maybe_otherfn ? &$maybe_otherfn($v, $uplist) : $v
    }
}

# 'Eager' mapping, meaning, the body is mapped already when $elementfn
# receives an element.

sub pxml_eager_map {
    @_ >= 2 and @_ <= 4 or fp_croak_arity "2-4";

    # The functions receive (value, up-list), where up-list is a
    # linked list to the parents
    my ($v, $elementfn, $maybe_otherfn, $maybe_uplist) = @_;

    pxml_deferred_map(
        $v,
        sub {
            my ($e, $uplist, $inferior_map) = @_;

            # XX TCO?
            &$elementfn($e->body_map($inferior_map), $uplist)
        },
        $maybe_otherfn,
        $maybe_uplist
    )
}

sub t_data {
    require PXML::XHTML;
    import PXML::XHTML qw(P B A CODE);
    P("foo", B("bar", undef, stream_iota(5)->take(4)))
}

TEST { t_data->string }
'<p>foo<b>bar5678</b></p>';

TEST {
    pxml_eager_map(
        t_data,
        sub {
            $_[0]->name_update(sub { $_[0] . "A" })
        },
        sub { ($_[0] // "-") . "." }
        )->string
}
'<pA>foo.<bA>bar.-.5.6.7.8.</bA></pA>';

use FP::Ops "the_method";

sub uplist_show {
    my ($uplist) = @_;
    "[" . $uplist->map (the_method("name"))->strings_join("|") . "]"
}

TEST {
    pxml_eager_map(
        t_data,
        sub {
            my ($e, $uplist) = @_;
            $e->body_update(
                sub {
                    cons(uplist_show($uplist), $_[0])
                }
            )
        },
        sub {
            my ($v, $uplist) = @_;
            uplist_show($uplist) . ($v // "-") . "."
        }
        )->string
}
'<p>[][p]foo.<b>[p][b|p]bar.[b|p]-.[b|p]5.[b|p]6.[b|p]7.[b|p]8.</b></p>';

TEST {
    pxml_deferred_map(
        t_data,
        sub {
            my ($e, $uplist, $inferior_map) = @_;
            $e->body_update(
                sub {
                    my ($body) = @_;
                    cons(
                        uplist_show($uplist),
                        $e->name eq "b"
                        ? do {
                            my $s = stream_mixed_flatten $body;
                            cons(&$inferior_map(car $s), cdr $s )
                            }
                        : &$inferior_map($body)
                    )



( run in 0.834 second using v1.01-cache-2.11-cpan-354807fb38d )