Perl6-Perldoc

 view release on metacpan or  search on metacpan

lib/Perl6/Perldoc/Parser.pm  view on Meta::CPAN

        if ($block->{typename} eq '(use)') {
            my $source = $block->{source};
            if (eval "require $source") {
                my %options = (
                    %{ $block->{config}{use}||{} },
                    %{ $block->{options} || {} },
                );
                $source->import(\%options);
            }
            else {
                _err_use_cant_load($source, $line_num, \@errors);
                next TOKEN;
            }
        }

        push @{ $stack[-1]{content} }, $block;

        ### Implicitly terminated block: $block
    }

    # Apply global processing to root of data structure...
    my $root = pop(@stack);

    # Number all numbered blocks...
    my $state_ref = { errors => \@errors };
    _resolve_numbering($root, $state_ref);

    # Convert internal hash-based representation to objects...
    my $tree = _create_objects($root, $state_ref);

    # Build and install any tables-of-content for P<toc:...> codes...
    TOC:
    for my $toc_placement_obj (@toc_placements) {
        next TOC if $toc_placement_obj->{content}[0] !~ m{\A \s* toc:}xms;

        # Replace P<toc:...>'s contents with TOC...
        $toc_placement_obj->{content}
            = [ _build_toc($tree, $toc_placement_obj) ];

        # Set flag to ignore this node on subsequent TOC-building passes...
        $toc_placement_obj->{ignore_toc} = 1;
    }

    # Aggregrate and return information in an object...
    return bless {
        tree     => $tree,
        errors   => \@errors,
        warnings => \@warnings,
    }, 'Perl6::Perldoc::Parser::ReturnVal';

}

# Build the table of contents for a given P<toc:> request...
sub _build_toc {
    my ($data_structure, $placement_obj) = @_;

    # Work out what's in the TOC (including the =item/=item1 alias)...
    my $requested_types = $placement_obj->{target};
       $requested_types =~ s{\A \s* toc: \s*}{}xms;
    my %toc_wants; 
       @toc_wants{ split m/\s+/, $requested_types } = ();
    if (exists $toc_wants{item} || exists $toc_wants{item1}) {
       @toc_wants{qw< item item1 >} = ();
    }

    # Build flat list of tocitems into nested toclists...
    my @toc_stack = [];
    for my $toc_entry ( _walk_toc($data_structure, \%toc_wants) ) {
        my $level = $toc_entry->{level};

        # Increase nesting for higher numbered items...
        while ($level > @toc_stack) {
            push @toc_stack, [];
        }
        # Decrease nesting for lower numbered items...
        while ($level < @toc_stack) {
            my $content = pop @toc_stack;
            push @{ $toc_stack[-1] }, Perl6::Perldoc::Block::toclist->new({
                typename => 'toclist',
                style    => 'implicit',
                content  => $content,
                range    => {},
            });
        }
        # Insert the item into the hierarchy...
        push @{ $toc_stack[-1] }, $toc_entry;
    }

    # Nest any unclosed lists...
    while (@toc_stack > 1) {
        my $content = pop @toc_stack;
        push @{ $toc_stack[-1] }, Perl6::Perldoc::Block::toclist->new({
            typename => 'toclist',
            style    => 'implicit',
            content  => $content,
            range    => {},
        });
    }

    # Retrieve a flat list of tocitem blocks representing the TOC...
    return @{ $toc_stack[-1] };
}

# Blocks without an inherent nesting level default to this nesting...
my $DEFAULT_LEVEL = 5;

