1N/A#############################################################################
1N/A# Pod/Parser.pm -- package which defines a base class for parsing POD docs.
1N/A#
1N/A# Copyright (C) 1996-2000 by Bradford Appleton. All rights reserved.
1N/A# This file is part of "PodParser". PodParser is free software;
1N/A# you can redistribute it and/or modify it under the same terms
1N/A# as Perl itself.
1N/A#############################################################################
1N/A
1N/Apackage Pod::Parser;
1N/A
1N/Ause vars qw($VERSION);
1N/A$VERSION = 1.14; ## Current version of this package
1N/Arequire 5.005; ## requires this Perl version or later
1N/A
1N/A#############################################################################
1N/A
1N/A=head1 NAME
1N/A
1N/APod::Parser - base class for creating POD filters and translators
1N/A
1N/A=head1 SYNOPSIS
1N/A
1N/A use Pod::Parser;
1N/A
1N/A package MyParser;
1N/A @ISA = qw(Pod::Parser);
1N/A
1N/A sub command {
1N/A my ($parser, $command, $paragraph, $line_num) = @_;
1N/A ## Interpret the command and its text; sample actions might be:
1N/A if ($command eq 'head1') { ... }
1N/A elsif ($command eq 'head2') { ... }
1N/A ## ... other commands and their actions
1N/A my $out_fh = $parser->output_handle();
1N/A my $expansion = $parser->interpolate($paragraph, $line_num);
1N/A print $out_fh $expansion;
1N/A }
1N/A
1N/A sub verbatim {
1N/A my ($parser, $paragraph, $line_num) = @_;
1N/A ## Format verbatim paragraph; sample actions might be:
1N/A my $out_fh = $parser->output_handle();
1N/A print $out_fh $paragraph;
1N/A }
1N/A
1N/A sub textblock {
1N/A my ($parser, $paragraph, $line_num) = @_;
1N/A ## Translate/Format this block of text; sample actions might be:
1N/A my $out_fh = $parser->output_handle();
1N/A my $expansion = $parser->interpolate($paragraph, $line_num);
1N/A print $out_fh $expansion;
1N/A }
1N/A
1N/A sub interior_sequence {
1N/A my ($parser, $seq_command, $seq_argument) = @_;
1N/A ## Expand an interior sequence; sample actions might be:
1N/A return "*$seq_argument*" if ($seq_command eq 'B');
1N/A return "`$seq_argument'" if ($seq_command eq 'C');
1N/A return "_${seq_argument}_'" if ($seq_command eq 'I');
1N/A ## ... other sequence commands and their resulting text
1N/A }
1N/A
1N/A package main;
1N/A
1N/A ## Create a parser object and have it parse file whose name was
1N/A ## given on the command-line (use STDIN if no files were given).
1N/A $parser = new MyParser();
1N/A $parser->parse_from_filehandle(\*STDIN) if (@ARGV == 0);
1N/A for (@ARGV) { $parser->parse_from_file($_); }
1N/A
1N/A=head1 REQUIRES
1N/A
1N/Aperl5.005, Pod::InputObjects, Exporter, Symbol, Carp
1N/A
1N/A=head1 EXPORTS
1N/A
1N/ANothing.
1N/A
1N/A=head1 DESCRIPTION
1N/A
1N/AB<Pod::Parser> is a base class for creating POD filters and translators.
1N/AIt handles most of the effort involved with parsing the POD sections
1N/Afrom an input stream, leaving subclasses free to be concerned only with
1N/Aperforming the actual translation of text.
1N/A
1N/AB<Pod::Parser> parses PODs, and makes method calls to handle the various
1N/Acomponents of the POD. Subclasses of B<Pod::Parser> override these methods
1N/Ato translate the POD into whatever output format they desire.
1N/A
1N/A=head1 QUICK OVERVIEW
1N/A
1N/ATo create a POD filter for translating POD documentation into some other
1N/Aformat, you create a subclass of B<Pod::Parser> which typically overrides
1N/Ajust the base class implementation for the following methods:
1N/A
1N/A=over 2
1N/A
1N/A=item *
1N/A
1N/AB<command()>
1N/A
1N/A=item *
1N/A
1N/AB<verbatim()>
1N/A
1N/A=item *
1N/A
1N/AB<textblock()>
1N/A
1N/A=item *
1N/A
1N/AB<interior_sequence()>
1N/A
1N/A=back
1N/A
1N/AYou may also want to override the B<begin_input()> and B<end_input()>
1N/Amethods for your subclass (to perform any needed per-file and/or
1N/Aper-document initialization or cleanup).
1N/A
1N/AIf you need to perform any preprocesssing of input before it is parsed
1N/Ayou may want to override one or more of B<preprocess_line()> and/or
1N/AB<preprocess_paragraph()>.
1N/A
1N/ASometimes it may be necessary to make more than one pass over the input
1N/Afiles. If this is the case you have several options. You can make the
1N/Afirst pass using B<Pod::Parser> and override your methods to store the
1N/Aintermediate results in memory somewhere for the B<end_pod()> method to
1N/Aprocess. You could use B<Pod::Parser> for several passes with an
1N/Aappropriate state variable to control the operation for each pass. If
1N/Ayour input source can't be reset to start at the beginning, you can
1N/Astore it in some other structure as a string or an array and have that
1N/Astructure implement a B<getline()> method (which is all that
1N/AB<parse_from_filehandle()> uses to read input).
1N/A
1N/AFeel free to add any member data fields you need to keep track of things
1N/Alike current font, indentation, horizontal or vertical position, or
1N/Awhatever else you like. Be sure to read L<"PRIVATE METHODS AND DATA">
1N/Ato avoid name collisions.
1N/A
1N/AFor the most part, the B<Pod::Parser> base class should be able to
1N/Ado most of the input parsing for you and leave you free to worry about
1N/Ahow to intepret the commands and translate the result.
1N/A
1N/ANote that all we have described here in this quick overview is the
1N/Asimplest most straightforward use of B<Pod::Parser> to do stream-based
1N/Aparsing. It is also possible to use the B<Pod::Parser::parse_text> function
1N/Ato do more sophisticated tree-based parsing. See L<"TREE-BASED PARSING">.
1N/A
1N/A=head1 PARSING OPTIONS
1N/A
1N/AA I<parse-option> is simply a named option of B<Pod::Parser> with a
1N/Avalue that corresponds to a certain specified behavior. These various
1N/Abehaviors of B<Pod::Parser> may be enabled/disabled by setting
1N/Aor unsetting one or more I<parse-options> using the B<parseopts()> method.
1N/AThe set of currently accepted parse-options is as follows:
1N/A
1N/A=over 3
1N/A
1N/A=item B<-want_nonPODs> (default: unset)
1N/A
1N/ANormally (by default) B<Pod::Parser> will only provide access to
1N/Athe POD sections of the input. Input paragraphs that are not part
1N/Aof the POD-format documentation are not made available to the caller
1N/A(not even using B<preprocess_paragraph()>). Setting this option to a
1N/Anon-empty, non-zero value will allow B<preprocess_paragraph()> to see
1N/Anon-POD sections of the input as well as POD sections. The B<cutting()>
1N/Amethod can be used to determine if the corresponding paragraph is a POD
1N/Aparagraph, or some other input paragraph.
1N/A
1N/A=item B<-process_cut_cmd> (default: unset)
1N/A
1N/ANormally (by default) B<Pod::Parser> handles the C<=cut> POD directive
1N/Aby itself and does not pass it on to the caller for processing. Setting
1N/Athis option to a non-empty, non-zero value will cause B<Pod::Parser> to
1N/Apass the C<=cut> directive to the caller just like any other POD command
1N/A(and hence it may be processed by the B<command()> method).
1N/A
1N/AB<Pod::Parser> will still interpret the C<=cut> directive to mean that
1N/A"cutting mode" has been (re)entered, but the caller will get a chance
1N/Ato capture the actual C<=cut> paragraph itself for whatever purpose
1N/Ait desires.
1N/A
1N/A=item B<-warnings> (default: unset)
1N/A
1N/ANormally (by default) B<Pod::Parser> recognizes a bare minimum of
1N/Apod syntax errors and warnings and issues diagnostic messages
1N/Afor errors, but not for warnings. (Use B<Pod::Checker> to do more
1N/Athorough checking of POD syntax.) Setting this option to a non-empty,
1N/Anon-zero value will cause B<Pod::Parser> to issue diagnostics for
1N/Athe few warnings it recognizes as well as the errors.
1N/A
1N/A=back
1N/A
1N/APlease see L<"parseopts()"> for a complete description of the interface
1N/Afor the setting and unsetting of parse-options.
1N/A
1N/A=cut
1N/A
1N/A#############################################################################
1N/A
1N/Ause vars qw(@ISA);
1N/Ause strict;
1N/A#use diagnostics;
1N/Ause Pod::InputObjects;
1N/Ause Carp;
1N/Ause Exporter;
1N/ABEGIN {
1N/A if ($] < 5.6) {
1N/A require Symbol;
1N/A import Symbol;
1N/A }
1N/A}
1N/A@ISA = qw(Exporter);
1N/A
1N/A## These "variables" are used as local "glob aliases" for performance
1N/Ause vars qw(%myData %myOpts @input_stack);
1N/A
1N/A#############################################################################
1N/A
1N/A=head1 RECOMMENDED SUBROUTINE/METHOD OVERRIDES
1N/A
1N/AB<Pod::Parser> provides several methods which most subclasses will probably
1N/Awant to override. These methods are as follows:
1N/A
1N/A=cut
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<command()>
1N/A
1N/A $parser->command($cmd,$text,$line_num,$pod_para);
1N/A
1N/AThis method should be overridden by subclasses to take the appropriate
1N/Aaction when a POD command paragraph (denoted by a line beginning with
1N/A"=") is encountered. When such a POD directive is seen in the input,
1N/Athis method is called and is passed:
1N/A
1N/A=over 3
1N/A
1N/A=item C<$cmd>
1N/A
1N/Athe name of the command for this POD paragraph
1N/A
1N/A=item C<$text>
1N/A
1N/Athe paragraph text for the given POD paragraph command.
1N/A
1N/A=item C<$line_num>
1N/A
1N/Athe line-number of the beginning of the paragraph
1N/A
1N/A=item C<$pod_para>
1N/A
1N/Aa reference to a C<Pod::Paragraph> object which contains further
1N/Ainformation about the paragraph command (see L<Pod::InputObjects>
1N/Afor details).
1N/A
1N/A=back
1N/A
1N/AB<Note> that this method I<is> called for C<=pod> paragraphs.
1N/A
1N/AThe base class implementation of this method simply treats the raw POD
1N/Acommand as normal block of paragraph text (invoking the B<textblock()>
1N/Amethod with the command paragraph).
1N/A
1N/A=cut
1N/A
1N/Asub command {
1N/A my ($self, $cmd, $text, $line_num, $pod_para) = @_;
1N/A ## Just treat this like a textblock
1N/A $self->textblock($pod_para->raw_text(), $line_num, $pod_para);
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<verbatim()>
1N/A
1N/A $parser->verbatim($text,$line_num,$pod_para);
1N/A
1N/AThis method may be overridden by subclasses to take the appropriate
1N/Aaction when a block of verbatim text is encountered. It is passed the
1N/Afollowing parameters:
1N/A
1N/A=over 3
1N/A
1N/A=item C<$text>
1N/A
1N/Athe block of text for the verbatim paragraph
1N/A
1N/A=item C<$line_num>
1N/A
1N/Athe line-number of the beginning of the paragraph
1N/A
1N/A=item C<$pod_para>
1N/A
1N/Aa reference to a C<Pod::Paragraph> object which contains further
1N/Ainformation about the paragraph (see L<Pod::InputObjects>
1N/Afor details).
1N/A
1N/A=back
1N/A
1N/AThe base class implementation of this method simply prints the textblock
1N/A(unmodified) to the output filehandle.
1N/A
1N/A=cut
1N/A
1N/Asub verbatim {
1N/A my ($self, $text, $line_num, $pod_para) = @_;
1N/A my $out_fh = $self->{_OUTPUT};
1N/A print $out_fh $text;
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<textblock()>
1N/A
1N/A $parser->textblock($text,$line_num,$pod_para);
1N/A
1N/AThis method may be overridden by subclasses to take the appropriate
1N/Aaction when a normal block of POD text is encountered (although the base
1N/Aclass method will usually do what you want). It is passed the following
1N/Aparameters:
1N/A
1N/A=over 3
1N/A
1N/A=item C<$text>
1N/A
1N/Athe block of text for the a POD paragraph
1N/A
1N/A=item C<$line_num>
1N/A
1N/Athe line-number of the beginning of the paragraph
1N/A
1N/A=item C<$pod_para>
1N/A
1N/Aa reference to a C<Pod::Paragraph> object which contains further
1N/Ainformation about the paragraph (see L<Pod::InputObjects>
1N/Afor details).
1N/A
1N/A=back
1N/A
1N/AIn order to process interior sequences, subclasses implementations of
1N/Athis method will probably want to invoke either B<interpolate()> or
1N/AB<parse_text()>, passing it the text block C<$text>, and the corresponding
1N/Aline number in C<$line_num>, and then perform any desired processing upon
1N/Athe returned result.
1N/A
1N/AThe base class implementation of this method simply prints the text block
1N/Aas it occurred in the input stream).
1N/A
1N/A=cut
1N/A
1N/Asub textblock {
1N/A my ($self, $text, $line_num, $pod_para) = @_;
1N/A my $out_fh = $self->{_OUTPUT};
1N/A print $out_fh $self->interpolate($text, $line_num);
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<interior_sequence()>
1N/A
1N/A $parser->interior_sequence($seq_cmd,$seq_arg,$pod_seq);
1N/A
1N/AThis method should be overridden by subclasses to take the appropriate
1N/Aaction when an interior sequence is encountered. An interior sequence is
1N/Aan embedded command within a block of text which appears as a command
1N/Aname (usually a single uppercase character) followed immediately by a
1N/Astring of text which is enclosed in angle brackets. This method is
1N/Apassed the sequence command C<$seq_cmd> and the corresponding text
1N/AC<$seq_arg>. It is invoked by the B<interpolate()> method for each interior
1N/Asequence that occurs in the string that it is passed. It should return
1N/Athe desired text string to be used in place of the interior sequence.
1N/AThe C<$pod_seq> argument is a reference to a C<Pod::InteriorSequence>
1N/Aobject which contains further information about the interior sequence.
1N/APlease see L<Pod::InputObjects> for details if you need to access this
1N/Aadditional information.
1N/A
1N/ASubclass implementations of this method may wish to invoke the
1N/AB<nested()> method of C<$pod_seq> to see if it is nested inside
1N/Asome other interior-sequence (and if so, which kind).
1N/A
1N/AThe base class implementation of the B<interior_sequence()> method
1N/Asimply returns the raw text of the interior sequence (as it occurred
1N/Ain the input) to the caller.
1N/A
1N/A=cut
1N/A
1N/Asub interior_sequence {
1N/A my ($self, $seq_cmd, $seq_arg, $pod_seq) = @_;
1N/A ## Just return the raw text of the interior sequence
1N/A return $pod_seq->raw_text();
1N/A}
1N/A
1N/A#############################################################################
1N/A
1N/A=head1 OPTIONAL SUBROUTINE/METHOD OVERRIDES
1N/A
1N/AB<Pod::Parser> provides several methods which subclasses may want to override
1N/Ato perform any special pre/post-processing. These methods do I<not> have to
1N/Abe overridden, but it may be useful for subclasses to take advantage of them.
1N/A
1N/A=cut
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<new()>
1N/A
1N/A my $parser = Pod::Parser->new();
1N/A
1N/AThis is the constructor for B<Pod::Parser> and its subclasses. You
1N/AI<do not> need to override this method! It is capable of constructing
1N/Asubclass objects as well as base class objects, provided you use
1N/Aany of the following constructor invocation styles:
1N/A
1N/A my $parser1 = MyParser->new();
1N/A my $parser2 = new MyParser();
1N/A my $parser3 = $parser2->new();
1N/A
1N/Awhere C<MyParser> is some subclass of B<Pod::Parser>.
1N/A
1N/AUsing the syntax C<MyParser::new()> to invoke the constructor is I<not>
1N/Arecommended, but if you insist on being able to do this, then the
1N/Asubclass I<will> need to override the B<new()> constructor method. If
1N/Ayou do override the constructor, you I<must> be sure to invoke the
1N/AB<initialize()> method of the newly blessed object.
1N/A
1N/AUsing any of the above invocations, the first argument to the
1N/Aconstructor is always the corresponding package name (or object
1N/Areference). No other arguments are required, but if desired, an
1N/Aassociative array (or hash-table) my be passed to the B<new()>
1N/Aconstructor, as in:
1N/A
1N/A my $parser1 = MyParser->new( MYDATA => $value1, MOREDATA => $value2 );
1N/A my $parser2 = new MyParser( -myflag => 1 );
1N/A
1N/AAll arguments passed to the B<new()> constructor will be treated as
1N/Akey/value pairs in a hash-table. The newly constructed object will be
1N/Ainitialized by copying the contents of the given hash-table (which may
1N/Ahave been empty). The B<new()> constructor for this class and all of its
1N/Asubclasses returns a blessed reference to the initialized object (hash-table).
1N/A
1N/A=cut
1N/A
1N/Asub new {
1N/A ## Determine if we were called via an object-ref or a classname
1N/A my $this = shift;
1N/A my $class = ref($this) || $this;
1N/A ## Any remaining arguments are treated as initial values for the
1N/A ## hash that is used to represent this object.
1N/A my %params = @_;
1N/A my $self = { %params };
1N/A ## Bless ourselves into the desired class and perform any initialization
1N/A bless $self, $class;
1N/A $self->initialize();
1N/A return $self;
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<initialize()>
1N/A
1N/A $parser->initialize();
1N/A
1N/AThis method performs any necessary object initialization. It takes no
1N/Aarguments (other than the object instance of course, which is typically
1N/Acopied to a local variable named C<$self>). If subclasses override this
1N/Amethod then they I<must> be sure to invoke C<$self-E<gt>SUPER::initialize()>.
1N/A
1N/A=cut
1N/A
1N/Asub initialize {
1N/A #my $self = shift;
1N/A #return;
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<begin_pod()>
1N/A
1N/A $parser->begin_pod();
1N/A
1N/AThis method is invoked at the beginning of processing for each POD
1N/Adocument that is encountered in the input. Subclasses should override
1N/Athis method to perform any per-document initialization.
1N/A
1N/A=cut
1N/A
1N/Asub begin_pod {
1N/A #my $self = shift;
1N/A #return;
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<begin_input()>
1N/A
1N/A $parser->begin_input();
1N/A
1N/AThis method is invoked by B<parse_from_filehandle()> immediately I<before>
1N/Aprocessing input from a filehandle. The base class implementation does
1N/Anothing, however, subclasses may override it to perform any per-file
1N/Ainitializations.
1N/A
1N/ANote that if multiple files are parsed for a single POD document
1N/A(perhaps the result of some future C<=include> directive) this method
1N/Ais invoked for every file that is parsed. If you wish to perform certain
1N/Ainitializations once per document, then you should use B<begin_pod()>.
1N/A
1N/A=cut
1N/A
1N/Asub begin_input {
1N/A #my $self = shift;
1N/A #return;
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<end_input()>
1N/A
1N/A $parser->end_input();
1N/A
1N/AThis method is invoked by B<parse_from_filehandle()> immediately I<after>
1N/Aprocessing input from a filehandle. The base class implementation does
1N/Anothing, however, subclasses may override it to perform any per-file
1N/Acleanup actions.
1N/A
1N/APlease note that if multiple files are parsed for a single POD document
1N/A(perhaps the result of some kind of C<=include> directive) this method
1N/Ais invoked for every file that is parsed. If you wish to perform certain
1N/Acleanup actions once per document, then you should use B<end_pod()>.
1N/A
1N/A=cut
1N/A
1N/Asub end_input {
1N/A #my $self = shift;
1N/A #return;
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<end_pod()>
1N/A
1N/A $parser->end_pod();
1N/A
1N/AThis method is invoked at the end of processing for each POD document
1N/Athat is encountered in the input. Subclasses should override this method
1N/Ato perform any per-document finalization.
1N/A
1N/A=cut
1N/A
1N/Asub end_pod {
1N/A #my $self = shift;
1N/A #return;
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<preprocess_line()>
1N/A
1N/A $textline = $parser->preprocess_line($text, $line_num);
1N/A
1N/AThis method should be overridden by subclasses that wish to perform
1N/Aany kind of preprocessing for each I<line> of input (I<before> it has
1N/Abeen determined whether or not it is part of a POD paragraph). The
1N/Aparameter C<$text> is the input line; and the parameter C<$line_num> is
1N/Athe line number of the corresponding text line.
1N/A
1N/AThe value returned should correspond to the new text to use in its
1N/Aplace. If the empty string or an undefined value is returned then no
1N/Afurther processing will be performed for this line.
1N/A
1N/APlease note that the B<preprocess_line()> method is invoked I<before>
1N/Athe B<preprocess_paragraph()> method. After all (possibly preprocessed)
1N/Alines in a paragraph have been assembled together and it has been
1N/Adetermined that the paragraph is part of the POD documentation from one
1N/Aof the selected sections, then B<preprocess_paragraph()> is invoked.
1N/A
1N/AThe base class implementation of this method returns the given text.
1N/A
1N/A=cut
1N/A
1N/Asub preprocess_line {
1N/A my ($self, $text, $line_num) = @_;
1N/A return $text;
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<preprocess_paragraph()>
1N/A
1N/A $textblock = $parser->preprocess_paragraph($text, $line_num);
1N/A
1N/AThis method should be overridden by subclasses that wish to perform any
1N/Akind of preprocessing for each block (paragraph) of POD documentation
1N/Athat appears in the input stream. The parameter C<$text> is the POD
1N/Aparagraph from the input file; and the parameter C<$line_num> is the
1N/Aline number for the beginning of the corresponding paragraph.
1N/A
1N/AThe value returned should correspond to the new text to use in its
1N/Aplace If the empty string is returned or an undefined value is
1N/Areturned, then the given C<$text> is ignored (not processed).
1N/A
1N/AThis method is invoked after gathering up all the lines in a paragraph
1N/Aand after determining the cutting state of the paragraph,
1N/Abut before trying to further parse or interpret them. After
1N/AB<preprocess_paragraph()> returns, the current cutting state (which
1N/Ais returned by C<$self-E<gt>cutting()>) is examined. If it evaluates
1N/Ato true then input text (including the given C<$text>) is cut (not
1N/Aprocessed) until the next POD directive is encountered.
1N/A
1N/APlease note that the B<preprocess_line()> method is invoked I<before>
1N/Athe B<preprocess_paragraph()> method. After all (possibly preprocessed)
1N/Alines in a paragraph have been assembled together and either it has been
1N/Adetermined that the paragraph is part of the POD documentation from one
1N/Aof the selected sections or the C<-want_nonPODs> option is true,
1N/Athen B<preprocess_paragraph()> is invoked.
1N/A
1N/AThe base class implementation of this method returns the given text.
1N/A
1N/A=cut
1N/A
1N/Asub preprocess_paragraph {
1N/A my ($self, $text, $line_num) = @_;
1N/A return $text;
1N/A}
1N/A
1N/A#############################################################################
1N/A
1N/A=head1 METHODS FOR PARSING AND PROCESSING
1N/A
1N/AB<Pod::Parser> provides several methods to process input text. These
1N/Amethods typically won't need to be overridden (and in some cases they
1N/Acan't be overridden), but subclasses may want to invoke them to exploit
1N/Atheir functionality.
1N/A
1N/A=cut
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<parse_text()>
1N/A
1N/A $ptree1 = $parser->parse_text($text, $line_num);
1N/A $ptree2 = $parser->parse_text({%opts}, $text, $line_num);
1N/A $ptree3 = $parser->parse_text(\%opts, $text, $line_num);
1N/A
1N/AThis method is useful if you need to perform your own interpolation
1N/Aof interior sequences and can't rely upon B<interpolate> to expand
1N/Athem in simple bottom-up order.
1N/A
1N/AThe parameter C<$text> is a string or block of text to be parsed
1N/Afor interior sequences; and the parameter C<$line_num> is the
1N/Aline number curresponding to the beginning of C<$text>.
1N/A
1N/AB<parse_text()> will parse the given text into a parse-tree of "nodes."
1N/Aand interior-sequences. Each "node" in the parse tree is either a
1N/Atext-string, or a B<Pod::InteriorSequence>. The result returned is a
1N/Aparse-tree of type B<Pod::ParseTree>. Please see L<Pod::InputObjects>
1N/Afor more information about B<Pod::InteriorSequence> and B<Pod::ParseTree>.
1N/A
1N/AIf desired, an optional hash-ref may be specified as the first argument
1N/Ato customize certain aspects of the parse-tree that is created and
1N/Areturned. The set of recognized option keywords are:
1N/A
1N/A=over 3
1N/A
1N/A=item B<-expand_seq> =E<gt> I<code-ref>|I<method-name>
1N/A
1N/ANormally, the parse-tree returned by B<parse_text()> will contain an
1N/Aunexpanded C<Pod::InteriorSequence> object for each interior-sequence
1N/Aencountered. Specifying B<-expand_seq> tells B<parse_text()> to "expand"
1N/Aevery interior-sequence it sees by invoking the referenced function
1N/A(or named method of the parser object) and using the return value as the
1N/Aexpanded result.
1N/A
1N/AIf a subroutine reference was given, it is invoked as:
1N/A
1N/A &$code_ref( $parser, $sequence )
1N/A
1N/Aand if a method-name was given, it is invoked as:
1N/A
1N/A $parser->method_name( $sequence )
1N/A
1N/Awhere C<$parser> is a reference to the parser object, and C<$sequence>
1N/Ais a reference to the interior-sequence object.
1N/A[I<NOTE>: If the B<interior_sequence()> method is specified, then it is
1N/Ainvoked according to the interface specified in L<"interior_sequence()">].
1N/A
1N/A=item B<-expand_text> =E<gt> I<code-ref>|I<method-name>
1N/A
1N/ANormally, the parse-tree returned by B<parse_text()> will contain a
1N/Atext-string for each contiguous sequence of characters outside of an
1N/Ainterior-sequence. Specifying B<-expand_text> tells B<parse_text()> to
1N/A"preprocess" every such text-string it sees by invoking the referenced
1N/Afunction (or named method of the parser object) and using the return value
1N/Aas the preprocessed (or "expanded") result. [Note that if the result is
1N/Aan interior-sequence, then it will I<not> be expanded as specified by the
1N/AB<-expand_seq> option; Any such recursive expansion needs to be handled by
1N/Athe specified callback routine.]
1N/A
1N/AIf a subroutine reference was given, it is invoked as:
1N/A
1N/A &$code_ref( $parser, $text, $ptree_node )
1N/A
1N/Aand if a method-name was given, it is invoked as:
1N/A
1N/A $parser->method_name( $text, $ptree_node )
1N/A
1N/Awhere C<$parser> is a reference to the parser object, C<$text> is the
1N/Atext-string encountered, and C<$ptree_node> is a reference to the current
1N/Anode in the parse-tree (usually an interior-sequence object or else the
1N/Atop-level node of the parse-tree).
1N/A
1N/A=item B<-expand_ptree> =E<gt> I<code-ref>|I<method-name>
1N/A
1N/ARather than returning a C<Pod::ParseTree>, pass the parse-tree as an
1N/Aargument to the referenced subroutine (or named method of the parser
1N/Aobject) and return the result instead of the parse-tree object.
1N/A
1N/AIf a subroutine reference was given, it is invoked as:
1N/A
1N/A &$code_ref( $parser, $ptree )
1N/A
1N/Aand if a method-name was given, it is invoked as:
1N/A
1N/A $parser->method_name( $ptree )
1N/A
1N/Awhere C<$parser> is a reference to the parser object, and C<$ptree>
1N/Ais a reference to the parse-tree object.
1N/A
1N/A=back
1N/A
1N/A=cut
1N/A
1N/Asub parse_text {
1N/A my $self = shift;
1N/A local $_ = '';
1N/A
1N/A ## Get options and set any defaults
1N/A my %opts = (ref $_[0]) ? %{ shift() } : ();
1N/A my $expand_seq = $opts{'-expand_seq'} || undef;
1N/A my $expand_text = $opts{'-expand_text'} || undef;
1N/A my $expand_ptree = $opts{'-expand_ptree'} || undef;
1N/A
1N/A my $text = shift;
1N/A my $line = shift;
1N/A my $file = $self->input_file();
1N/A my $cmd = "";
1N/A
1N/A ## Convert method calls into closures, for our convenience
1N/A my $xseq_sub = $expand_seq;
1N/A my $xtext_sub = $expand_text;
1N/A my $xptree_sub = $expand_ptree;
1N/A if (defined $expand_seq and $expand_seq eq 'interior_sequence') {
1N/A ## If 'interior_sequence' is the method to use, we have to pass
1N/A ## more than just the sequence object, we also need to pass the
1N/A ## sequence name and text.
1N/A $xseq_sub = sub {
1N/A my ($self, $iseq) = @_;
1N/A my $args = join("", $iseq->parse_tree->children);
1N/A return $self->interior_sequence($iseq->name, $args, $iseq);
1N/A };
1N/A }
1N/A ref $xseq_sub or $xseq_sub = sub { shift()->$expand_seq(@_) };
1N/A ref $xtext_sub or $xtext_sub = sub { shift()->$expand_text(@_) };
1N/A ref $xptree_sub or $xptree_sub = sub { shift()->$expand_ptree(@_) };
1N/A
1N/A ## Keep track of the "current" interior sequence, and maintain a stack
1N/A ## of "in progress" sequences.
1N/A ##
1N/A ## NOTE that we push our own "accumulator" at the very beginning of the
1N/A ## stack. It's really a parse-tree, not a sequence; but it implements
1N/A ## the methods we need so we can use it to gather-up all the sequences
1N/A ## and strings we parse. Thus, by the end of our parsing, it should be
1N/A ## the only thing left on our stack and all we have to do is return it!
1N/A ##
1N/A my $seq = Pod::ParseTree->new();
1N/A my @seq_stack = ($seq);
1N/A my ($ldelim, $rdelim) = ('', '');
1N/A
1N/A ## Iterate over all sequence starts text (NOTE: split with
1N/A ## capturing parens keeps the delimiters)
1N/A $_ = $text;
1N/A my @tokens = split /([A-Z]<(?:<+\s)?)/;
1N/A while ( @tokens ) {
1N/A $_ = shift @tokens;
1N/A ## Look for the beginning of a sequence
1N/A if ( /^([A-Z])(<(?:<+\s)?)$/ ) {
1N/A ## Push a new sequence onto the stack of those "in-progress"
1N/A my $ldelim_orig;
1N/A ($cmd, $ldelim_orig) = ($1, $2);
1N/A ($ldelim = $ldelim_orig) =~ s/\s+$//;
1N/A ($rdelim = $ldelim) =~ tr/</>/;
1N/A $seq = Pod::InteriorSequence->new(
1N/A -name => $cmd,
1N/A -ldelim => $ldelim_orig, -rdelim => $rdelim,
1N/A -file => $file, -line => $line
1N/A );
1N/A (@seq_stack > 1) and $seq->nested($seq_stack[-1]);
1N/A push @seq_stack, $seq;
1N/A }
1N/A ## Look for sequence ending
1N/A elsif ( @seq_stack > 1 ) {
1N/A ## Make sure we match the right kind of closing delimiter
1N/A my ($seq_end, $post_seq) = ("", "");
1N/A if ( ($ldelim eq '<' and /\A(.*?)(>)/s)
1N/A or /\A(.*?)(\s+$rdelim)/s )
1N/A {
1N/A ## Found end-of-sequence, capture the interior and the
1N/A ## closing the delimiter, and put the rest back on the
1N/A ## token-list
1N/A $post_seq = substr($_, length($1) + length($2));
1N/A ($_, $seq_end) = ($1, $2);
1N/A (length $post_seq) and unshift @tokens, $post_seq;
1N/A }
1N/A if (length) {
1N/A ## In the middle of a sequence, append this text to it, and
1N/A ## dont forget to "expand" it if that's what the caller wanted
1N/A $seq->append($expand_text ? &$xtext_sub($self,$_,$seq) : $_);
1N/A $_ .= $seq_end;
1N/A }
1N/A if (length $seq_end) {
1N/A ## End of current sequence, record terminating delimiter
1N/A $seq->rdelim($seq_end);
1N/A ## Pop it off the stack of "in progress" sequences
1N/A pop @seq_stack;
1N/A ## Append result to its parent in current parse tree
1N/A $seq_stack[-1]->append($expand_seq ? &$xseq_sub($self,$seq)
1N/A : $seq);
1N/A ## Remember the current cmd-name and left-delimiter
1N/A if(@seq_stack > 1) {
1N/A $cmd = $seq_stack[-1]->name;
1N/A $ldelim = $seq_stack[-1]->ldelim;
1N/A $rdelim = $seq_stack[-1]->rdelim;
1N/A } else {
1N/A $cmd = $ldelim = $rdelim = '';
1N/A }
1N/A }
1N/A }
1N/A elsif (length) {
1N/A ## In the middle of a sequence, append this text to it, and
1N/A ## dont forget to "expand" it if that's what the caller wanted
1N/A $seq->append($expand_text ? &$xtext_sub($self,$_,$seq) : $_);
1N/A }
1N/A ## Keep track of line count
1N/A $line += tr/\n//;
1N/A ## Remember the "current" sequence
1N/A $seq = $seq_stack[-1];
1N/A }
1N/A
1N/A ## Handle unterminated sequences
1N/A my $errorsub = (@seq_stack > 1) ? $self->errorsub() : undef;
1N/A while (@seq_stack > 1) {
1N/A ($cmd, $file, $line) = ($seq->name, $seq->file_line);
1N/A $ldelim = $seq->ldelim;
1N/A ($rdelim = $ldelim) =~ tr/</>/;
1N/A $rdelim =~ s/^(\S+)(\s*)$/$2$1/;
1N/A pop @seq_stack;
1N/A my $errmsg = "*** ERROR: unterminated ${cmd}${ldelim}...${rdelim}".
1N/A " at line $line in file $file\n";
1N/A (ref $errorsub) and &{$errorsub}($errmsg)
1N/A or (defined $errorsub) and $self->$errorsub($errmsg)
1N/A or warn($errmsg);
1N/A $seq_stack[-1]->append($expand_seq ? &$xseq_sub($self,$seq) : $seq);
1N/A $seq = $seq_stack[-1];
1N/A }
1N/A
1N/A ## Return the resulting parse-tree
1N/A my $ptree = (pop @seq_stack)->parse_tree;
1N/A return $expand_ptree ? &$xptree_sub($self, $ptree) : $ptree;
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<interpolate()>
1N/A
1N/A $textblock = $parser->interpolate($text, $line_num);
1N/A
1N/AThis method translates all text (including any embedded interior sequences)
1N/Ain the given text string C<$text> and returns the interpolated result. The
1N/Aparameter C<$line_num> is the line number corresponding to the beginning
1N/Aof C<$text>.
1N/A
1N/AB<interpolate()> merely invokes a private method to recursively expand
1N/Anested interior sequences in bottom-up order (innermost sequences are
1N/Aexpanded first). If there is a need to expand nested sequences in
1N/Asome alternate order, use B<parse_text> instead.
1N/A
1N/A=cut
1N/A
1N/Asub interpolate {
1N/A my($self, $text, $line_num) = @_;
1N/A my %parse_opts = ( -expand_seq => 'interior_sequence' );
1N/A my $ptree = $self->parse_text( \%parse_opts, $text, $line_num );
1N/A return join "", $ptree->children();
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=begin __PRIVATE__
1N/A
1N/A=head1 B<parse_paragraph()>
1N/A
1N/A $parser->parse_paragraph($text, $line_num);
1N/A
1N/AThis method takes the text of a POD paragraph to be processed, along
1N/Awith its corresponding line number, and invokes the appropriate method
1N/A(one of B<command()>, B<verbatim()>, or B<textblock()>).
1N/A
1N/AFor performance reasons, this method is invoked directly without any
1N/Adynamic lookup; Hence subclasses may I<not> override it!
1N/A
1N/A=end __PRIVATE__
1N/A
1N/A=cut
1N/A
1N/Asub parse_paragraph {
1N/A my ($self, $text, $line_num) = @_;
1N/A local *myData = $self; ## alias to avoid deref-ing overhead
1N/A local *myOpts = ($myData{_PARSEOPTS} ||= {}); ## get parse-options
1N/A local $_;
1N/A
1N/A ## See if we want to preprocess nonPOD paragraphs as well as POD ones.
1N/A my $wantNonPods = $myOpts{'-want_nonPODs'};
1N/A
1N/A ## Update cutting status
1N/A $myData{_CUTTING} = 0 if $text =~ /^={1,2}\S/;
1N/A
1N/A ## Perform any desired preprocessing if we wanted it this early
1N/A $wantNonPods and $text = $self->preprocess_paragraph($text, $line_num);
1N/A
1N/A ## Ignore up until next POD directive if we are cutting
1N/A return if $myData{_CUTTING};
1N/A
1N/A ## Now we know this is block of text in a POD section!
1N/A
1N/A ##-----------------------------------------------------------------
1N/A ## This is a hook (hack ;-) for Pod::Select to do its thing without
1N/A ## having to override methods, but also without Pod::Parser assuming
1N/A ## $self is an instance of Pod::Select (if the _SELECTED_SECTIONS
1N/A ## field exists then we assume there is an is_selected() method for
1N/A ## us to invoke (calling $self->can('is_selected') could verify this
1N/A ## but that is more overhead than I want to incur)
1N/A ##-----------------------------------------------------------------
1N/A
1N/A ## Ignore this block if it isnt in one of the selected sections
1N/A if (exists $myData{_SELECTED_SECTIONS}) {
1N/A $self->is_selected($text) or return ($myData{_CUTTING} = 1);
1N/A }
1N/A
1N/A ## If we havent already, perform any desired preprocessing and
1N/A ## then re-check the "cutting" state
1N/A unless ($wantNonPods) {
1N/A $text = $self->preprocess_paragraph($text, $line_num);
1N/A return 1 unless ((defined $text) and (length $text));
1N/A return 1 if ($myData{_CUTTING});
1N/A }
1N/A
1N/A ## Look for one of the three types of paragraphs
1N/A my ($pfx, $cmd, $arg, $sep) = ('', '', '', '');
1N/A my $pod_para = undef;
1N/A if ($text =~ /^(={1,2})(?=\S)/) {
1N/A ## Looks like a command paragraph. Capture the command prefix used
1N/A ## ("=" or "=="), as well as the command-name, its paragraph text,
1N/A ## and whatever sequence of characters was used to separate them
1N/A $pfx = $1;
1N/A $_ = substr($text, length $pfx);
1N/A ($cmd, $sep, $text) = split /(\s+)/, $_, 2;
1N/A ## If this is a "cut" directive then we dont need to do anything
1N/A ## except return to "cutting" mode.
1N/A if ($cmd eq 'cut') {
1N/A $myData{_CUTTING} = 1;
1N/A return unless $myOpts{'-process_cut_cmd'};
1N/A }
1N/A }
1N/A ## Save the attributes indicating how the command was specified.
1N/A $pod_para = new Pod::Paragraph(
1N/A -name => $cmd,
1N/A -text => $text,
1N/A -prefix => $pfx,
1N/A -separator => $sep,
1N/A -file => $myData{_INFILE},
1N/A -line => $line_num
1N/A );
1N/A # ## Invoke appropriate callbacks
1N/A # if (exists $myData{_CALLBACKS}) {
1N/A # ## Look through the callback list, invoke callbacks,
1N/A # ## then see if we need to do the default actions
1N/A # ## (invoke_callbacks will return true if we do).
1N/A # return 1 unless $self->invoke_callbacks($cmd, $text, $line_num, $pod_para);
1N/A # }
1N/A if (length $cmd) {
1N/A ## A command paragraph
1N/A $self->command($cmd, $text, $line_num, $pod_para);
1N/A }
1N/A elsif ($text =~ /^\s+/) {
1N/A ## Indented text - must be a verbatim paragraph
1N/A $self->verbatim($text, $line_num, $pod_para);
1N/A }
1N/A else {
1N/A ## Looks like an ordinary block of text
1N/A $self->textblock($text, $line_num, $pod_para);
1N/A }
1N/A return 1;
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<parse_from_filehandle()>
1N/A
1N/A $parser->parse_from_filehandle($in_fh,$out_fh);
1N/A
1N/AThis method takes an input filehandle (which is assumed to already be
1N/Aopened for reading) and reads the entire input stream looking for blocks
1N/A(paragraphs) of POD documentation to be processed. If no first argument
1N/Ais given the default input filehandle C<STDIN> is used.
1N/A
1N/AThe C<$in_fh> parameter may be any object that provides a B<getline()>
1N/Amethod to retrieve a single line of input text (hence, an appropriate
1N/Awrapper object could be used to parse PODs from a single string or an
1N/Aarray of strings).
1N/A
1N/AUsing C<$in_fh-E<gt>getline()>, input is read line-by-line and assembled
1N/Ainto paragraphs or "blocks" (which are separated by lines containing
1N/Anothing but whitespace). For each block of POD documentation
1N/Aencountered it will invoke a method to parse the given paragraph.
1N/A
1N/AIf a second argument is given then it should correspond to a filehandle where
1N/Aoutput should be sent (otherwise the default output filehandle is
1N/AC<STDOUT> if no output filehandle is currently in use).
1N/A
1N/AB<NOTE:> For performance reasons, this method caches the input stream at
1N/Athe top of the stack in a local variable. Any attempts by clients to
1N/Achange the stack contents during processing when in the midst executing
1N/Aof this method I<will not affect> the input stream used by the current
1N/Ainvocation of this method.
1N/A
1N/AThis method does I<not> usually need to be overridden by subclasses.
1N/A
1N/A=cut
1N/A
1N/Asub parse_from_filehandle {
1N/A my $self = shift;
1N/A my %opts = (ref $_[0] eq 'HASH') ? %{ shift() } : ();
1N/A my ($in_fh, $out_fh) = @_;
1N/A $in_fh = \*STDIN unless ($in_fh);
1N/A local *myData = $self; ## alias to avoid deref-ing overhead
1N/A local *myOpts = ($myData{_PARSEOPTS} ||= {}); ## get parse-options
1N/A local $_;
1N/A
1N/A ## Put this stream at the top of the stack and do beginning-of-input
1N/A ## processing. NOTE that $in_fh might be reset during this process.
1N/A my $topstream = $self->_push_input_stream($in_fh, $out_fh);
1N/A (exists $opts{-cutting}) and $self->cutting( $opts{-cutting} );
1N/A
1N/A ## Initialize line/paragraph
1N/A my ($textline, $paragraph) = ('', '');
1N/A my ($nlines, $plines) = (0, 0);
1N/A
1N/A ## Use <$fh> instead of $fh->getline where possible (for speed)
1N/A $_ = ref $in_fh;
1N/A my $tied_fh = (/^(?:GLOB|FileHandle|IO::\w+)$/ or tied $in_fh);
1N/A
1N/A ## Read paragraphs line-by-line
1N/A while (defined ($textline = $tied_fh ? <$in_fh> : $in_fh->getline)) {
1N/A $textline = $self->preprocess_line($textline, ++$nlines);
1N/A next unless ((defined $textline) && (length $textline));
1N/A $_ = $paragraph; ## save previous contents
1N/A
1N/A if ((! length $paragraph) && ($textline =~ /^==/)) {
1N/A ## '==' denotes a one-line command paragraph
1N/A $paragraph = $textline;
1N/A $plines = 1;
1N/A $textline = '';
1N/A } else {
1N/A ## Append this line to the current paragraph
1N/A $paragraph .= $textline;
1N/A ++$plines;
1N/A }
1N/A
1N/A ## See if this line is blank and ends the current paragraph.
1N/A ## If it isnt, then keep iterating until it is.
1N/A next unless (($textline =~ /^([^\S\r\n]*)[\r\n]*$/)
1N/A && (length $paragraph));
1N/A
1N/A ## Issue a warning about any non-empty blank lines
1N/A if (length($1) > 0 and $myOpts{'-warnings'} and ! $myData{_CUTTING}) {
1N/A my $errorsub = $self->errorsub();
1N/A my $file = $self->input_file();
1N/A my $errmsg = "*** WARNING: line containing nothing but whitespace".
1N/A " in paragraph at line $nlines in file $file\n";
1N/A (ref $errorsub) and &{$errorsub}($errmsg)
1N/A or (defined $errorsub) and $self->$errorsub($errmsg)
1N/A or warn($errmsg);
1N/A }
1N/A
1N/A ## Now process the paragraph
1N/A parse_paragraph($self, $paragraph, ($nlines - $plines) + 1);
1N/A $paragraph = '';
1N/A $plines = 0;
1N/A }
1N/A ## Dont forget about the last paragraph in the file
1N/A if (length $paragraph) {
1N/A parse_paragraph($self, $paragraph, ($nlines - $plines) + 1)
1N/A }
1N/A
1N/A ## Now pop the input stream off the top of the input stack.
1N/A $self->_pop_input_stream();
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<parse_from_file()>
1N/A
1N/A $parser->parse_from_file($filename,$outfile);
1N/A
1N/AThis method takes a filename and does the following:
1N/A
1N/A=over 2
1N/A
1N/A=item *
1N/A
1N/Aopens the input and output files for reading
1N/A(creating the appropriate filehandles)
1N/A
1N/A=item *
1N/A
1N/Ainvokes the B<parse_from_filehandle()> method passing it the
1N/Acorresponding input and output filehandles.
1N/A
1N/A=item *
1N/A
1N/Acloses the input and output files.
1N/A
1N/A=back
1N/A
1N/AIf the special input filename "-" or "<&STDIN" is given then the STDIN
1N/Afilehandle is used for input (and no open or close is performed). If no
1N/Ainput filename is specified then "-" is implied.
1N/A
1N/AIf a second argument is given then it should be the name of the desired
1N/Aoutput file. If the special output filename "-" or ">&STDOUT" is given
1N/Athen the STDOUT filehandle is used for output (and no open or close is
1N/Aperformed). If the special output filename ">&STDERR" is given then the
1N/ASTDERR filehandle is used for output (and no open or close is
1N/Aperformed). If no output filehandle is currently in use and no output
1N/Afilename is specified, then "-" is implied.
1N/A
1N/AThis method does I<not> usually need to be overridden by subclasses.
1N/A
1N/A=cut
1N/A
1N/Asub parse_from_file {
1N/A my $self = shift;
1N/A my %opts = (ref $_[0] eq 'HASH') ? %{ shift() } : ();
1N/A my ($infile, $outfile) = @_;
1N/A my ($in_fh, $out_fh) = (gensym, gensym) if ($] < 5.6);
1N/A my ($close_input, $close_output) = (0, 0);
1N/A local *myData = $self;
1N/A local $_;
1N/A
1N/A ## Is $infile a filename or a (possibly implied) filehandle
1N/A $infile = '-' unless ((defined $infile) && (length $infile));
1N/A if (($infile eq '-') || ($infile =~ /^<&(STDIN|0)$/i)) {
1N/A ## Not a filename, just a string implying STDIN
1N/A $myData{_INFILE} = "<standard input>";
1N/A $in_fh = \*STDIN;
1N/A }
1N/A elsif (ref $infile) {
1N/A ## Must be a filehandle-ref (or else assume its a ref to an object
1N/A ## that supports the common IO read operations).
1N/A $myData{_INFILE} = ${$infile};
1N/A $in_fh = $infile;
1N/A }
1N/A else {
1N/A ## We have a filename, open it for reading
1N/A $myData{_INFILE} = $infile;
1N/A open($in_fh, "< $infile") or
1N/A croak "Can't open $infile for reading: $!\n";
1N/A $close_input = 1;
1N/A }
1N/A
1N/A ## NOTE: we need to be *very* careful when "defaulting" the output
1N/A ## file. We only want to use a default if this is the beginning of
1N/A ## the entire document (but *not* if this is an included file). We
1N/A ## determine this by seeing if the input stream stack has been set-up
1N/A ## already
1N/A ##
1N/A unless ((defined $outfile) && (length $outfile)) {
1N/A (defined $myData{_TOP_STREAM}) && ($out_fh = $myData{_OUTPUT})
1N/A || ($outfile = '-');
1N/A }
1N/A ## Is $outfile a filename or a (possibly implied) filehandle
1N/A if ((defined $outfile) && (length $outfile)) {
1N/A if (($outfile eq '-') || ($outfile =~ /^>&?(?:STDOUT|1)$/i)) {
1N/A ## Not a filename, just a string implying STDOUT
1N/A $myData{_OUTFILE} = "<standard output>";
1N/A $out_fh = \*STDOUT;
1N/A }
1N/A elsif ($outfile =~ /^>&(STDERR|2)$/i) {
1N/A ## Not a filename, just a string implying STDERR
1N/A $myData{_OUTFILE} = "<standard error>";
1N/A $out_fh = \*STDERR;
1N/A }
1N/A elsif (ref $outfile) {
1N/A ## Must be a filehandle-ref (or else assume its a ref to an
1N/A ## object that supports the common IO write operations).
1N/A $myData{_OUTFILE} = ${$outfile};
1N/A $out_fh = $outfile;
1N/A }
1N/A else {
1N/A ## We have a filename, open it for writing
1N/A $myData{_OUTFILE} = $outfile;
1N/A (-d $outfile) and croak "$outfile is a directory, not POD input!\n";
1N/A open($out_fh, "> $outfile") or
1N/A croak "Can't open $outfile for writing: $!\n";
1N/A $close_output = 1;
1N/A }
1N/A }
1N/A
1N/A ## Whew! That was a lot of work to set up reasonably/robust behavior
1N/A ## in the case of a non-filename for reading and writing. Now we just
1N/A ## have to parse the input and close the handles when we're finished.
1N/A $self->parse_from_filehandle(\%opts, $in_fh, $out_fh);
1N/A
1N/A $close_input and
1N/A close($in_fh) || croak "Can't close $infile after reading: $!\n";
1N/A $close_output and
1N/A close($out_fh) || croak "Can't close $outfile after writing: $!\n";
1N/A}
1N/A
1N/A#############################################################################
1N/A
1N/A=head1 ACCESSOR METHODS
1N/A
1N/AClients of B<Pod::Parser> should use the following methods to access
1N/Ainstance data fields:
1N/A
1N/A=cut
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<errorsub()>
1N/A
1N/A $parser->errorsub("method_name");
1N/A $parser->errorsub(\&warn_user);
1N/A $parser->errorsub(sub { print STDERR, @_ });
1N/A
1N/ASpecifies the method or subroutine to use when printing error messages
1N/Aabout POD syntax. The supplied method/subroutine I<must> return TRUE upon
1N/Asuccessful printing of the message. If C<undef> is given, then the B<warn>
1N/Abuiltin is used to issue error messages (this is the default behavior).
1N/A
1N/A my $errorsub = $parser->errorsub()
1N/A my $errmsg = "This is an error message!\n"
1N/A (ref $errorsub) and &{$errorsub}($errmsg)
1N/A or (defined $errorsub) and $parser->$errorsub($errmsg)
1N/A or warn($errmsg);
1N/A
1N/AReturns a method name, or else a reference to the user-supplied subroutine
1N/Aused to print error messages. Returns C<undef> if the B<warn> builtin
1N/Ais used to issue error messages (this is the default behavior).
1N/A
1N/A=cut
1N/A
1N/Asub errorsub {
1N/A return (@_ > 1) ? ($_[0]->{_ERRORSUB} = $_[1]) : $_[0]->{_ERRORSUB};
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<cutting()>
1N/A
1N/A $boolean = $parser->cutting();
1N/A
1N/AReturns the current C<cutting> state: a boolean-valued scalar which
1N/Aevaluates to true if text from the input file is currently being "cut"
1N/A(meaning it is I<not> considered part of the POD document).
1N/A
1N/A $parser->cutting($boolean);
1N/A
1N/ASets the current C<cutting> state to the given value and returns the
1N/Aresult.
1N/A
1N/A=cut
1N/A
1N/Asub cutting {
1N/A return (@_ > 1) ? ($_[0]->{_CUTTING} = $_[1]) : $_[0]->{_CUTTING};
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<parseopts()>
1N/A
1N/AWhen invoked with no additional arguments, B<parseopts> returns a hashtable
1N/Aof all the current parsing options.
1N/A
1N/A ## See if we are parsing non-POD sections as well as POD ones
1N/A my %opts = $parser->parseopts();
1N/A $opts{'-want_nonPODs}' and print "-want_nonPODs\n";
1N/A
1N/AWhen invoked using a single string, B<parseopts> treats the string as the
1N/Aname of a parse-option and returns its corresponding value if it exists
1N/A(returns C<undef> if it doesn't).
1N/A
1N/A ## Did we ask to see '=cut' paragraphs?
1N/A my $want_cut = $parser->parseopts('-process_cut_cmd');
1N/A $want_cut and print "-process_cut_cmd\n";
1N/A
1N/AWhen invoked with multiple arguments, B<parseopts> treats them as
1N/Akey/value pairs and the specified parse-option names are set to the
1N/Agiven values. Any unspecified parse-options are unaffected.
1N/A
1N/A ## Set them back to the default
1N/A $parser->parseopts(-warnings => 0);
1N/A
1N/AWhen passed a single hash-ref, B<parseopts> uses that hash to completely
1N/Areset the existing parse-options, all previous parse-option values
1N/Aare lost.
1N/A
1N/A ## Reset all options to default
1N/A $parser->parseopts( { } );
1N/A
1N/ASee L<"PARSING OPTIONS"> for more information on the name and meaning of each
1N/Aparse-option currently recognized.
1N/A
1N/A=cut
1N/A
1N/Asub parseopts {
1N/A local *myData = shift;
1N/A local *myOpts = ($myData{_PARSEOPTS} ||= {});
1N/A return %myOpts if (@_ == 0);
1N/A if (@_ == 1) {
1N/A local $_ = shift;
1N/A return ref($_) ? $myData{_PARSEOPTS} = $_ : $myOpts{$_};
1N/A }
1N/A my @newOpts = (%myOpts, @_);
1N/A $myData{_PARSEOPTS} = { @newOpts };
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<output_file()>
1N/A
1N/A $fname = $parser->output_file();
1N/A
1N/AReturns the name of the output file being written.
1N/A
1N/A=cut
1N/A
1N/Asub output_file {
1N/A return $_[0]->{_OUTFILE};
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<output_handle()>
1N/A
1N/A $fhandle = $parser->output_handle();
1N/A
1N/AReturns the output filehandle object.
1N/A
1N/A=cut
1N/A
1N/Asub output_handle {
1N/A return $_[0]->{_OUTPUT};
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<input_file()>
1N/A
1N/A $fname = $parser->input_file();
1N/A
1N/AReturns the name of the input file being read.
1N/A
1N/A=cut
1N/A
1N/Asub input_file {
1N/A return $_[0]->{_INFILE};
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=head1 B<input_handle()>
1N/A
1N/A $fhandle = $parser->input_handle();
1N/A
1N/AReturns the current input filehandle object.
1N/A
1N/A=cut
1N/A
1N/Asub input_handle {
1N/A return $_[0]->{_INPUT};
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=begin __PRIVATE__
1N/A
1N/A=head1 B<input_streams()>
1N/A
1N/A $listref = $parser->input_streams();
1N/A
1N/AReturns a reference to an array which corresponds to the stack of all
1N/Athe input streams that are currently in the middle of being parsed.
1N/A
1N/AWhile parsing an input stream, it is possible to invoke
1N/AB<parse_from_file()> or B<parse_from_filehandle()> to parse a new input
1N/Astream and then return to parsing the previous input stream. Each input
1N/Astream to be parsed is pushed onto the end of this input stack
1N/Abefore any of its input is read. The input stream that is currently
1N/Abeing parsed is always at the end (or top) of the input stack. When an
1N/Ainput stream has been exhausted, it is popped off the end of the
1N/Ainput stack.
1N/A
1N/AEach element on this input stack is a reference to C<Pod::InputSource>
1N/Aobject. Please see L<Pod::InputObjects> for more details.
1N/A
1N/AThis method might be invoked when printing diagnostic messages, for example,
1N/Ato obtain the name and line number of the all input files that are currently
1N/Abeing processed.
1N/A
1N/A=end __PRIVATE__
1N/A
1N/A=cut
1N/A
1N/Asub input_streams {
1N/A return $_[0]->{_INPUT_STREAMS};
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=begin __PRIVATE__
1N/A
1N/A=head1 B<top_stream()>
1N/A
1N/A $hashref = $parser->top_stream();
1N/A
1N/AReturns a reference to the hash-table that represents the element
1N/Athat is currently at the top (end) of the input stream stack
1N/A(see L<"input_streams()">). The return value will be the C<undef>
1N/Aif the input stack is empty.
1N/A
1N/AThis method might be used when printing diagnostic messages, for example,
1N/Ato obtain the name and line number of the current input file.
1N/A
1N/A=end __PRIVATE__
1N/A
1N/A=cut
1N/A
1N/Asub top_stream {
1N/A return $_[0]->{_TOP_STREAM} || undef;
1N/A}
1N/A
1N/A#############################################################################
1N/A
1N/A=head1 PRIVATE METHODS AND DATA
1N/A
1N/AB<Pod::Parser> makes use of several internal methods and data fields
1N/Awhich clients should not need to see or use. For the sake of avoiding
1N/Aname collisions for client data and methods, these methods and fields
1N/Aare briefly discussed here. Determined hackers may obtain further
1N/Ainformation about them by reading the B<Pod::Parser> source code.
1N/A
1N/APrivate data fields are stored in the hash-object whose reference is
1N/Areturned by the B<new()> constructor for this class. The names of all
1N/Aprivate methods and data-fields used by B<Pod::Parser> begin with a
1N/Aprefix of "_" and match the regular expression C</^_\w+$/>.
1N/A
1N/A=cut
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=begin _PRIVATE_
1N/A
1N/A=head1 B<_push_input_stream()>
1N/A
1N/A $hashref = $parser->_push_input_stream($in_fh,$out_fh);
1N/A
1N/AThis method will push the given input stream on the input stack and
1N/Aperform any necessary beginning-of-document or beginning-of-file
1N/Aprocessing. The argument C<$in_fh> is the input stream filehandle to
1N/Apush, and C<$out_fh> is the corresponding output filehandle to use (if
1N/Ait is not given or is undefined, then the current output stream is used,
1N/Awhich defaults to standard output if it doesnt exist yet).
1N/A
1N/AThe value returned will be reference to the hash-table that represents
1N/Athe new top of the input stream stack. I<Please Note> that it is
1N/Apossible for this method to use default values for the input and output
1N/Afile handles. If this happens, you will need to look at the C<INPUT>
1N/Aand C<OUTPUT> instance data members to determine their new values.
1N/A
1N/A=end _PRIVATE_
1N/A
1N/A=cut
1N/A
1N/Asub _push_input_stream {
1N/A my ($self, $in_fh, $out_fh) = @_;
1N/A local *myData = $self;
1N/A
1N/A ## Initialize stuff for the entire document if this is *not*
1N/A ## an included file.
1N/A ##
1N/A ## NOTE: we need to be *very* careful when "defaulting" the output
1N/A ## filehandle. We only want to use a default value if this is the
1N/A ## beginning of the entire document (but *not* if this is an included
1N/A ## file).
1N/A unless (defined $myData{_TOP_STREAM}) {
1N/A $out_fh = \*STDOUT unless (defined $out_fh);
1N/A $myData{_CUTTING} = 1; ## current "cutting" state
1N/A $myData{_INPUT_STREAMS} = []; ## stack of all input streams
1N/A }
1N/A
1N/A ## Initialize input indicators
1N/A $myData{_OUTFILE} = '(unknown)' unless (defined $myData{_OUTFILE});
1N/A $myData{_OUTPUT} = $out_fh if (defined $out_fh);
1N/A $in_fh = \*STDIN unless (defined $in_fh);
1N/A $myData{_INFILE} = '(unknown)' unless (defined $myData{_INFILE});
1N/A $myData{_INPUT} = $in_fh;
1N/A my $input_top = $myData{_TOP_STREAM}
1N/A = new Pod::InputSource(
1N/A -name => $myData{_INFILE},
1N/A -handle => $in_fh,
1N/A -was_cutting => $myData{_CUTTING}
1N/A );
1N/A local *input_stack = $myData{_INPUT_STREAMS};
1N/A push(@input_stack, $input_top);
1N/A
1N/A ## Perform beginning-of-document and/or beginning-of-input processing
1N/A $self->begin_pod() if (@input_stack == 1);
1N/A $self->begin_input();
1N/A
1N/A return $input_top;
1N/A}
1N/A
1N/A##---------------------------------------------------------------------------
1N/A
1N/A=begin _PRIVATE_
1N/A
1N/A=head1 B<_pop_input_stream()>
1N/A
1N/A $hashref = $parser->_pop_input_stream();
1N/A
1N/AThis takes no arguments. It will perform any necessary end-of-file or
1N/Aend-of-document processing and then pop the current input stream from
1N/Athe top of the input stack.
1N/A
1N/AThe value returned will be reference to the hash-table that represents
1N/Athe new top of the input stream stack.
1N/A
1N/A=end _PRIVATE_
1N/A
1N/A=cut
1N/A
1N/Asub _pop_input_stream {
1N/A my ($self) = @_;
1N/A local *myData = $self;
1N/A local *input_stack = $myData{_INPUT_STREAMS};
1N/A
1N/A ## Perform end-of-input and/or end-of-document processing
1N/A $self->end_input() if (@input_stack > 0);
1N/A $self->end_pod() if (@input_stack == 1);
1N/A
1N/A ## Restore cutting state to whatever it was before we started
1N/A ## parsing this file.
1N/A my $old_top = pop(@input_stack);
1N/A $myData{_CUTTING} = $old_top->was_cutting();
1N/A
1N/A ## Dont forget to reset the input indicators
1N/A my $input_top = undef;
1N/A if (@input_stack > 0) {
1N/A $input_top = $myData{_TOP_STREAM} = $input_stack[-1];
1N/A $myData{_INFILE} = $input_top->name();
1N/A $myData{_INPUT} = $input_top->handle();
1N/A } else {
1N/A delete $myData{_TOP_STREAM};
1N/A delete $myData{_INPUT_STREAMS};
1N/A }
1N/A
1N/A return $input_top;
1N/A}
1N/A
1N/A#############################################################################
1N/A
1N/A=head1 TREE-BASED PARSING
1N/A
1N/AIf straightforward stream-based parsing wont meet your needs (as is
1N/Alikely the case for tasks such as translating PODs into structured
1N/Amarkup languages like HTML and XML) then you may need to take the
1N/Atree-based approach. Rather than doing everything in one pass and
1N/Acalling the B<interpolate()> method to expand sequences into text, it
1N/Amay be desirable to instead create a parse-tree using the B<parse_text()>
1N/Amethod to return a tree-like structure which may contain an ordered
1N/Alist of children (each of which may be a text-string, or a similar
1N/Atree-like structure).
1N/A
1N/APay special attention to L<"METHODS FOR PARSING AND PROCESSING"> and
1N/Ato the objects described in L<Pod::InputObjects>. The former describes
1N/Athe gory details and parameters for how to customize and extend the
1N/Aparsing behavior of B<Pod::Parser>. B<Pod::InputObjects> provides
1N/Aseveral objects that may all be used interchangeably as parse-trees. The
1N/Amost obvious one is the B<Pod::ParseTree> object. It defines the basic
1N/Ainterface and functionality that all things trying to be a POD parse-tree
1N/Ashould do. A B<Pod::ParseTree> is defined such that each "node" may be a
1N/Atext-string, or a reference to another parse-tree. Each B<Pod::Paragraph>
1N/Aobject and each B<Pod::InteriorSequence> object also supports the basic
1N/Aparse-tree interface.
1N/A
1N/AThe B<parse_text()> method takes a given paragraph of text, and
1N/Areturns a parse-tree that contains one or more children, each of which
1N/Amay be a text-string, or an InteriorSequence object. There are also
1N/Acallback-options that may be passed to B<parse_text()> to customize
1N/Athe way it expands or transforms interior-sequences, as well as the
1N/Areturned result. These callbacks can be used to create a parse-tree
1N/Awith custom-made objects (which may or may not support the parse-tree
1N/Ainterface, depending on how you choose to do it).
1N/A
1N/AIf you wish to turn an entire POD document into a parse-tree, that process
1N/Ais fairly straightforward. The B<parse_text()> method is the key to doing
1N/Athis successfully. Every paragraph-callback (i.e. the polymorphic methods
1N/Afor B<command()>, B<verbatim()>, and B<textblock()> paragraphs) takes
1N/Aa B<Pod::Paragraph> object as an argument. Each paragraph object has a
1N/AB<parse_tree()> method that can be used to get or set a corresponding
1N/Aparse-tree. So for each of those paragraph-callback methods, simply call
1N/AB<parse_text()> with the options you desire, and then use the returned
1N/Aparse-tree to assign to the given paragraph object.
1N/A
1N/AThat gives you a parse-tree for each paragraph - so now all you need is
1N/Aan ordered list of paragraphs. You can maintain that yourself as a data
1N/Aelement in the object/hash. The most straightforward way would be simply
1N/Ato use an array-ref, with the desired set of custom "options" for each
1N/Ainvocation of B<parse_text>. Let's assume the desired option-set is
1N/Agiven by the hash C<%options>. Then we might do something like the
1N/Afollowing:
1N/A
1N/A package MyPodParserTree;
1N/A
1N/A @ISA = qw( Pod::Parser );
1N/A
1N/A ...
1N/A
1N/A sub begin_pod {
1N/A my $self = shift;
1N/A $self->{'-paragraphs'} = []; ## initialize paragraph list
1N/A }
1N/A
1N/A sub command {
1N/A my ($parser, $command, $paragraph, $line_num, $pod_para) = @_;
1N/A my $ptree = $parser->parse_text({%options}, $paragraph, ...);
1N/A $pod_para->parse_tree( $ptree );
1N/A push @{ $self->{'-paragraphs'} }, $pod_para;
1N/A }
1N/A
1N/A sub verbatim {
1N/A my ($parser, $paragraph, $line_num, $pod_para) = @_;
1N/A push @{ $self->{'-paragraphs'} }, $pod_para;
1N/A }
1N/A
1N/A sub textblock {
1N/A my ($parser, $paragraph, $line_num, $pod_para) = @_;
1N/A my $ptree = $parser->parse_text({%options}, $paragraph, ...);
1N/A $pod_para->parse_tree( $ptree );
1N/A push @{ $self->{'-paragraphs'} }, $pod_para;
1N/A }
1N/A
1N/A ...
1N/A
1N/A package main;
1N/A ...
1N/A my $parser = new MyPodParserTree(...);
1N/A $parser->parse_from_file(...);
1N/A my $paragraphs_ref = $parser->{'-paragraphs'};
1N/A
1N/AOf course, in this module-author's humble opinion, I'd be more inclined to
1N/Ause the existing B<Pod::ParseTree> object than a simple array. That way
1N/Aeverything in it, paragraphs and sequences, all respond to the same core
1N/Ainterface for all parse-tree nodes. The result would look something like:
1N/A
1N/A package MyPodParserTree2;
1N/A
1N/A ...
1N/A
1N/A sub begin_pod {
1N/A my $self = shift;
1N/A $self->{'-ptree'} = new Pod::ParseTree; ## initialize parse-tree
1N/A }
1N/A
1N/A sub parse_tree {
1N/A ## convenience method to get/set the parse-tree for the entire POD
1N/A (@_ > 1) and $_[0]->{'-ptree'} = $_[1];
1N/A return $_[0]->{'-ptree'};
1N/A }
1N/A
1N/A sub command {
1N/A my ($parser, $command, $paragraph, $line_num, $pod_para) = @_;
1N/A my $ptree = $parser->parse_text({<<options>>}, $paragraph, ...);
1N/A $pod_para->parse_tree( $ptree );
1N/A $parser->parse_tree()->append( $pod_para );
1N/A }
1N/A
1N/A sub verbatim {
1N/A my ($parser, $paragraph, $line_num, $pod_para) = @_;
1N/A $parser->parse_tree()->append( $pod_para );
1N/A }
1N/A
1N/A sub textblock {
1N/A my ($parser, $paragraph, $line_num, $pod_para) = @_;
1N/A my $ptree = $parser->parse_text({<<options>>}, $paragraph, ...);
1N/A $pod_para->parse_tree( $ptree );
1N/A $parser->parse_tree()->append( $pod_para );
1N/A }
1N/A
1N/A ...
1N/A
1N/A package main;
1N/A ...
1N/A my $parser = new MyPodParserTree2(...);
1N/A $parser->parse_from_file(...);
1N/A my $ptree = $parser->parse_tree;
1N/A ...
1N/A
1N/ANow you have the entire POD document as one great big parse-tree. You
1N/Acan even use the B<-expand_seq> option to B<parse_text> to insert
1N/Awhole different kinds of objects. Just don't expect B<Pod::Parser>
1N/Ato know what to do with them after that. That will need to be in your
1N/Acode. Or, alternatively, you can insert any object you like so long as
1N/Ait conforms to the B<Pod::ParseTree> interface.
1N/A
1N/AOne could use this to create subclasses of B<Pod::Paragraphs> and
1N/AB<Pod::InteriorSequences> for specific commands (or to create your own
1N/Acustom node-types in the parse-tree) and add some kind of B<emit()>
1N/Amethod to each custom node/subclass object in the tree. Then all you'd
1N/Aneed to do is recursively walk the tree in the desired order, processing
1N/Athe children (most likely from left to right) by formatting them if
1N/Athey are text-strings, or by calling their B<emit()> method if they
1N/Aare objects/references.
1N/A
1N/A=head1 SEE ALSO
1N/A
1N/AL<Pod::InputObjects>, L<Pod::Select>
1N/A
1N/AB<Pod::InputObjects> defines POD input objects corresponding to
1N/Acommand paragraphs, parse-trees, and interior-sequences.
1N/A
1N/AB<Pod::Select> is a subclass of B<Pod::Parser> which provides the ability
1N/Ato selectively include and/or exclude sections of a POD document from being
1N/Atranslated based upon the current heading, subheading, subsubheading, etc.
1N/A
1N/A=for __PRIVATE__
1N/AB<Pod::Callbacks> is a subclass of B<Pod::Parser> which gives its users
1N/Athe ability the employ I<callback functions> instead of, or in addition
1N/Ato, overriding methods of the base class.
1N/A
1N/A=for __PRIVATE__
1N/AB<Pod::Select> and B<Pod::Callbacks> do not override any
1N/Amethods nor do they define any new methods with the same name. Because
1N/Aof this, they may I<both> be used (in combination) as a base class of
1N/Athe same subclass in order to combine their functionality without
1N/Acausing any namespace clashes due to multiple inheritance.
1N/A
1N/A=head1 AUTHOR
1N/A
1N/APlease report bugs using L<http://rt.cpan.org>.
1N/A
1N/ABrad Appleton E<lt>bradapp@enteract.comE<gt>
1N/A
1N/ABased on code for B<Pod::Text> written by
1N/ATom Christiansen E<lt>tchrist@mox.perl.comE<gt>
1N/A
1N/A=cut
1N/A
1N/A1;