$VERSION
=
'3.38'
;
sub
my_qr ($$) {
my
(
$input_re
,
$should_match
) =
@_
;
my
$use_utf8
= ($] le 5.006002) ?
'use utf8;'
:
""
;
my
$re
=
eval
"no warnings; $use_utf8 qr/$input_re/"
;
return
""
if
$@;
my
$matches
=
eval
"no warnings; $use_utf8 '$should_match' =~ /$re/"
;
return
""
if
$@;
return
$re
if
$matches
;
return
""
;
}
BEGIN {
*DEBUG
= \
&Pod::Simple::DEBUG
unless
defined
&DEBUG
}
my
$non_ascii_re
= my_qr(
'[[:^ascii:]]'
,
"\xB6"
);
$non_ascii_re
=
qr/[\x80-\xFF]/
unless
$non_ascii_re
;
my
$cs_re
= my_qr(
'\p{IsCs}'
,
"\x{D800}"
);
my
$cn_re
= my_qr(
'\p{IsCn}'
,
"\x{09E4}"
);
my
$rare_blocks_re
= my_qr(
'[\p{InIPAExtensions}\p{InSpacingModifierLetters}]'
,
"\x{250}"
);
$rare_blocks_re
= my_qr(
'[\x{0250}-\x{02FF}]'
,
"\x{250}"
)
unless
$rare_blocks_re
;
my
$script_run_re
=
eval
'
no
warnings
"experimental::script_run"
;
qr/(*script_run: ^ .* $ )/
x';
my
$latin_re
= my_qr(
'[\p{IsLatin}\p{IsInherited}\p{IsCommon}]'
,
"\x{100}"
);
unless
(
$latin_re
) {
$latin_re
= my_qr(
'[\x00-\x{02E9}\x{02EC}-\x{0374}\x{037E}\x{0385}\x{0387}\x{0485}\x{0486}\x{0589}\x{060C}\x{061B}\x{061F}\x{0640}\x{064B}-\x{0655}\x{0670}\x{06DD}\x{0951}-\x{0954}\x{0964}\x{0965}\x{0E3F}\x{10FB}\x{16EB}-\x{16ED}\x{1735}\x{1736}\x{1802}\x{1803}\x{1805}\x{1D00}-\x{1D25}\x{1D2C}-\x{1D5C}\x{1D62}-\x{1D65}\x{1D6B}-\x{1D77}\x{1D79}-\x{1DBE}\x{1DC0}-\x{1EF9}\x{2000}-\x{2125}\x{2127}-\x{27FF}\x{2900}-\x{2B13}\x{2E00}-\x{2E1D}\x{2FF0}-\x{3004}\x{3006}\x{3008}-\x{3020}\x{302A}-\x{302D}\x{3030}-\x{3037}\x{303C}-\x{303F}\x{3099}-\x{309C}\x{30A0}\x{30FB}\x{30FC}\x{3190}-\x{319F}\x{31C0}-\x{31CF}\x{3220}-\x{325F}\x{327F}-\x{32CF}\x{3358}-\x{33FF}\x{4DC0}-\x{4DFF}\x{A700}-\x{A716}\x{FB00}-\x{FB06}\x{FD3E}\x{FD3F}\x{FE00}-\x{FE6B}\x{FEFF}-\x{FF65}\x{FF70}\x{FF9E}\x{FF9F}\x{FFE0}-\x{FFFD}\x{10100}-\x{1013F}\x{1D000}-\x{1D1DD}\x{1D300}-\x{1D7FF}]'
,
"\x{100}"
);
}
my
$every_char_is_latin_re
= my_qr(
"^(?:$latin_re)*\\z"
,
"A"
);
my
$later_latin_re
= my_qr(
'[^\P{IsLatin}\p{IsAge=1.1}]'
,
"\x{1F6}"
);
my
$deprecated_re
= my_qr(
'\p{IsDeprecated}'
,
"\x{149}"
);
$deprecated_re
=
qr/\x{149}/
unless
$deprecated_re
;
my
$utf8_bom
;
if
(($] ge 5.007_003)) {
$utf8_bom
=
"\x{FEFF}"
;
utf8::encode(
$utf8_bom
);
}
else
{
$utf8_bom
=
"\xEF\xBB\xBF"
;
}
sub
parse_line {
shift
->parse_lines(
@_
) }
sub
parse_lines {
my
$self
=
shift
;
my
$code_handler
=
$self
->{
'code_handler'
};
my
$cut_handler
=
$self
->{
'cut_handler'
};
my
$wl_handler
=
$self
->{
'whiteline_handler'
};
$self
->{
'line_count'
} ||= 0;
my
$scratch
;
DEBUG > 4 and
print
STDERR
"# Parsing starting at line "
,
$self
->{
'line_count'
},
".\n"
;
DEBUG > 5 and
print
STDERR
"# About to parse lines: "
,
join
(
' '
,
map
defined
(
$_
) ?
"[$_]"
:
"EOF"
,
@_
),
"\n"
;
my
$paras
= (
$self
->{
'paras'
} ||= []);
$self
->{
'pod_para_count'
} ||= 0;
my
$format_codes
=
join
""
,
'['
,
grep
{ / ^ [A-Za-z] $/x }
keys
%{
$self
->{accept_codes}};
$format_codes
.=
']'
;
my
$pod_chars_re
=
qr/ ^ = [A-Za-z]+ | $format_codes < /
x;
my
$line
;
foreach
my
$source_line
(
@_
) {
if
(
$self
->{
'source_dead'
} ) {
DEBUG > 4 and
print
STDERR
"# Source is dead.\n"
;
last
;
}
unless
(
defined
$source_line
) {
DEBUG > 4 and
print
STDERR
"# Undef-line seen.\n"
;
push
@$paras
, [
'~end'
, {
'start_line'
=>
$self
->{
'line_count'
}}];
push
@$paras
,
$paras
->[-1],
$paras
->[-1];
$self
->{
'source_dead'
} = 1;
$self
->_ponder_paragraph_buffer;
next
;
}
if
(
$self
->{
'line_count'
}++ ) {
(
$line
=
$source_line
) =~
tr
/\n\r//d;
}
else
{
DEBUG > 2 and
print
STDERR
"First line: [$source_line]\n"
;
if
( (
$line
=
$source_line
) =~ s/^
$utf8_bom
//s ) {
DEBUG and
print
STDERR
"UTF-8 BOM seen. Faking a '=encoding utf8'.\n"
;
$self
->_handle_encoding_line(
"=encoding utf8"
);
delete
$self
->{
'_processed_encoding'
};
$line
=~
tr
/\n\r//d;
}
elsif
(
$line
=~ s/^\xFE\xFF//s ) {
DEBUG and
print
STDERR
"Big-endian UTF-16 BOM seen. Aborting parsing.\n"
;
$self
->scream(
$self
->{
'line_count'
},
"UTF16-BE Byte Encoding Mark found; but Pod::Simple v$Pod::Simple::VERSION doesn't implement UTF16 yet."
);
splice
@_
;
push
@_
,
undef
;
next
;
}
elsif
(
$line
=~ s/^\xFF\xFE//s ) {
DEBUG and
print
STDERR
"Little-endian UTF-16 BOM seen. Aborting parsing.\n"
;
$self
->scream(
$self
->{
'line_count'
},
"UTF16-LE Byte Encoding Mark found; but Pod::Simple v$Pod::Simple::VERSION doesn't implement UTF16 yet."
);
splice
@_
;
push
@_
,
undef
;
next
;
}
else
{
DEBUG > 2 and
print
STDERR
"First line is BOM-less.\n"
;
(
$line
=
$source_line
) =~
tr
/\n\r//d;
}
}
if
(!
$self
->{
'parse_characters'
} && !
$self
->{
'encoding'
}
&& (
$self
->{
'in_pod'
} ||
$line
=~ /^=/s)
&&
$line
=~ /
$non_ascii_re
/
) {
my
$encoding
;
goto
set_1252
if
$] lt 5.006_000;
my
$copy
;
no
warnings
'utf8'
;
if
($] ge 5.007_003) {
$copy
=
$line
;
goto
set_1252
if
! utf8::decode(
$copy
);
}
elsif
(
ord
(
"A"
) != 65) {
goto
set_utf8;
}
else
{
use
if
$] le 5.006002,
'utf8'
;
my
$char_ord
;
my
$needed
;
$copy
=
chr
(0x100);
for
(
my
$i
= 0;
$i
<
length
$line
;
$i
++) {
my
$byte
=
substr
(
$line
,
$i
, 1);
if
(
$byte
!~
$non_ascii_re
) {
$copy
.=
$byte
;
next
;
}
my
$b_ord
=
ord
$byte
;
my
$min_cont
= 0x80;
if
(
$b_ord
< 0xC2) {
goto
set_1252;
}
elsif
(
$b_ord
<= 0xDF) {
$needed
= 1;
$char_ord
=
$b_ord
& 0x1F;
}
elsif
(
$b_ord
<= 0xEF) {
$min_cont
= 0xA0
if
$b_ord
== 0xE0;
$needed
= 2;
$char_ord
=
$b_ord
& (0x1F >> 1);
}
elsif
(
$b_ord
<= 0xF4) {
$min_cont
= 0x90
if
$b_ord
== 0xF0;
$needed
= 3;
$char_ord
=
$b_ord
& (0x1F >> 2);
}
else
{
goto
set_1252;
}
goto
set_1252
if
$i
+
$needed
>=
length
$line
;
while
(
$needed
-- > 0) {
my
$cont
=
substr
(
$line
, ++
$i
, 1);
$b_ord
=
ord
$cont
;
goto
set_1252
if
$b_ord
<
$min_cont
||
$b_ord
> 0xBF;
$min_cont
= 0x80;
$char_ord
<<= 6;
$char_ord
|= (
$b_ord
& 0x3F);
}
$copy
.=
chr
$char_ord
;
}
$copy
=
substr
(
$copy
, 1);
}
goto
set_utf8
if
ord
(
"A"
) == 65 &&
$line
=~ /[\x81\x8D\x8F\x90\x9D]/;
goto
set_1252
if
ord
(
"A"
) == 65 &&
$copy
=~ /[\x80-\x9F]/;
DEBUG > 8 and
print
STDERR __LINE__,
": $copy: surrogate\n"
if
$copy
=~
$cs_re
;
goto
set_1252
if
$cs_re
&&
$copy
=~
$cs_re
;
DEBUG > 8 and
print
STDERR __LINE__,
": $copy: unassigned\n"
if
$cn_re
&&
$copy
=~
$cn_re
;
goto
set_1252
if
$cn_re
&&
$copy
=~
$cn_re
;
DEBUG > 8 and
print
STDERR __LINE__,
": $copy: deprecated\n"
if
$copy
=~
$deprecated_re
;
goto
set_1252
if
$copy
=~
$deprecated_re
;
DEBUG > 8 and
print
STDERR __LINE__,
": $copy: rare\n"
if
$copy
=~
$rare_blocks_re
;
goto
set_1252
if
$rare_blocks_re
&&
$copy
=~
$rare_blocks_re
;
DEBUG > 8 and
print
STDERR __LINE__,
": $copy: later_latin\n"
if
$later_latin_re
&&
$copy
=~
$later_latin_re
;
goto
set_1252
if
$later_latin_re
&&
$copy
=~
$later_latin_re
;
$copy
=~ s/
$pod_chars_re
//g;
if
(
$script_run_re
) {
goto
set_utf8
if
$copy
=~
$script_run_re
;
DEBUG > 8 and
print
STDERR __LINE__,
": not script run\n"
;
goto
set_1252;
}
DEBUG > 8 and
print
STDERR __LINE__,
": $copy: not latin\n"
if
$copy
!~
$latin_re
;
goto
set_utf8
if
$copy
!~
$latin_re
;
DEBUG > 8 and
print
STDERR __LINE__,
": $copy: all latin\n"
if
$copy
=~
$every_char_is_latin_re
;
goto
set_utf8
if
$copy
=~
$every_char_is_latin_re
;
DEBUG > 8 and
print
STDERR __LINE__,
": $copy: mixed\n"
;
set_1252:
DEBUG > 9 and
print
STDERR __LINE__,
": $copy: is 1252\n"
;
$encoding
=
'CP1252'
;
goto
done_set;
set_utf8:
DEBUG > 9 and
print
STDERR __LINE__,
": $copy: is UTF-8\n"
;
$encoding
=
'UTF-8'
;
done_set:
$self
->_handle_encoding_line(
"=encoding $encoding"
);
delete
$self
->{
'_processed_encoding'
};
$self
->{
'_transcoder'
} &&
$self
->{
'_transcoder'
}->(
$line
);
my
(
$word
) =
$line
=~ /(\S
*$non_ascii_re
\S*)/;
$self
->whine(
$self
->{
'line_count'
},
"Non-ASCII character seen before =encoding in '$word'. Assuming $encoding"
);
}
DEBUG > 5 and
print
STDERR
"# Parsing line: [$line]\n"
;
if
(!
$self
->{
'in_pod'
}) {
if
(
$line
=~ m/^=([a-zA-Z][a-zA-Z0-9]*)(?:\s|$)/s) {
if
($1 eq
'cut'
) {
$self
->scream(
$self
->{
'line_count'
},
"=cut found outside a pod block. Skipping to next block."
);
next
;
}
else
{
$self
->{
'in_pod'
} =
$self
->{
'start_of_pod_block'
}
=
$self
->{
'last_was_blank'
} = 1;
}
}
else
{
DEBUG > 5 and
print
STDERR
"# It's a code-line.\n"
;
$code_handler
->(
map
$_
,
$line
,
$self
->{
'line_count'
},
$self
)
if
$code_handler
;
if
(
$line
=~ m/^
DEBUG > 1 and
print
STDERR
"# Setting nextline to $1\n"
;
$self
->{
'line_count'
} = $1 - 1;
}
next
;
}
}
$self
->{
'_transcoder'
} &&
$self
->{
'_transcoder'
}->(
$line
);
if
(
$line
=~ m/^=encoding\s+\S+\s*$/s ) {
next
if
$self
->parse_characters;
$line
=
$self
->_handle_encoding_line(
$line
);
}
if
(
$line
=~ m/^=cut/s) {
DEBUG > 1 and
print
STDERR
"Noting =cut at line ${$self}{'line_count'}\n"
;
$self
->{
'in_pod'
} = 0;
$self
->_ponder_paragraph_buffer();
DEBUG > 6 and
print
STDERR
"Processing any cut handler, line ${$self}{'line_count'}\n"
;
$cut_handler
->(
map
$_
,
$line
,
$self
->{
'line_count'
},
$self
)
if
$cut_handler
;
}
elsif
(
$line
=~ m/^(\s*)$/s) {
if
(
defined
$1 and $1 =~ /[^\S\r\n]/) {
$wl_handler
->(
map
$_
,
$line
,
$self
->{
'line_count'
},
$self
)
if
$wl_handler
;
}
if
(!
$self
->{
'start_of_pod_block'
} and
@$paras
and
$paras
->[-1][0] eq
'~Verbatim'
) {
DEBUG > 1 and
print
STDERR
"Saving blank line at line ${$self}{'line_count'}\n"
;
push
@{
$paras
->[-1]},
$line
;
}
if
(!
$self
->{
'start_of_pod_block'
} and !
$self
->{
'last_was_blank'
}) {
DEBUG > 1 and
print
STDERR
"Noting para ends with blank line at ${$self}{'line_count'}\n"
;
}
$self
->{
'last_was_blank'
} = 1;
}
elsif
(
$self
->{
'last_was_blank'
}) {
if
(
$line
=~ m/^(=[a-zA-Z][a-zA-Z0-9]*)(\s+|$)(.*)/s) {
my
$new
= [$1, {
'start_line'
=>
$self
->{
'line_count'
}}, $3];
$new
->[1]{
'~orig_spacer'
} = $2
if
$2 && $2 ne
" "
;
++
$self
->{
'pod_para_count'
};
$self
->_ponder_paragraph_buffer();
push
@$paras
,
$new
;
DEBUG > 1 and
print
STDERR
"Starting new ${$paras}[-1][0] para at line ${$self}{'line_count'}\n"
;
}
elsif
(
$line
=~ m/^\s/s) {
if
(!
$self
->{
'start_of_pod_block'
} and
@$paras
and
$paras
->[-1][0] eq
'~Verbatim'
) {
DEBUG > 1 and
print
STDERR
"Resuming verbatim para at line ${$self}{'line_count'}\n"
;
push
@{
$paras
->[-1]},
$line
;
}
else
{
++
$self
->{
'pod_para_count'
};
$self
->_ponder_paragraph_buffer();
DEBUG > 1 and
print
STDERR
"Starting verbatim para at line ${$self}{'line_count'}\n"
;
push
@$paras
, [
'~Verbatim'
, {
'start_line'
=>
$self
->{
'line_count'
}},
$line
];
}
}
else
{
++
$self
->{
'pod_para_count'
};
$self
->_ponder_paragraph_buffer();
push
@$paras
, [
'~Para'
, {
'start_line'
=>
$self
->{
'line_count'
}},
$line
];
DEBUG > 1 and
print
STDERR
"Starting plain para at line ${$self}{'line_count'}\n"
;
}
$self
->{
'last_was_blank'
} =
$self
->{
'start_of_pod_block'
} = 0;
}
else
{
if
(
@$paras
) {
DEBUG > 2 and
print
STDERR
"Line ${$self}{'line_count'} continues current paragraph\n"
;
push
@{
$paras
->[-1]},
$line
;
}
else
{
die
"Continuing a paragraph but \@\$paras is empty?"
;
}
$self
->{
'last_was_blank'
} =
$self
->{
'start_of_pod_block'
} = 0;
}
}
DEBUG > 1 and
print
STDERR (pretty(
@$paras
),
"\n"
);
return
$self
;
}
sub
_handle_encoding_line {
my
(
$self
,
$line
) =
@_
;
return
if
$self
->parse_characters;
return
$line
unless
$line
=~ m/^=encoding\s+(\S+)\s*$/s;
DEBUG > 1 and
print
STDERR
"Found an encoding line \"=encoding $1\"\n"
;
my
$e
= $1;
my
$orig
=
$e
;
push
@{
$self
->{
'encoding_command_reqs'
} },
"=encoding $orig"
;
my
$enc_error
;
if
(
$self
->{
'encoding'
} ) {
my
$norm_current
=
$self
->{
'encoding'
};
my
$norm_e
=
$e
;
foreach
my
$that
(
$norm_current
,
$norm_e
) {
$that
=
lc
(
$that
);
$that
=~ s/[-_]//g;
}
if
(
$norm_current
eq
$norm_e
) {
DEBUG > 1 and
print
STDERR
"The '=encoding $orig' line is "
,
"redundant. ($norm_current eq $norm_e). Ignoring.\n"
;
$enc_error
=
''
;
}
else
{
$enc_error
=
"Encoding is already set to "
.
$self
->{
'encoding'
};
DEBUG > 1 and
print
STDERR
$enc_error
;
}
}
elsif
(
do
{
DEBUG > 1 and
print
STDERR
" Setting encoding to $e\n"
;
$self
->{
'encoding'
} =
$e
;
1;
}
and
$e
eq
'HACKRAW'
) {
DEBUG and
print
STDERR
" Putting in HACKRAW (no-op) encoding mode.\n"
;
}
elsif
( Pod::Simple::Transcode::->encoding_is_available(
$e
) ) {
die
(
$enc_error
=
"WHAT? _transcoder is already set?!"
)
if
$self
->{
'_transcoder'
};
$self
->{
'_transcoder'
} = Pod::Simple::Transcode::->make_transcoder(
$e
);
eval
{
my
@x
= (
''
,
"abc"
,
"123"
);
$self
->{
'_transcoder'
}->(
@x
);
};
$@ &&
die
(
$enc_error
=
"Really unexpected error setting up encoding $e: $@\nAborting"
);
$self
->{
'detected_encoding'
} =
$e
;
}
else
{
my
@supported
= Pod::Simple::Transcode::->all_encodings;
DEBUG and
print
STDERR
" Encoding [$e] is unsupported."
,
"\nSupporteds: @supported\n"
;
my
$suggestion
=
''
;
my
$norm
=
lc
(
$e
);
$norm
=~
tr
[-_][]d;
my
$n
;
foreach
my
$enc
(
@supported
) {
$n
=
lc
(
$enc
);
$n
=~
tr
[-_][]d;
next
unless
$n
eq
$norm
;
$suggestion
=
" (Maybe \"$e\" should be \"$enc\"?)"
;
last
;
}
my
$encmodver
= Pod::Simple::Transcode::->encmodver;
$enc_error
=
join
''
=>
"This document probably does not appear as it should, because its "
,
"\"=encoding $e\" line calls for an unsupported encoding."
,
$suggestion
,
" [$encmodver\'s supported encodings are: @supported]"
;
$self
->scream(
$self
->{
'line_count'
},
$enc_error
);
}
push
@{
$self
->{
'encoding_command_statuses'
} },
$enc_error
;
if
(
defined
(
$self
->{
'_processed_encoding'
})) {
$self
->scream(
$self
->{
'line_count'
},
'Cannot have multiple =encoding directives'
);
}
$self
->{
'_processed_encoding'
} =
$orig
;
return
$line
;
}
sub
_handle_encoding_second_level {
my
(
$self
,
$para
) =
@_
;
my
@x
=
@$para
;
my
$content
=
join
' '
,
splice
@x
, 2;
$content
=~ s/^\s+//s;
$content
=~ s/\s+$//s;
DEBUG > 2 and
print
STDERR
"Ogling encoding directive: =encoding $content\n"
;
if
(
defined
(
$self
->{
'_processed_encoding'
})) {
delete
$self
->{
'_processed_encoding'
};
if
(!
$self
->{
'encoding_command_statuses'
} ) {
DEBUG > 2 and
print
STDERR
" CRAZY ERROR: It wasn't really handled?!\n"
;
}
elsif
(
$self
->{
'encoding_command_statuses'
}[-1] ) {
$self
->whine(
$para
->[1]{
'start_line'
},
sprintf
"Couldn't do %s: %s"
,
$self
->{
'encoding_command_reqs'
}[-1],
$self
->{
'encoding_command_statuses'
}[-1],
);
}
else
{
DEBUG > 2 and
print
STDERR
" (Yup, it was successfully handled already.)\n"
;
}
}
else
{
$self
->whine(
$para
->[1]{
'start_line'
},
"Invalid =encoding syntax: $content"
);
}
return
;
}
{
my
$m
= -321;
sub
_gen_errata {
my
$self
=
$_
[0];
return
()
unless
$self
->{
'errata'
} and
keys
%{
$self
->{
'errata'
}};
my
@out
;
foreach
my
$line
(
sort
{
$a
<=>
$b
}
keys
%{
$self
->{
'errata'
}}) {
push
@out
,
[
'=item'
, {
'start_line'
=>
$m
},
"Around line $line:"
],
map
( [
'~Para'
, {
'start_line'
=>
$m
,
'~cooked'
=> 1},
$_
],
@{
$self
->{
'errata'
}{
$line
}}
)
;
}
unshift
@out
,
[
'=head1'
, {
'start_line'
=>
$m
,
'errata'
=> 1},
'POD ERRORS'
],
[
'~Para'
, {
'start_line'
=>
$m
,
'~cooked'
=> 1,
'errata'
=> 1},
"Hey! "
,
[
'B'
, {},
'The above document had some coding errors, which are explained below:'
]
],
[
'=over'
, {
'start_line'
=>
$m
,
'errata'
=> 1},
''
],
;
push
@out
,
[
'=back'
, {
'start_line'
=>
$m
,
'errata'
=> 1},
''
],
;
DEBUG and
print
STDERR
"\n<<\n"
, pretty(\
@out
),
"\n>>\n\n"
;
return
@out
;
}
}
sub
_ponder_paragraph_buffer {
my
$self
=
$_
[0];
my
$paras
;
return
unless
@{
$paras
=
$self
->{
'paras'
}};
my
$curr_open
= (
$self
->{
'curr_open'
} ||= []);
my
$scratch
;
DEBUG > 10 and
print
STDERR
"# Paragraph buffer: <<"
, pretty(
$paras
),
">>\n"
;
unless
(
$self
->{
'doc_has_started'
}) {
$self
->{
'doc_has_started'
} = 1;
my
$starting_contentless
;
$starting_contentless
=
(
!
@$curr_open
and
@$paras
and !
grep
$_
->[0] ne
'~end'
,
@$paras
)
;
DEBUG and
print
STDERR
"# Starting "
,
$starting_contentless
?
'contentless'
:
'contentful'
,
" document\n"
;
$self
->_handle_element_start(
(
$scratch
=
'Document'
),
{
'start_line'
=>
$paras
->[0][1]{
'start_line'
},
$starting_contentless
? (
'contentless'
=> 1 ) : (),
},
);
}
my
(
$para
,
$para_type
);
while
(
@$paras
) {
last
if
@$paras
== 1
and (
$paras
->[0][0] eq
'=over'
or
$paras
->[0][0] eq
'=item'
or (
$paras
->[0][0] eq
'~Verbatim'
and
$self
->{
'in_pod'
}));
$para
=
shift
@$paras
;
$para_type
=
$para
->[0];
DEBUG > 1 and
print
STDERR
"Pondering a $para_type paragraph, given the stack: ("
,
$self
->_dump_curr_open(),
")\n"
;
if
(
$para_type
eq
'=for'
) {
next
if
$self
->_ponder_for(
$para
,
$curr_open
,
$paras
);
}
elsif
(
$para_type
eq
'=begin'
) {
next
if
$self
->_ponder_begin(
$para
,
$curr_open
,
$paras
);
}
elsif
(
$para_type
eq
'=end'
) {
next
if
$self
->_ponder_end(
$para
,
$curr_open
,
$paras
);
}
elsif
(
$para_type
eq
'~end'
) {
next
if
$self
->_ponder_doc_end(
$para
,
$curr_open
,
$paras
);
}
if
(
grep
$_
->[1]{
'~ignore'
},
@$curr_open
) {
DEBUG > 1 and
print
STDERR
"Skipping $para_type paragraph because in ignore mode.\n"
;
next
;
}
if
(
$para_type
eq
'=pod'
) {
$self
->_ponder_pod(
$para
,
$curr_open
,
$paras
);
}
elsif
(
$para_type
eq
'=over'
) {
next
if
$self
->_ponder_over(
$para
,
$curr_open
,
$paras
);
}
elsif
(
$para_type
eq
'=back'
) {
next
if
$self
->_ponder_back(
$para
,
$curr_open
,
$paras
);
}
else
{
DEBUG > 1 and
print
STDERR
"Pondering non-magical $para_type\n"
;
my
$i
;
if
(
$para_type
=~ m/^=head\d$/s
and !
$self
->{
'accept_heads_anywhere'
}
and
@$curr_open
and
$curr_open
->[-1][0] eq
'=over'
) {
DEBUG > 2 and
print
STDERR
"'=$para_type' inside an '=over'!\n"
;
$self
->whine(
$para
->[1]{
'start_line'
},
"You forgot a '=back' before '$para_type'"
);
unshift
@$paras
, [
'=back'
, {},
''
],
$para
;
next
;
}
if
(
$para_type
eq
'=item'
) {
my
$over
;
unless
(
@$curr_open
and
$over
= (
grep
{
$_
->[0] eq
'=over'
}
@$curr_open
)[-1]) {
$self
->whine(
$para
->[1]{
'start_line'
},
"'=item' outside of any '=over'"
);
unshift
@$paras
,
[
'=over'
, {
'start_line'
=>
$para
->[1]{
'start_line'
}},
''
],
$para
;
next
;
}
my
$over_type
=
$over
->[1]{
'~type'
};
if
(!
$over_type
) {
die
"Typeless over in stack, starting at line "
.
$over
->[1]{
'start_line'
};
}
elsif
(
$over_type
eq
'block'
) {
unless
(
$curr_open
->[-1][1]{
'~bitched_about'
}) {
$curr_open
->[-1][1]{
'~bitched_about'
} = 1;
$self
->whine(
$curr_open
->[-1][1]{
'start_line'
},
"You can't have =items (as at line "
.
$para
->[1]{
'start_line'
}
.
") unless the first thing after the =over is an =item"
);
}
$para
->[0] =
'~Para'
;
unshift
@$paras
,
$para
;
next
;
}
elsif
(
$over_type
eq
'text'
) {
my
$item_type
=
$self
->_get_item_type(
$para
);
DEBUG and
print
STDERR
" Item is of type "
,
$para
->[0],
" under $over_type\n"
;
if
(
$item_type
eq
'text'
) {
}
elsif
(
$item_type
eq
'number'
or
$item_type
eq
'bullet'
) {
$self
->whine(
$para
->[1]{
'start_line'
},
"Expected text after =item, not a $item_type"
);
push
@$para
,
$para
->[1]{
'~orig_content'
};
delete
$para
->[1]{
'number'
};
}
else
{
die
"Unhandled item type $item_type"
;
}
}
elsif
(
$over_type
eq
'number'
) {
my
$item_type
=
$self
->_get_item_type(
$para
);
DEBUG and
print
STDERR
" Item is of type "
,
$para
->[0],
" under $over_type\n"
;
my
$expected_value
= ++
$curr_open
->[-1][1]{
'~counter'
};
if
(
$item_type
eq
'bullet'
) {
$para
->[1]{
'number'
} =
$expected_value
;
$self
->whine(
$para
->[1]{
'start_line'
},
"Expected '=item $expected_value'"
);
push
@$para
,
$para
->[1]{
'~orig_content'
};
}
elsif
(
$item_type
eq
'text'
) {
$para
->[1]{
'number'
} =
$expected_value
;
$self
->whine(
$para
->[1]{
'start_line'
},
"Expected '=item $expected_value'"
);
}
elsif
(
$item_type
ne
'number'
) {
die
"Unknown item type $item_type"
;
}
elsif
(
$expected_value
==
$para
->[1]{
'number'
}) {
DEBUG > 1 and
print
STDERR
" Numeric item has the expected value of $expected_value\n"
;
}
else
{
DEBUG > 1 and
print
STDERR
" Numeric item has "
,
$para
->[1]{
'number'
},
" instead of the expected value of $expected_value\n"
;
$self
->whine(
$para
->[1]{
'start_line'
},
"You have '=item "
.
$para
->[1]{'number'} .
"' instead of the expected '=item $expected_value'"
);
$para
->[1]{
'number'
} =
$expected_value
;
}
if
(
@$para
== 2) {
if
(
$paras
->[0][0] eq
'~Para'
) {
DEBUG and
print
STDERR
"Assimilating following ~Para content into $over_type item\n"
;
push
@$para
,
splice
@{
shift
@$paras
},2;
}
else
{
DEBUG and
print
STDERR
"Can't assimilate following "
,
$paras
->[0][0],
"\n"
;
push
@$para
,
''
;
}
}
}
elsif
(
$over_type
eq
'bullet'
) {
my
$item_type
=
$self
->_get_item_type(
$para
);
DEBUG and
print
STDERR
" Item is of type "
,
$para
->[0],
" under $over_type\n"
;
if
(
$item_type
eq
'bullet'
) {
if
(
$para
->[1]{
'~_freaky_para_hack'
} ) {
DEBUG and
print
STDERR
"Accomodating '=item * Foo' tolerance hack.\n"
;
push
@$para
,
$para
->[1]{
'~_freaky_para_hack'
};
}
}
elsif
(
$item_type
eq
'number'
) {
$self
->whine(
$para
->[1]{
'start_line'
},
"Expected '=item *'"
);
push
@$para
,
$para
->[1]{
'~orig_content'
};
delete
$para
->[1]{
'number'
};
}
elsif
(
$item_type
eq
'text'
) {
$self
->whine(
$para
->[1]{
'start_line'
},
"Expected '=item *'"
);
}
else
{
die
"Unhandled item type $item_type"
;
}
if
(
@$para
== 2) {
if
(
$paras
->[0][0] eq
'~Para'
) {
DEBUG and
print
STDERR
"Assimilating following ~Para content into $over_type item\n"
;
push
@$para
,
splice
@{
shift
@$paras
},2;
}
else
{
DEBUG and
print
STDERR
"Can't assimilate following "
,
$paras
->[0][0],
"\n"
;
push
@$para
,
''
;
}
}
}
else
{
die
"Unhandled =over type \"$over_type\"?"
;
}
$para_type
=
'Plain'
;
$para
->[0] .=
'-'
.
$over_type
;
}
elsif
(
$para_type
eq
'=extend'
) {
$self
->_ponder_extend(
$para
);
next
;
}
elsif
(
$para_type
eq
'=encoding'
) {
$self
->_handle_encoding_second_level(
$para
);
next
unless
$self
->keep_encoding_directive;
$para_type
=
'Plain'
;
}
elsif
(
$para_type
eq
'~Verbatim'
) {
$para
->[0] =
'Verbatim'
;
$para_type
=
'?Verbatim'
;
}
elsif
(
$para_type
eq
'~Para'
) {
$para
->[0] =
'Para'
;
$para_type
=
'?Plain'
;
}
elsif
(
$para_type
eq
'Data'
) {
$para
->[0] =
'Data'
;
$para_type
=
'?Data'
;
}
elsif
(
$para_type
=~ s/^=//s
and
defined
(
$para_type
=
$self
->{
'accept_directives'
}{
$para_type
} )
) {
DEBUG > 1 and
print
STDERR
" Pondering known directive ${$para}[0] as $para_type\n"
;
}
else
{
DEBUG > 1 and
printf
STDERR
"Unhandled directive %s (Handled: %s)\n"
,
$para
->[0],
join
(
' '
,
sort
keys
%{
$self
->{
'accept_directives'
}} )
;
$self
->whine(
$para
->[1]{
'start_line'
},
"Unknown directive: $para->[0]"
);
next
;
}
if
(
$para_type
=~ s/^\?//s) {
if
(!
@$curr_open
) {
DEBUG and
print
STDERR
"Treating $para_type paragraph as such because stack is empty.\n"
;
}
else
{
my
@fors
=
grep
$_
->[0] eq
'=for'
,
@$curr_open
;
DEBUG > 1 and
print
STDERR
"Containing fors: "
,
join
(
','
,
map
$_
->[1]{
'target'
},
@fors
),
"\n"
;
if
(!
@fors
) {
DEBUG and
print
STDERR
"Treating $para_type paragraph as such because stack has no =for's\n"
;
}
elsif
(
$fors
[-1][1]{
'~resolve'
} ) {
if
(
$para_type
eq
'Data'
) {
DEBUG and
print
STDERR
"Treating Data paragraph as Plain/Verbatim because the containing =for ($fors[-1][1]{'target'}) is a resolver\n"
;
$para
->[0] =
'Para'
;
$para_type
=
'Plain'
;
}
else
{
DEBUG and
print
STDERR
"Treating $para_type paragraph as such because the containing =for ($fors[-1][1]{'target'}) is a resolver\n"
;
}
}
else
{
DEBUG and
print
STDERR
"Treating $para_type paragraph as Data because the containing =for ($fors[-1][1]{'target'}) is a non-resolver\n"
;
$para
->[0] =
$para_type
=
'Data'
;
}
}
}
if
(
$para_type
eq
'Plain'
) {
$self
->_ponder_Plain(
$para
);
}
elsif
(
$para_type
eq
'Verbatim'
) {
$self
->_ponder_Verbatim(
$para
);
}
elsif
(
$para_type
eq
'Data'
) {
$self
->_ponder_Data(
$para
);
}
else
{
die
"\$para type is $para_type -- how did that happen?"
;
}
$para
->[0] =~ s/^[~=]//s;
DEBUG and
print
STDERR
"\n"
, pretty(
$para
),
"\n"
;
$self
->{
'content_seen'
} ||= 1;
$self
->_traverse_treelet_bit(
@$para
);
}
}
return
;
}
sub
_ponder_for {
my
(
$self
,
$para
,
$curr_open
,
$paras
) =
@_
;
my
$target
;
if
(
grep
$_
->[1]{
'~ignore'
},
@$curr_open
) {
DEBUG > 1 and
print
STDERR
"Ignoring ignorable =for\n"
;
return
1;
}
for
(
my
$i
= 2;
$i
<
@$para
; ++
$i
) {
if
(
$para
->[
$i
] =~ s/^\s*(\S+)\s*//s) {
$target
= $1;
last
;
}
}
unless
(
defined
$target
) {
$self
->whine(
$para
->[1]{
'start_line'
},
"=for without a target?"
);
return
1;
}
DEBUG > 1 and
print
STDERR
"Faking out a =for $target as a =begin $target / =end $target\n"
;
$para
->[0] =
'Data'
;
unshift
@$paras
,
[
'=begin'
,
{
'start_line'
=>
$para
->[1]{
'start_line'
},
'~really'
=>
'=for'
},
$target
,
],
$para
,
[
'=end'
,
{
'start_line'
=>
$para
->[1]{
'start_line'
},
'~really'
=>
'=for'
},
$target
,
],
;
return
1;
}
sub
_ponder_begin {
my
(
$self
,
$para
,
$curr_open
,
$paras
) =
@_
;
my
$content
=
join
' '
,
splice
@$para
, 2;
$content
=~ s/^\s+//s;
$content
=~ s/\s+$//s;
unless
(
length
(
$content
)) {
$self
->whine(
$para
->[1]{
'start_line'
},
"=begin without a target?"
);
DEBUG and
print
STDERR
"Ignoring targetless =begin\n"
;
return
1;
}
my
(
$target
,
$title
) =
$content
=~ m/^(\S+)\s*(.*)$/;
$para
->[1]{
'title'
} =
$title
if
(
$title
);
$para
->[1]{
'target'
} =
$target
;
$content
=
$target
;
$content
=~ s/^:!/!:/s;
my
$neg
;
$neg
= 1
if
$content
=~ s/^!//s;
my
$to_resolve
;
$to_resolve
= 1
if
$content
=~ s/^://s;
my
$dont_ignore
;
foreach
my
$target_name
(
split
(
','
,
$content
, -1),
$neg
? () :
'*'
) {
DEBUG > 2 and
print
STDERR
" Considering whether =begin $content matches $target_name\n"
;
next
unless
$self
->{
'accept_targets'
}{
$target_name
};
DEBUG > 2 and
print
STDERR
" It DOES match the acceptable target $target_name!\n"
;
$to_resolve
= 1
if
$self
->{
'accept_targets'
}{
$target_name
} eq
'force_resolve'
;
$dont_ignore
= 1;
$para
->[1]{
'target_matching'
} =
$target_name
;
last
;
}
if
(
$neg
) {
if
(
$dont_ignore
) {
$dont_ignore
=
''
;
delete
$para
->[1]{
'target_matching'
};
DEBUG > 2 and
print
STDERR
" But the leading ! means that this is a NON-match!\n"
;
}
else
{
$dont_ignore
= 1;
$para
->[1]{
'target_matching'
} =
'!'
;
DEBUG > 2 and
print
STDERR
" But the leading ! means that this IS a match!\n"
;
}
}
$para
->[0] =
'=for'
;
$para
->[1]{
'~really'
} ||=
'=begin'
;
$para
->[1]{
'~ignore'
} = (!
$dont_ignore
) || 0;
$para
->[1]{
'~resolve'
} =
$to_resolve
|| 0;
DEBUG > 1 and
print
STDERR
" Making note to "
,
$dont_ignore
?
'not '
:
''
,
"ignore contents of this region\n"
;
DEBUG > 1 and
$dont_ignore
and
print
STDERR
" Making note to treat contents as "
,
(
$to_resolve
?
'verbatim/plain'
:
'data'
),
" paragraphs\n"
;
DEBUG > 1 and
print
STDERR
" (Stack now: "
,
$self
->_dump_curr_open(),
")\n"
;
push
@$curr_open
,
$para
;
if
(!
$dont_ignore
or
scalar
grep
$_
->[1]{
'~ignore'
},
@$curr_open
) {
DEBUG > 1 and
print
STDERR
"Ignoring ignorable =begin\n"
;
}
else
{
$self
->{
'content_seen'
} ||= 1;
$self
->_handle_element_start((
my
$scratch
=
'for'
),
$para
->[1]);
}
return
1;
}
sub
_ponder_end {
my
(
$self
,
$para
,
$curr_open
,
$paras
) =
@_
;
my
$content
=
join
' '
,
splice
@$para
, 2;
$content
=~ s/^\s+//s;
$content
=~ s/\s+$//s;
DEBUG and
print
STDERR
"Ogling '=end $content' directive\n"
;
unless
(
length
(
$content
)) {
$self
->whine(
$para
->[1]{
'start_line'
},
"'=end' without a target?"
. (
(
@$curr_open
and
$curr_open
->[-1][0] eq
'=for'
)
? (
" (Should be \"=end "
.
$curr_open
->[-1][1]{
'target'
} .
'")'
)
:
''
)
);
DEBUG and
print
STDERR
"Ignoring targetless =end\n"
;
return
1;
}
unless
(
$content
=~ m/^\S+$/) {
$self
->whine(
$para
->[1]{
'start_line'
},
"'=end $content' is invalid. (Stack: "
.
$self
->_dump_curr_open() .
')'
);
DEBUG and
print
STDERR
"Ignoring mistargetted =end $content\n"
;
return
1;
}
unless
(
@$curr_open
and
$curr_open
->[-1][0] eq
'=for'
) {
$self
->whine(
$para
->[1]{
'start_line'
},
"=end $content without matching =begin. (Stack: "
.
$self
->_dump_curr_open() .
')'
);
DEBUG and
print
STDERR
"Ignoring mistargetted =end $content\n"
;
return
1;
}
unless
(
$content
eq
$curr_open
->[-1][1]{
'target'
}) {
$self
->whine(
$para
->[1]{
'start_line'
},
"=end $content doesn't match =begin "
.
$curr_open
->[-1][1]{
'target'
}
.
". (Stack: "
.
$self
->_dump_curr_open() .
')'
);
DEBUG and
print
STDERR
"Ignoring mistargetted =end $content at line $para->[1]{'start_line'}\n"
;
return
1;
}
if
(
grep
$_
->[1]{
'~ignore'
},
@$curr_open
) {
DEBUG > 1 and
print
STDERR
"Not firing any event for this =end $content because in an ignored region\n"
;
}
else
{
$curr_open
->[-1][1]{
'start_line'
} =
$para
->[1]{
'start_line'
};
$self
->{
'content_seen'
} ||= 1;
$self
->_handle_element_end(
my
$scratch
=
'for'
,
$para
->[1]);
}
DEBUG > 1 and
print
STDERR
"Popping $curr_open->[-1][0] $curr_open->[-1][1]{'target'} because of =end $content\n"
;
pop
@$curr_open
;
return
1;
}
sub
_ponder_doc_end {
my
(
$self
,
$para
,
$curr_open
,
$paras
) =
@_
;
if
(
@$curr_open
) {
DEBUG and
print
STDERR
"Stack is nonempty at end-document: ("
,
$self
->_dump_curr_open(),
")\n"
;
DEBUG > 9 and
print
STDERR
"Stack: "
, pretty(
$curr_open
),
"\n"
;
unshift
@$paras
,
$self
->_closers_for_all_curr_open;
@$paras
=
grep
$_
->[0] ne
'~end'
,
@$paras
;
push
@$paras
,
$para
,
$para
;
return
1;
}
else
{
DEBUG and
print
STDERR
"Okay, stack is empty now.\n"
;
}
unless
(
$self
->{
'~tried_gen_errata'
}) {
$self
->{
'~tried_gen_errata'
} = 1;
my
@extras
=
$self
->_gen_errata();
if
(
@extras
) {
unshift
@$paras
,
@extras
;
DEBUG and
print
STDERR
"Generated errata... relooping...\n"
;
return
1;
}
}
splice
@$paras
;
DEBUG and
print
STDERR
"Throwing end-document event.\n"
;
$self
->_handle_element_end(
my
$scratch
=
'Document'
);
return
1;
}
sub
_ponder_pod {
my
(
$self
,
$para
,
$curr_open
,
$paras
) =
@_
;
$self
->whine(
$para
->[1]{
'start_line'
},
"=pod directives shouldn't be over one line long! Ignoring all "
. (
@$para
- 2) .
" lines of content"
)
if
@$para
> 3;
if
(
my
$pod_handler
=
$self
->{
'pod_handler'
}) {
my
(
$line_num
,
$line
) =
map
$_
,
$para
->[1]{
'start_line'
},
$para
->[2];
$line
=
$line
eq
''
?
"=pod"
:
"=pod $line"
;
$pod_handler
->(
$line
,
$line_num
,
$self
);
}
return
;
}
sub
_ponder_over {
my
(
$self
,
$para
,
$curr_open
,
$paras
) =
@_
;
return
1
unless
@$paras
;
my
$list_type
;
if
(
$paras
->[0][0] eq
'=item'
) {
$list_type
=
$self
->_get_initial_item_type(
$paras
->[0]);
}
elsif
(
$paras
->[0][0] eq
'=back'
) {
if
(
$self
->{
'parse_empty_lists'
}) {
$list_type
=
'empty'
;
}
else
{
shift
@$paras
;
return
1;
}
}
elsif
(
$paras
->[0][0] eq
'~end'
) {
$self
->whine(
$para
->[1]{
'start_line'
},
"=over is the last thing in the document?!"
);
return
1;
}
else
{
$list_type
=
'block'
;
}
$para
->[1]{
'~type'
} =
$list_type
;
push
@$curr_open
,
$para
;
my
$content
=
join
' '
,
splice
@$para
, 2;
$para
->[1]{
'~orig_content'
} =
$content
;
my
$overness
;
if
(
$content
=~ m/^\s*$/s) {
$para
->[1]{
'indent'
} = 4;
}
elsif
(
$content
=~ m/^\s*((?:\d*\.)?\d+)\s*$/s) {
no
integer;
$para
->[1]{
'indent'
} = $1;
if
($1 == 0) {
$self
->whine(
$para
->[1]{
'start_line'
},
"Can't have a 0 in =over $content"
);
$para
->[1]{
'indent'
} = 4;
}
}
else
{
$self
->whine(
$para
->[1]{
'start_line'
},
"=over should be: '=over' or '=over positive_number'"
);
$para
->[1]{
'indent'
} = 4;
}
DEBUG > 1 and
print
STDERR
"=over found of type $list_type\n"
;
$self
->{
'content_seen'
} ||= 1;
$self
->_handle_element_start((
my
$scratch
=
'over-'
.
$list_type
),
$para
->[1]);
return
;
}
sub
_ponder_back {
my
(
$self
,
$para
,
$curr_open
,
$paras
) =
@_
;
my
$content
=
join
' '
,
splice
@$para
, 2;
if
(
$content
=~ m/\S/) {
$self
->whine(
$para
->[1]{
'start_line'
},
"=back doesn't take any parameters, but you said =back $content"
);
}
if
(
@$curr_open
and
$curr_open
->[-1][0] eq
'=over'
) {
DEBUG > 1 and
print
STDERR
"=back happily closes matching =over\n"
;
$self
->{
'content_seen'
} ||= 1;
$self
->_handle_element_end(
my
$scratch
=
'over-'
. ( (
pop
@$curr_open
)->[1]{
'~type'
} ),
$para
->[1]
);
}
else
{
DEBUG > 1 and
print
STDERR
"=back found without a matching =over. Stack: ("
,
join
(
', '
,
map
$_
->[0],
@$curr_open
),
").\n"
;
$self
->whine(
$para
->[1]{
'start_line'
},
'=back without =over'
);
return
1;
}
}
sub
_ponder_item {
my
(
$self
,
$para
,
$curr_open
,
$paras
) =
@_
;
my
$over
;
unless
(
@$curr_open
and
$over
= (
grep
{
$_
->[0] eq
'=over'
}
@$curr_open
)[-1]) {
$self
->whine(
$para
->[1]{
'start_line'
},
"'=item' outside of any '=over'"
);
unshift
@$paras
,
[
'=over'
, {
'start_line'
=>
$para
->[1]{
'start_line'
}},
''
],
$para
;
return
1;
}
my
$over_type
=
$over
->[1]{
'~type'
};
if
(!
$over_type
) {
die
"Typeless over in stack, starting at line "
.
$over
->[1]{
'start_line'
};
}
elsif
(
$over_type
eq
'block'
) {
unless
(
$curr_open
->[-1][1]{
'~bitched_about'
}) {
$curr_open
->[-1][1]{
'~bitched_about'
} = 1;
$self
->whine(
$curr_open
->[-1][1]{
'start_line'
},
"You can't have =items (as at line "
.
$para
->[1]{
'start_line'
}
.
") unless the first thing after the =over is an =item"
);
}
$para
->[0] =
'~Para'
;
unshift
@$paras
,
$para
;
return
1;
}
elsif
(
$over_type
eq
'text'
) {
my
$item_type
=
$self
->_get_item_type(
$para
);
DEBUG and
print
STDERR
" Item is of type "
,
$para
->[0],
" under $over_type\n"
;
if
(
$item_type
eq
'text'
) {
}
elsif
(
$item_type
eq
'number'
or
$item_type
eq
'bullet'
) {
$self
->whine(
$para
->[1]{
'start_line'
},
"Expected text after =item, not a $item_type"
);
push
@$para
,
$para
->[1]{
'~orig_content'
};
delete
$para
->[1]{
'number'
};
}
else
{
die
"Unhandled item type $item_type"
;
}
}
elsif
(
$over_type
eq
'number'
) {
my
$item_type
=
$self
->_get_item_type(
$para
);
DEBUG and
print
STDERR
" Item is of type "
,
$para
->[0],
" under $over_type\n"
;
my
$expected_value
= ++
$curr_open
->[-1][1]{
'~counter'
};
if
(
$item_type
eq
'bullet'
) {
$para
->[1]{
'number'
} =
$expected_value
;
$self
->whine(
$para
->[1]{
'start_line'
},
"Expected '=item $expected_value'"
);
push
@$para
,
$para
->[1]{
'~orig_content'
};
}
elsif
(
$item_type
eq
'text'
) {
$para
->[1]{
'number'
} =
$expected_value
;
$self
->whine(
$para
->[1]{
'start_line'
},
"Expected '=item $expected_value'"
);
}
elsif
(
$item_type
ne
'number'
) {
die
"Unknown item type $item_type"
;
}
elsif
(
$expected_value
==
$para
->[1]{
'number'
}) {
DEBUG > 1 and
print
STDERR
" Numeric item has the expected value of $expected_value\n"
;
}
else
{
DEBUG > 1 and
print
STDERR
" Numeric item has "
,
$para
->[1]{
'number'
},
" instead of the expected value of $expected_value\n"
;
$self
->whine(
$para
->[1]{
'start_line'
},
"You have '=item "
.
$para
->[1]{'number'} .
"' instead of the expected '=item $expected_value'"
);
$para
->[1]{
'number'
} =
$expected_value
;
}
if
(
@$para
== 2) {
if
(
$paras
->[0][0] eq
'~Para'
) {
DEBUG and
print
STDERR
"Assimilating following ~Para content into $over_type item\n"
;
push
@$para
,
splice
@{
shift
@$paras
},2;
}
else
{
DEBUG and
print
STDERR
"Can't assimilate following "
,
$paras
->[0][0],
"\n"
;
push
@$para
,
''
;
}
}
}
elsif
(
$over_type
eq
'bullet'
) {
my
$item_type
=
$self
->_get_item_type(
$para
);
DEBUG and
print
STDERR
" Item is of type "
,
$para
->[0],
" under $over_type\n"
;
if
(
$item_type
eq
'bullet'
) {
if
(
$para
->[1]{
'~_freaky_para_hack'
} ) {
DEBUG and
print
STDERR
"Accomodating '=item * Foo' tolerance hack.\n"
;
push
@$para
,
$para
->[1]{
'~_freaky_para_hack'
};
}
}
elsif
(
$item_type
eq
'number'
) {
$self
->whine(
$para
->[1]{
'start_line'
},
"Expected '=item *'"
);
push
@$para
,
$para
->[1]{
'~orig_content'
};
delete
$para
->[1]{
'number'
};
}
elsif
(
$item_type
eq
'text'
) {
$self
->whine(
$para
->[1]{
'start_line'
},
"Expected '=item *'"
);
}
else
{
die
"Unhandled item type $item_type"
;
}
if
(
@$para
== 2) {
if
(
$paras
->[0][0] eq
'~Para'
) {
DEBUG and
print
STDERR
"Assimilating following ~Para content into $over_type item\n"
;
push
@$para
,
splice
@{
shift
@$paras
},2;
}
else
{
DEBUG and
print
STDERR
"Can't assimilate following "
,
$paras
->[0][0],
"\n"
;
push
@$para
,
''
;
}
}
}
else
{
die
"Unhandled =over type \"$over_type\"?"
;
}
$para
->[0] .=
'-'
.
$over_type
;
return
;
}
sub
_ponder_Plain {
my
(
$self
,
$para
) =
@_
;
DEBUG and
print
STDERR
" giving plain treatment...\n"
;
unless
(
@$para
== 2 or (
@$para
== 3 and
$para
->[2] eq
''
)
or
$para
->[1]{
'~cooked'
}
) {
push
@$para
,
@{
$self
->_make_treelet(
join
(
"\n"
,
splice
(
@$para
, 2)),
$para
->[1]{
'start_line'
}
)};
}
return
;
}
sub
_ponder_Verbatim {
my
(
$self
,
$para
) =
@_
;
DEBUG and
print
STDERR
" giving verbatim treatment...\n"
;
$para
->[1]{
'xml:space'
} =
'preserve'
;
unless
(
$self
->{
'_output_is_for_JustPod'
}) {
my
$indent
=
$self
->strip_verbatim_indent;
if
(
$indent
&&
ref
$indent
eq
'CODE'
) {
my
@shifted
= (
shift
@{
$para
},
shift
@{
$para
});
$indent
=
$indent
->(
$para
);
unshift
@{
$para
},
@shifted
;
}
for
(
my
$i
= 2;
$i
<
@$para
;
$i
++) {
foreach
my
$line
(
$para
->[
$i
]) {
$line
=~ s/^\Q
$indent
//
if
$indent
;
while
(
$line
=~
s/^([^\t]*)(\t+)/$1.(
" "
x ((
length
($2)<<3)-(
length
($1)&7)))/e
) {}
}
}
}
if
(
$self
->{
'accept_codes'
} and
$self
->{
'accept_codes'
}{
'VerbatimFormatted'
}
) {
while
(
@$para
> 3 and
$para
->[-1] !~ m/\S/) {
pop
@$para
}
$self
->_verbatim_format(
$para
);
}
elsif
(
$self
->{
'codes_in_verbatim'
}) {
push
@$para
,
@{
$self
->_make_treelet(
join
(
"\n"
,
splice
(
@$para
, 2)),
$para
->[1]{
'start_line'
},
$para
->[1]{
'xml:space'
}
)};
$para
->[-1] =~ s/\n+$//s;
}
else
{
push
@$para
,
join
"\n"
,
splice
(
@$para
, 2)
if
@$para
> 3;
$para
->[-1] =~ s/\n+$//s;
}
return
;
}
sub
_ponder_Data {
my
(
$self
,
$para
) =
@_
;
DEBUG and
print
STDERR
" giving data treatment...\n"
;
$para
->[1]{
'xml:space'
} =
'preserve'
;
push
@$para
,
join
"\n"
,
splice
(
@$para
, 2)
if
@$para
> 3;
return
;
}
sub
_traverse_treelet_bit {
my
(
$self
,
$name
) =
splice
@_
,0,2;
my
$scratch
;
$self
->_handle_element_start((
$scratch
=
$name
),
shift
@_
);
while
(
@_
) {
my
$x
=
shift
;
if
(
ref
(
$x
)) {
&_traverse_treelet_bit
(
$self
,
@$x
);
}
else
{
$x
.=
shift
while
@_
&& !
ref
(
$_
[0]);
$self
->_handle_text(
$x
);
}
}
$self
->_handle_element_end(
$scratch
=
$name
);
return
;
}
sub
_closers_for_all_curr_open {
my
$self
=
$_
[0];
my
@closers
;
foreach
my
$still_open
(@{
$self
->{
'curr_open'
} ||
return
}) {
my
@copy
=
@$still_open
;
$copy
[1] = {%{
$copy
[1] }};
if
(
$copy
[0] eq
'=for'
) {
$copy
[0] =
'=end'
;
}
elsif
(
$copy
[0] eq
'=over'
) {
$self
->whine(
$still_open
->[1]{start_line} ,
"=over without closing =back"
);
$copy
[0] =
'=back'
;
}
else
{
die
"I don't know how to auto-close an open $copy[0] region"
;
}
unless
(
@copy
> 2 ) {
push
@copy
,
$copy
[1]{
'target'
};
$copy
[-1] =
''
unless
defined
$copy
[-1];
}
$copy
[1]{
'fake-closer'
} = 1;
DEBUG and
print
STDERR
"Queuing up fake-o event: "
, pretty(\
@copy
),
"\n"
;
unshift
@closers
, \
@copy
;
}
return
@closers
;
}
sub
_verbatim_format {
my
(
$it
,
$p
) =
@_
;
my
$formatting
;
for
(
my
$i
= 2;
$i
<
@$p
;
$i
++) {
DEBUG and
print
STDERR
"_verbatim_format appends a newline to $i: $p->[$i]\n"
;
$p
->[
$i
] .=
"\n"
;
}
if
( DEBUG > 4 ) {
print
STDERR
"<<\n"
;
for
(
my
$i
=
$#$p
;
$i
>= 2;
$i
--) { # work backwards over the lines
print
STDERR
"_verbatim_format $i: $p->[$i]"
;
}
print
STDERR
">>\n"
;
}
for
(
my
$i
=
$#$p
;
$i
> 2;
$i
--) {
DEBUG > 5 and
print
STDERR
"Scrutinizing line $i: $$p[$i]\n"
;
if
(
$p
->[
$i
] =~ m{^
DEBUG > 5 and
print
STDERR
" It's a formatty line. "
,
"Peeking at previous line "
,
$i
-1,
": $$p[$i-1]: \n"
;
if
(
$p
->[
$i
-1] =~ m{^
DEBUG > 5 and
print
STDERR
" Previous line is formatty! Skipping this one.\n"
;
next
;
}
else
{
DEBUG > 5 and
print
STDERR
" Previous line is non-formatty! Yay!\n"
;
}
}
else
{
DEBUG > 5 and
print
STDERR
" It's not a formatty line. Ignoring\n"
;
next
;
}
DEBUG > 4 and
print
STDERR
"_verbatim_format considers:\n<$p->[$i-1]>\n<$p->[$i]>\n"
;
$formatting
=
' '
. $1;
$formatting
=~ s/\s+$//s;
unless
(
length
$formatting
and
$p
->[
$i
-1] =~ m/\S/) {
splice
@$p
,
$i
,1;
$i
--;
next
;
}
if
(
length
(
$formatting
) >=
length
(
$p
->[
$i
-1]) ) {
$formatting
=
substr
(
$formatting
, 0,
length
(
$p
->[
$i
-1]) - 1) .
' '
;
}
else
{
$formatting
.=
' '
x (
length
(
$p
->[
$i
-1]) -
length
(
$formatting
));
}
DEBUG > 4 and
print
STDERR
"Formatting <$formatting> on <"
,
$p
->[
$i
-1],
">\n"
;
my
@new_line
;
while
(
$formatting
=~ m{\G(( +)|(\^+)|(\/+)|(\%+))}g ) {
if
($2) {
push
@new_line
,
substr
(
$p
->[
$i
-1],
pos
(
$formatting
)-
length
($1),
length
($1));
}
else
{
push
@new_line
, [
(
$3 ?
'VerbatimB'
:
$4 ?
'VerbatimI'
:
$5 ?
'VerbatimBI'
:
die
(
"Should never get called"
)
), {},
substr
(
$p
->[
$i
-1],
pos
(
$formatting
)-
length
($1),
length
($1))
];
}
}
my
@nixed
=
splice
@$p
,
$i
-1, 2,
@new_line
;
DEBUG > 10 and
print
STDERR
"Nixed count: "
,
scalar
(
@nixed
),
"\n"
;
DEBUG > 6 and
print
STDERR
"New version of the above line is these tokens ("
,
scalar
(
@new_line
),
"):"
,
map
(
ref
(
$_
)?
"<@$_> "
:
"<$_>"
,
@new_line
),
"\n"
;
$i
--;
}
$p
->[0] =
'VerbatimFormatted'
;
for
(
my
$i
= 2;
$i
>
$#$p
;
$i
++ ) { # work forwards over the tokens except
for
the
last
if
( !
ref
(
$p
->[
$i
]) and !
ref
(
$p
->[
$i
+ 1]) ) {
DEBUG > 5 and
print
STDERR
"_verbatim_format merges {$p->[$i]} and {$p->[$i+1]}\n"
;
$p
->[
$i
] .=
splice
@$p
,
$i
+1, 1;
--
$i
;
}
}
for
(
my
$i
=
$#$p
;
$i
>= 2;
$i
-- ) {
if
( !
ref
(
$p
->[
$i
]) ) {
if
(
$p
->[
$i
] =~ s/\n$//s) {
DEBUG > 5 and
print
STDERR
"_verbatim_format killed the terminal newline on #$i: {$p->[$i]}, after {$p->[$i-1]}\n"
;
}
else
{
DEBUG > 5 and
print
STDERR
"No terminal newline on #$i: {$p->[$i]}, after {$p->[$i-1]} !?\n"
;
}
last
;
}
}
return
;
}
sub
_treelet_from_formatting_codes {
my
(
$self
,
$para
,
$start_line
,
$preserve_space
) =
@_
;
my
$treelet
= [
'~Top'
, {
'start_line'
=>
$start_line
},];
unless
(
$preserve_space
||
$self
->{
'preserve_whitespace'
}) {
$para
=~ s/\s+/ /g;
$para
=~ s/ $//;
$para
=~ s/^ //;
}
my
@stack
;
my
@lineage
= (
$treelet
);
my
$raw
=
''
;
my
$inL
= 0;
DEBUG > 4 and
print
STDERR
"Paragraph:\n$para\n\n"
;
while
(
$para
=~
m/\G
(?:
([A-Z]<(?:(<+)\s+)?)
|
(\s+|(?<=\s\s))(>{2,})
|
(\s?>)
|
(
(?:
[^A-Z\s>]
|
(?:
[A-Z](?!<)
)
|
(?:
\s(?!\s*>{2,})
)
)+
)
)
/xgo
) {
DEBUG > 4 and
print
STDERR
"\nParagraphic tokenstack = (@stack)\n"
;
if
(
defined
$1) {
my
$bracket_count
;
if
(
defined
$2) {
DEBUG > 3 and
print
STDERR
"Found complex start-text code \"$1\"\n"
;
$bracket_count
=
length
($2) + 1;
push
@stack
,
$bracket_count
;
}
else
{
DEBUG > 3 and
print
STDERR
"Found simple start-text code \"$1\"\n"
;
push
@stack
, 0;
$bracket_count
= 1;
}
my
$code
=
substr
($1,0,1);
if
(
'L'
eq
$code
) {
if
(
$inL
) {
$raw
.= $1;
$self
->scream(
$start_line
,
'Nested L<> are illegal. Pretending inner one is '
.
'X<...> so can continue looking for other errors.'
);
$code
=
"X"
;
}
else
{
$raw
=
""
;
$inL
=
@stack
;
}
}
else
{
$raw
.= $1
if
$inL
;
}
push
@lineage
, [
$code
, {}, ];
if
(
$self
->{
'_output_is_for_JustPod'
} &&
$bracket_count
> 1) {
$lineage
[-1][1]{
'~bracket_count'
} =
$bracket_count
;
my
$lspacer
=
substr
($1, 1 +
$bracket_count
);
$lineage
[-1][1]{
'~lspacer'
} =
$lspacer
if
$lspacer
ne
" "
;
}
push
@{
$lineage
[-2] },
$lineage
[-1];
}
elsif
(
defined
$4) {
DEBUG > 3 and
print
STDERR
"Found apparent complex end-text code \"$3$4\"\n"
;
if
(!
@stack
) {
DEBUG > 4 and
print
STDERR
" But it's really just stuff.\n"
;
push
@{
$lineage
[-1] }, $3, $4;
next
;
}
elsif
(!
$stack
[-1]) {
DEBUG > 4 and
print
STDERR
" And that's more than we needed to close simple.\n"
;
push
@{
$lineage
[-1] }, $3;
pos
(
$para
) =
pos
(
$para
) -
length
($4) + 1;
}
elsif
(
$stack
[-1] ==
length
($4)) {
DEBUG > 4 and
print
STDERR
" And that's exactly what we needed to close complex.\n"
;
}
elsif
(
$stack
[-1] <
length
($4)) {
DEBUG > 4 and
print
STDERR
" And that's more than we needed to close complex.\n"
;
pos
(
$para
) =
pos
(
$para
) -
length
($4) +
$stack
[-1];
}
else
{
DEBUG > 4 and
print
STDERR
" But it's really just stuff, because we needed more.\n"
;
push
@{
$lineage
[-1] }, $3, $4;
next
;
}
if
($3 ne
" "
&&
$self
->{
'_output_is_for_JustPod'
}) {
if
($3 ne
""
) {
$lineage
[-1][1]{
'~rspacer'
} = $3;
}
elsif
(
$lineage
[-1][1]{
'~lspacer'
} eq
" "
) {
delete
$lineage
[-1][1]{
'~lspacer'
};
}
else
{
$lineage
[-1][1]{
'~rspacer'
}
=
substr
(
$lineage
[-1][1]{
'~lspacer'
}, -1, 1);
chop
$lineage
[-1][1]{
'~lspacer'
};
}
}
push
@{
$lineage
[-1] },
''
if
2 == @{
$lineage
[-1] };
if
(
$inL
==
@stack
) {
$lineage
[-1][1]{
'raw'
} =
$raw
;
$inL
= 0;
}
pop
@stack
;
pop
@lineage
;
$raw
.= $3.$4
if
$inL
;
}
elsif
(
defined
$5) {
DEBUG > 3 and
print
STDERR
"Found apparent simple end-text code \"$5\"\n"
;
if
(
@stack
and !
$stack
[-1]) {
DEBUG > 4 and
print
STDERR
" It's indeed an end-code.\n"
;
if
(
length
($5) == 2) {
push
@{
$lineage
[-1] },
' '
;
}
elsif
( 2 == @{
$lineage
[-1] } ) {
push
@{
$lineage
[-1] },
''
;
}
if
(
$inL
==
@stack
) {
$lineage
[-1][1]{
'raw'
} =
$raw
;
$inL
= 0;
}
pop
@stack
;
pop
@lineage
;
}
else
{
DEBUG > 4 and
print
STDERR
" It's just stuff.\n"
;
push
@{
$lineage
[-1] }, $5;
}
$raw
.= $5
if
$inL
;
}
elsif
(
defined
$6) {
DEBUG > 3 and
print
STDERR
"Found stuff \"$6\"\n"
;
push
@{
$lineage
[-1] }, $6;
$raw
.= $6
if
$inL
;
}
else
{
DEBUG and
print
STDERR
"AYYAYAAAAA at line "
, __LINE__,
"\n"
;
die
"SPORK 512512!"
;
}
}
if
(
@stack
) {
my
$x
=
"..."
;
while
(
@stack
) {
push
@{
$lineage
[-1] },
''
if
2 == @{
$lineage
[-1] };
my
$code
= (
pop
@lineage
)->[0];
my
$ender_length
=
pop
@stack
;
if
(
$ender_length
) {
--
$ender_length
;
$x
=
$code
. (
"<"
x
$ender_length
) .
" $x "
. (
">"
x
$ender_length
);
}
else
{
$x
=
$code
.
"<$x>"
;
}
}
DEBUG > 1 and
print
STDERR
"Unterminated $x sequence\n"
;
$self
->whine(
$start_line
,
"Unterminated $x sequence"
,
);
}
return
$treelet
;
}
sub
text_content_of_treelet {
return
stringify_lol(
$_
[1]);
}
sub
stringify_lol {
my
$string_form
=
''
;
_stringify_lol(
$_
[0] => \
$string_form
);
return
$string_form
;
}
sub
_stringify_lol {
my
(
$lol
,
$to
) =
@_
;
for
(
my
$i
= 2;
$i
<
@$lol
; ++
$i
) {
if
(
ref
(
$lol
->[
$i
] ||
''
) and UNIVERSAL::isa(
$lol
->[
$i
],
'ARRAY'
) ) {
_stringify_lol(
$lol
->[
$i
],
$to
);
}
else
{
$$to
.=
$lol
->[
$i
];
}
}
return
;
}
sub
_dump_curr_open {
my
$curr_open
=
$_
[0]{
'curr_open'
};
return
'[empty]'
unless
@$curr_open
;
return
join
'; '
,
map
{;
(
$_
->[0] eq
'=for'
)
? ( (
$_
->[1]{
'~really'
} ||
'=over'
)
.
' '
.
$_
->[1]{
'target'
})
:
$_
->[0]
}
@$curr_open
;
}
my
%pretty_form
= (
"\a"
=>
'\a'
,
"\b"
=>
'\b'
,
"\e"
=>
'\e'
,
"\f"
=>
'\f'
,
"\t"
=>
'\t'
,
"\cm"
=>
'\cm'
,
"\cj"
=>
'\cj'
,
"\n"
=>
'\n'
,
'"'
=>
'\"'
,
'\\'
=>
'\\\\'
,
'$'
=>
'\\$'
,
'@'
=>
'\\@'
,
'%'
=>
'\\%'
,
'#'
=>
'\\#'
,
);
sub
pretty {
my
@stuff
=
@_
;
my
$x
;
my
$out
=
join
", "
,
map
{;
if
(!
defined
(
$_
)) {
"undef"
;
}
elsif
(
ref
(
$_
) eq
'ARRAY'
or
ref
(
$_
) eq
'Pod::Simple::LinkSection'
) {
$x
=
"[ "
. pretty(
@$_
) .
" ]"
;
$x
;
}
elsif
(
ref
(
$_
) eq
'SCALAR'
) {
$x
=
"\\"
. pretty(
$$_
) ;
$x
;
}
elsif
(
ref
(
$_
) eq
'HASH'
) {
my
$hr
=
$_
;
$x
=
"{"
.
join
(
", "
,
map
(pretty(
$_
) .
'=>'
. pretty(
$hr
->{
$_
}),
sort
keys
%$hr
) ) .
"}"
;
$x
;
}
elsif
(!
length
(
$_
)) {
q{''}
}
elsif
(
$_
eq
'0'
or(
m/^-?(?:[123456789]\d*|0)(?:\.\d+)?$/s
and
$_
ne
'-0'
)
) {
$_
;
}
else
{
s<([^ !"
<
$pretty_form
{$1} ||
'\\x{'
.
sprintf
(
"%x"
,
ord
($1)).
'}'
>eg;
qq{"$_"}
;
}
}
@stuff
;
return
$out
;
}
sub
reinit {
my
$self
=
shift
;
foreach
(
qw(source_dead source_filename doc_has_started
start_of_pod_block content_seen last_was_blank paras curr_open
line_count pod_para_count in_pod ~tried_gen_errata all_errata errata errors_seen
Title)
) {
delete
$self
->{
$_
};
}
}
1;