# Walk DOM tree extracting blocks specified to be part of TOC...
use Scalar::Util qw< reftype >;
sub _walk_toc {
    my ($node, $wanted_ref) = @_;

    my $node_type = reftype($node) || q{};

    # Hashes are nodes: check if this one (and its subnodes) should be included
    if ($node_type eq 'HASH') {
        return if $node->{ignore_toc};

        my $node_class = $node->{typename};
        my @this_node;

        # Is this node part of the TOC?

lib/Perl6/Perldoc/Parser.pm  view on Meta::CPAN

            my ($top)    = splice(@{$pre_sep}, 0, 2);
            my ($bottom) = splice(@{$post_sep}, 0, 2);

            my $content = join("\n", @{$cell});

            # Remove common horizontal whitespace prefix...
            if ($content =~ m{\A ([^\S\n]+)}xms) {
                my $prefix = $1;
                $content =~ s{^$prefix}{}gms;  # No /x so whitespace significant
            }

            open my $fh, '<', \$content
                or die "Internal error: could not parse table content";

            # Recursively parse content as Pod...
            $content
                = Perl6::Perldoc::Parser->parse($fh, {
                        all_pod=>1,
                        allow=>$allow_ref,
                        config_stack=>$config_stack_ref,
                  })->{tree}->{content};

            # Add cell to list for row...
            push @cell_objs, bless {
                content => $content,
                left    => join("\n", @{$left_sep}),
                right   => join("\n", @{$right_sep}),
                top     => $top,
                bottom  => $bottom,
                header  => $has_head && $row_index == 0,
            }, 'Perl6::Perldoc::Block::table::Cell';

            # Move left (right separator becomes left separator)
            $left_sep = $right_sep;
        }

        # Add the new row object...
        push @rows, bless {
             cells => \@cell_objs,
        }, 'Perl6::Perldoc::Block::table::Row';

        # Move downwards...
        $pre_sep = $post_sep;
    }

    return \@rows;
}

# Build entire table...
sub _build_table {
    my ($text, $allow_ref, $config_stack_ref) = @_;

    # Remove surrounding blank lines...
    $text =~ s{\A ($HWS* \n)+ | (^ $HWS* \n?)+ \z}{}gxms;

    # Remove top/bottom border...
    $text =~ s{\A ($ROW_SEP_LINE)}{}xms;     my $top_sep    = $1 || q{};
    $text =~ s{\n ($ROW_SEP_LINE) \Z}{}xms;  my $bottom_sep = $1 || q{};

    # Decompose into separated rows...
    my ($first_row, $first_sep, @rest) = split m{($ROW_SEP_LINE)}xms, $text;
    my $has_head = @rest != 0 && $first_sep =~ $NWS_ROW_SEP;

    my @rows = @rest == 0 ? (split m{(\n)}xms, $text)
             : @rest == 1 && !$bottom_sep ?
                            ($first_row, $first_sep, split m{(\n)}xms, $rest[0])
             :              ($first_row, $first_sep, @rest)
             ;

    my @separators = ($top_sep, @rows[grep {$_%2!=0} 0..$#rows], $bottom_sep);
    my @cells      = @rows[grep {$_%2==0} 0..$#rows];

    return _build_table_rows(
        $text, $has_head, \@cells, \@separators, $allow_ref, $config_stack_ref
    );
}

# Class to represent individual table row...
package Perl6::Perldoc::Block::table::Row;

# Read-only accessor for individual cells...
sub cells {
    my ($self) = @_; 
    my $vals_ref = $self->{cells};
    if (!wantarray) {
        if (@{ $vals_ref } > 1) {
            require Carp and Carp::carp(
                "Multivalued accessor cells() called in scalar context"
            );
        }
        return $vals_ref->[0];
    }
    return @{ $vals_ref };
}

# Class to represent individual table cell...
package Perl6::Perldoc::Block::table::Cell;

# Read-only content accessor...
sub content {
    my ($self) = @_; 
    my $vals_ref = $self->{content};
    if (!wantarray) {
        if (@{ $vals_ref } > 1) {
            require Carp and Carp::carp(
                "Multivalued accessor content() called in scalar context"
            );
        }
        return $vals_ref->[0];
    }
    return @{ $vals_ref };
}

# Is this a header row?
sub is_header {
    my ($self) = @_; 
    return $self->{header};
}


1;

__END__

=head1 NAME



( run in 2.604 seconds using v1.01-cache-2.11-cpan-71847e10f99 )