File Coverage

blib/lib/Text/Amuse/Document.pm
Criterion Covered Total %
statement 672 687 97.8
branch 255 288 88.5
condition 81 92 88.0
subroutine 72 73 98.6
pod 19 19 100.0
total 1099 1159 94.8


line stmt bran cond sub pod time code
1             package Text::Amuse::Document;
2              
3 47     47   358865 use strict;
  47         164  
  47         1356  
4 47     47   231 use warnings;
  47         105  
  47         1112  
5 47     47   20543 use Text::Amuse::Element;
  47         136  
  47         1477  
6 47     47   22449 use Text::Amuse::Utils;
  47         136  
  47         1401  
7 47     47   332 use File::Spec;
  47         112  
  47         1725  
8             use constant {
9 47         5936 IMAJOR => 1,
10             IEQUAL => 0,
11             IMINOR => -1,
12 47     47   301 };
  47         95  
13              
14 47     47   26620 use Data::Dumper;
  47         292882  
  47         381732  
15              
16             =head1 NAME
17              
18             Text::Amuse::Document - core parser for L (internal)
19              
20             =head1 SYNOPSIS
21              
22             The module is used internally by L, so everything here is
23             pretty much internal only (and underdocumented). The useful stuff is
24             accessible via the L class.
25              
26             =head1 METHODS
27              
28             =over 4
29              
30             =item new(file => $filename, include_paths => \@paths )
31              
32             =cut
33              
34             sub new {
35 731     731 1 55809 my $class = shift;
36 731         1346 my %args;
37 731         7083 my $self = {
38             _raw_footnotes => {},
39             _current_footnote_indent => 0,
40             _current_footnote_number => undef,
41             _current_footnote_stack => [],
42             _list_element_pile => [],
43             _list_parsing_output => [],
44             _bidi_document => 0,
45             _has_ruby => 0,
46             include_paths => [],
47             included_files => [],
48             _other_doc_language_codes => [],
49             };
50 731 50       3850 if (@_ % 2 == 0) {
    0          
51 731         3459 %args = @_;
52             }
53             elsif (@_ == 1) {
54 0         0 $args{file} = shift;
55             }
56             else {
57 0         0 die "Wrong arguments! The constructor accepts only a filename\n";
58             }
59 731 50       15855 if (-f $args{file}) {
60 731         3503 $self->{filename} = $args{file};
61             } else {
62 0         0 die "Wrong argument! $args{file} doesn't exist!\n"
63             }
64 731 100       2241 if ($args{include_paths}) {
65 4         7 my @includes;
66 4 50       13 if (ref($args{include_paths}) eq 'ARRAY') {
67 4         7 push @includes, @{$args{include_paths}};
  4         12  
68             }
69             else {
70 0         0 push @includes, $args{include_paths};
71             }
72 4 100       10 $self->{include_paths} = [ grep { length($_) && -d $_ } @includes ];
  7         118  
73             }
74 731 100       2012 $self->{debug} = 1 if $args{debug};
75 731         4194 bless $self, $class;
76             }
77              
78              
79             =item include_paths
80              
81             The return the list of directories where the included files need to be searched.
82              
83             =item included_files
84              
85             The return the list of files actually included.
86              
87             =cut
88              
89             sub include_paths {
90 659     659 1 1120 return @{ shift->{include_paths} };
  659         3175  
91             }
92              
93             sub included_files {
94 7     7 1 12 return @{ shift->{included_files} };
  7         68  
95             }
96              
97             sub _add_to_included_files {
98 2     2   15 my ($self, @files) = @_;
99 2         3 push @{shift->{included_files}}, @files;
  2         10  
100             }
101              
102             sub _list_index_map {
103             # numerals
104 1075     1075   1704 my $self = shift;
105 1075 100       2852 unless ($self->{_list_index_map}) {
106 96         429 my %map = map { $_ => $_ } (1..200); # never seen lists so long
  19200         39280  
107             # this is a bit naif but will do. Generated with Roman module. We
108             # support them to 89, otherwise you have to use i. i. i.
109              
110 96         3394 my @romans = (qw/i ii iii iv v vi vii viii ix x xi xii xiii
111             xiv xv xvi xvii xviii xix xx xxi xxii xxiii
112             xxiv xxv xxvi xxvii xxviii xxix xxx xxxi
113             xxxii xxxiii xxxiv xxxv xxxvi xxxvii xxxviii
114             xxxix xl xli xlii xliii xliv xlv xlvi xlvii
115             xlviii xlix l li lii liii liv lv lvi lvii
116             lviii lix lx lxi lxii lxiii lxiv lxv lxvi
117             lxvii lxviii lxix lxx lxxi lxxii lxxiii lxxiv
118             lxxv lxxvi lxxvii lxxviii lxxix lxxx lxxxi
119             lxxxii lxxxiii lxxxiv lxxxv lxxxvi lxxxvii
120             lxxxviii lxxxix/);
121 96         639 my @alpha = ('a'..'z');
122             # we will need to take care of 'i', 'x', 'v', 'l', which can be both alpha or roman
123 96         334 foreach my $list (\@alpha, \@romans) {
124 192         364 my $lcount = 0;
125 192         389 foreach my $letter (@$list) {
126 11040         13537 $lcount++;
127 11040         19134 $map{$letter} = $lcount;
128 11040         22239 $map{uc($letter)} = $lcount;
129             }
130             }
131 96         975 $self->{_list_index_map} = \%map;
132             }
133 1075         2262 return $self->{_list_index_map};
134             }
135              
136              
137             sub _debug {
138 3696     3696   1009275 my $self = shift;
139 3696         8010 my @args = @_;
140 3696 100 66     16091 if ((@args) && $self->{debug}) {
141 10         2970 print join("\n", @args), "\n";
142             }
143             }
144              
145              
146             =item filename
147              
148             Return the filename of the processed file
149              
150             =cut
151              
152             sub filename {
153 675     675 1 1183 my $self = shift;
154             return $self->{filename}
155 675         1708 }
156              
157             =item attachments
158              
159             Return the list of the filenames of the attached files, as linked.
160             With an optional argument, store that file in the list.
161              
162              
163             =cut
164              
165             sub attachments {
166 422     422 1 897 my ($self, $arg) = @_;
167 422 100       1062 unless (defined $self->{_attached_files}) {
168 24         87 $self->{_attached_files} = {};
169             }
170 422 100       911 if (defined $arg) {
171 420         1074 $self->{_attached_files}->{$arg} = 1;
172 420         947 return;
173             }
174             else {
175 2         5 return sort(keys %{$self->{_attached_files}});
  2         20  
176             }
177             }
178              
179             =item bidi_document
180              
181             Return true if the document uses a bidirectionl marker.
182              
183             =item set_bidi_document
184              
185             Internal, set the bidi flag on.
186              
187             =item has_ruby
188              
189             Return true if the document uses the ruby
190              
191             =item set_has_ruby
192              
193             Internal, set the ruby flag on.
194              
195             =cut
196              
197             sub bidi_document {
198 36     36 1 162 shift->{_bidi_document};
199             }
200              
201             sub set_bidi_document {
202 131     131 1 276 shift->{_bidi_document} = 1;
203             }
204              
205             sub set_has_ruby {
206 18     18 1 47 shift->{_has_ruby} = 1;
207             }
208              
209             sub has_ruby {
210 0     0 1 0 shift->{_has_ruby};
211             }
212              
213              
214              
215             =item language_code
216              
217             The language code of the document. This method will looks into the
218             header of the document, searching for the keys C or C,
219             defaulting to C.
220              
221             =item other_language_codes
222              
223             Same as above, but for other languages declared with the experimental
224             tag C<<[en>>
225              
226             =item language
227              
228             Same as above, but returns the human readable version, notably used by
229             Babel, Polyglossia, etc.
230              
231             =item other_languages
232              
233             Same as above, for the other languages
234              
235             =cut
236              
237             sub _language_mapping {
238 782     782   3156 return Text::Amuse::Utils::language_mapping();
239             }
240              
241             sub language_code {
242 1029     1029 1 1805 my $self = shift;
243 1029 100       2876 unless (defined $self->{_doc_language_code}) {
244 622         1982 my %header = $self->raw_header;
245 622   100     3865 my $lang = $header{lang} || $header{language} || "en";
246 622         1273 my $real = "en";
247             # check if language exists;
248 622 100       1519 if ($self->_language_mapping->{$lang}) {
249 597         1613 $real = $lang;
250             }
251 622         15374 $self->{_doc_language_code} = $real;
252             }
253 1029         4197 return $self->{_doc_language_code};
254             }
255              
256             sub language {
257 64     64 1 97 my $self = shift;
258 64 50       167 unless (defined $self->{_doc_language}) {
259 64         126 my $lc = $self->language_code;
260             # guaranteed not to return undef
261 64         118 $self->{_doc_language} = $self->_language_mapping->{$lc};
262             }
263 64         1538 return $self->{_doc_language};
264             }
265              
266             sub other_language_codes {
267 161     161 1 250 my $self = shift;
268 161         226 my @out = @{ $self->{_other_doc_language_codes} };
  161         416  
269 161 100       857 return @out ? \@out : undef;
270             }
271              
272             sub other_languages {
273 34     34 1 57 my $self = shift;
274 34         57 my $map = $self->_language_mapping;
275 34 100       81 my @out = map { $map->{$_} } @{ $self->other_language_codes || [] };
  5         26  
  34         75  
276 34 100       757 return @out ? \@out : undef;
277             }
278              
279             sub _add_to_other_language_codes {
280 62     62   133 my ($self, $lang) = @_;
281 62 50       141 return unless $lang;
282 62         154 $lang = lc($lang);
283 62 100       141 if ($self->_language_mapping->{$lang}) {
284 61 100       202 if ($lang ne $self->language_code) {
285 60 100       104 unless (grep { $_ eq $lang } @{ $self->other_language_codes || [] }) {
  114 100       326  
  60         138  
286 11         21 push @{$self->{_other_doc_language_codes}}, $lang;
  11         36  
287 11         33 return $lang;
288             }
289             }
290             }
291             else {
292 1         142 warn "Unknown language $lang";
293             }
294 51         1641 return 'en';
295             }
296              
297             =item parse_directives
298              
299             Return an hashref with the directives found in the document.
300              
301             =cut
302              
303             sub parse_directives {
304 19     19 1 41 my $self = shift;
305 19         59 my ($directives, $body) = $self->_parse_body_and_directives(directives_only => 1);
306 19         103 return $directives;
307             }
308              
309              
310             sub _parse_body_and_directives {
311 674     674   1632 my ($self, %options) = @_;
312 674         1844 my $file = $self->filename;
313 674 50   21   30868 open (my $fh, "<:encoding(UTF-8)", $file) or die "Couldn't open $file! $!\n";
  21         218  
  21         46  
  21         405  
314              
315 674         296468 my $in_meta = 1;
316 674         2498 my ($lastdirective, %directives, @body);
317 674         0 my @directives_array;
318             RAWLINE:
319 674         29138 while (my $line = <$fh>) {
320             # EOL
321 19871         45894 $line =~ s/\r\n/\n/gs;
322 19871         30268 $line =~ s/\r/\n/gs;
323             # TAB
324 19871         28590 $line =~ s/\t/ /g;
325             # trailing
326 19871         44158 $line =~ s/ +$//mg;
327              
328 19871 100       33329 if ($in_meta) {
329             # reset the directives on blank lines
330 2234 100       10989 if ($line =~ m/^\s*$/s) {
    100          
    100          
331 632         1088 $lastdirective = undef;
332             } elsif ($line =~ m/^\#([A-Za-z0-9_-]+)(\s+(.+))?$/s) {
333 842         3473 my ($dir, $material) = ($1, $3);
334              
335             # remove underscore and dashes from directive names to
336             # keep compatibility with Emacs Muse, so e.g.
337             # #disable-tables will be parsed as directive, not as
338             # a line.
339              
340 842         2300 $dir =~ s/[_-]//g;
341 842 100       2510 unless (length($dir)) {
342 24         1884 warn "$file: Found empty directive $line, it will be removed\n";
343             }
344 842 100       2692 if (exists $directives{$dir}) {
345 17         1152 warn "$file: Overwriting directive '$dir' $directives{$dir} with $line\n";
346             }
347 842 100       1953 if (defined $material) {
348 783         2489 $directives{$dir} = $material;
349             }
350             else {
351 59         197 $directives{$dir} = '';
352             }
353 842         2994 push @directives_array, [ $dir, $directives{$dir} ];
354              
355 842         2180 $lastdirective = $dir;
356             } elsif ($lastdirective) {
357 86         245 $directives{$lastdirective} .= $line;
358 86         183 $directives_array[-1][1] .= $line;
359             } else {
360 674         1242 $in_meta = 0
361             }
362             }
363 19871 100       35402 if ($in_meta) {
    100          
364 1560         5106 next RAWLINE;
365             }
366             elsif ($options{directives_only}) {
367 19         42 last RAWLINE;
368             }
369             else {
370 18292         57901 push @body, $line;
371             }
372             }
373 674         2443 push @body, "\n"; # append a newline
374 674         10299 close $fh;
375              
376             # before returning, let's clean the %directives from EOLs and from
377             # empty ones, e.g. #---------------------
378 674         2039 delete $directives{''};
379              
380 674         2996 foreach my $key (keys %directives) {
381 813         5155 $directives{$key} =~ s/\s+/ /gs;
382 813         4034 $directives{$key} =~ s/\s+\z//gs;
383 813         2478 $directives{$key} =~ s/\A\s+//gs;
384             }
385 674         4925 return (\%directives, \@body, \@directives_array);
386             }
387              
388             sub _split_body_and_directives {
389 655     655   1112 my $self = shift;
390 655         1822 my ($directives, $body, $dir_array) = $self->_parse_body_and_directives;
391              
392 655 100       2549 if (my @include_paths = $self->include_paths) {
393             # rescan the body and do the inclusion
394 4         5 my @full_body;
395             LINE:
396 4         19 foreach my $l (@$body) {
397 38 100       131 if ($l =~ m/^#include\s+(.+?)\s*$/) {
398 11 100       31 if (my $lines = $self->_resolve_include($1, \@include_paths)) {
399 2         8 push @full_body, @$lines;
400 2         5 next LINE;
401             }
402             }
403 36         81 push @full_body, $l;
404             }
405 4         20 $body = \@full_body;
406             }
407 655         1569 $self->{raw_body} = $body;
408 655         1372 $self->{raw_header} = $directives;
409 655         1793 $self->{directives_array} = $dir_array;
410             }
411              
412             sub _resolve_include {
413 11     11   37 my ($self, $filename, $include_paths) = @_;
414 11         126 my ($volume, $directories, $file) = File::Spec->splitpath($filename);
415 11         87 my @dirs = grep { length $_ } File::Spec->splitdir($directories);
  71         141  
416             # if hidden files or traversals are passed, bail out.
417 11 100       29 if (grep { /^\./ } @dirs, $file) {
  66         147  
418 6         501 warn "Directory traversal or hidden file found in included $filename!";
419 6         52 return;
420             }
421             # if we have slash (unix) or backslash (windows), it's not good
422 5 100       10 if (grep { /[\/\\]/ } @dirs, $file) {
  8         24  
423 2         17 warn "Invalid file or directory name (slashes?) found in included $filename!";
424 2         14 return;
425             }
426             # just in case
427 3 100       17 return unless $file;
428              
429             # the base directory are set by the object, not by the user, so
430             # they are considered safe.
431 2         3 my @out;
432              
433             INCLUDEFILE:
434 2         5 foreach my $base (@$include_paths) {
435 2         33 my $final = File::Spec->catfile($base, @dirs, $file);
436 2 50 33     204 if (-e $final && -T $final) {
437 2 50       74 open (my $fh, "<:encoding(UTF-8)", $final) or die "Couldn't open $final! $!\n";
438 2         173 while (my $line = <$fh>) {
439 6         32 $line =~ s/\r\n/\n/gs;
440 6         13 $line =~ s/\r/\n/gs;
441             # TAB
442 6         9 $line =~ s/\t/ /g;
443             # trailing
444 6         14 $line =~ s/ +$//mg;
445 6         34 push @out, $line;
446             }
447 2         25 close $fh;
448 2         12 $self->_add_to_included_files($final);
449 2         9 last INCLUDEFILE;
450             }
451             }
452 2 50       6 if (@out) {
453 2         10 return \@out;
454             }
455             else {
456 0         0 return;
457             }
458             }
459              
460             =item raw_header
461              
462             Accessor to the raw header of the muse file. The header is returned as
463             hash, with key/value pairs. Please note: NOT an hashref.
464              
465             =cut
466              
467             sub raw_header {
468 794     794 1 1391 my $self = shift;
469 794 100       2026 unless (defined $self->{raw_header}) {
470 636         1931 $self->_split_body_and_directives;
471             }
472 794         1290 return %{$self->{raw_header}}
  794         3771  
473             }
474              
475             =item raw_body
476              
477             Accessor to the raw body of the muse file. The body is returned as a
478             list of lines.
479              
480             =item directives_array
481              
482             This is very similar to raw_header, but store them in an array, so the
483             header can be rewritten.
484              
485             =cut
486              
487             sub raw_body {
488 638     638 1 1156 my $self = shift;
489 638 100       1808 unless (defined $self->{raw_body}) {
490 17         63 $self->_split_body_and_directives;
491             }
492 638         1171 return @{$self->{raw_body}}
  638         5218  
493             }
494              
495             sub directives_array {
496 2     2 1 4 my $self = shift;
497 2 50       11 unless (defined $self->{directives_array}) {
498 2         7 $self->_split_body_and_directives;
499             }
500 2         4 return @{$self->{directives_array}}
  2         8  
501             }
502              
503             sub _parse_body {
504 627     627   1164 my $self = shift;
505 627         1976 $self->_debug("Parsing body");
506              
507             # be sure to start with a null block and reset the state
508 627         1999 my @parsed = ($self->_construct_element(""));
509 627         1760 $self->_current_el(undef);
510              
511 627         1938 foreach my $l ($self->raw_body) {
512             # if doesn't return anything, the thing got merged
513 18793 100       38753 if (my $el = $self->_construct_element($l)) {
514 12532         30818 push @parsed, $el;
515             }
516             }
517 627         6531 $self->_debug(Dumper(\@parsed));
518              
519             # turn the versep into verse now that the merging is done
520 627         14811 foreach my $el (@parsed) {
521 13159 100       25151 if ($el->type eq 'versep') {
522 63         223 $el->type('verse');
523             }
524             }
525             # turn the direction switching into proper open/close blocks
526             {
527 627         1497 my $current_direction = '';
  627         1373  
528 627         3069 my %dirs = (
529             '<<<' => 'rtl',
530             '>>>' => 'ltr',
531             );
532 627         1458 foreach my $el (@parsed) {
533 13159 100       23662 if ($el->type eq 'bidimarker') {
534 45         162 $self->set_bidi_document;
535 45 50       104 my $dir = $dirs{$el->block} or die "Invalid bidimarker " . $el->block;
536 45 100 100     196 if ($current_direction and $current_direction ne $dir) {
537 15         52 $el->type('stopblock');
538 15         40 $el->block($current_direction);
539 15         38 $current_direction = '';
540             }
541             else {
542 30 100       633 warn "Direction already set to $current_direction!" if $current_direction;
543 30         128 $el->type('startblock');
544 30         84 $el->block($dir);
545 30         76 $current_direction = $dir;
546             }
547             }
548             }
549             }
550 627         2538 $self->_reset_list_parsing_output;
551             LISTP:
552 627         1881 while (@parsed) {
553 13159         20642 my $el = shift @parsed;
554 13159 100 100     25379 if ($el->type eq 'li' or $el->type eq 'dd') {
    100          
    100          
555 1438 100       2927 if ($self->_list_pile_count) {
    100          
556             # indentation is major, open a new level
557 1101 100       2225 if (_indentation_kinda_major($el, $self->_list_pile_last_element)) {
558 208         472 $self->_list_open_new_list_level($el);
559             }
560             else {
561             # close the lists until we get the right level
562 893         2349 $self->_list_close_until_indentation($el);
563 893 50       2032 if ($self->_list_pile_count) { # continue if open
564 893 100 100     1982 if ($self->_list_element_is_same_kind_as_in_list($el) and
565             $self->_list_element_is_a_progression($el)) {
566 779         1571 $self->_list_continuation($el);
567             }
568             else {
569 114         371 my $top = $self->_list_pile_last_element;
570 114   100     301 while ($self->_list_pile_count and
571             _indentation_kinda_equal($top, $self->_list_pile_last_element)) {
572             # empty the pile until the indentation drops.
573 228         596 $self->_close_list_level;
574             }
575             # and open a new level
576 114         323 $self->_list_open_new_list_level($el);
577             }
578             }
579             else { # if by chance, we emptied all, start anew.
580 0         0 $self->_list_open_new_list_level($el);
581             }
582             }
583             }
584             # no list pile, this is the first element
585             elsif ($self->_list_element_can_be_first($el)) {
586 315         899 $self->_list_open_new_list_level($el);
587             }
588             else {
589             # reparse and should become quote/center/right
590 22         97 $self->_append_element_to_list_parsing_output($self->_reparse_nolist($el));
591 22         177 next LISTP; # call next to avoid being mangled.
592             }
593 1416         4089 $el->become_regular;
594             }
595             elsif ($el->type eq 'regular') {
596             # the type is regular: It can only close or continue
597 3020         7202 $self->_list_close_until_indentation($el);
598 3020 100       5437 if ($self->_list_pile_count) {
599 185         447 $el->become_regular;
600             }
601             }
602             elsif ($el->type ne 'null') { # something else: close the pile
603 2117         4476 $self->_list_flush;
604             }
605 13137         25320 $self->_append_element_to_list_parsing_output($el);
606             }
607             # end of input, flush what we have.
608 627         1895 $self->_list_flush;
609              
610             # now we use parsed as output
611 627         2091 $self->_flush_current_footnote;
612 627         951 my @out;
613 627         1169 my $elnum = 0;
614 627         1056 while (@{$self->_list_parsing_output}) {
  18276         28042  
615 17649         22939 my $el = shift @{$self->_list_parsing_output};
  17649         25660  
616 17649         22946 $elnum++;
617 17649         40864 $el->_set_element_number($elnum);
618 17649 100 100     30660 if ($el->type eq 'footnote' or $el->type eq 'secondary_footnote') {
    100          
619 556         1380 $self->_register_footnote($el);
620             }
621             elsif (my $fn_indent = $self->_current_footnote_indent) {
622 883 100 66     1844 if ($el->type eq 'null') {
    100 100        
623 644         1417 push @parsed, $el;
624             }
625             elsif ($el->can_be_regular and
626             $el->indentation and
627             _kinda_equal($el->indentation, $fn_indent)) {
628 98         156 push @{$self->_current_footnote_stack}, Text::Amuse::Element->new($self->_parse_string("
\n")), $el;
  98         206  
629             }
630             else {
631 141         403 $self->_flush_current_footnote;
632 141         351 push @parsed, $el;
633             }
634             }
635             else {
636 16210         27905 push @parsed, $el;
637             }
638             }
639 627         1896 $self->_flush_current_footnote;
640              
641             # unroll the quote/center/right blocks
642 627         1698 while (@parsed) {
643 16995         24674 my $el = shift @parsed;
644 16995 100       32742 if ($el->can_be_regular) {
645 172         493 my $open = $self->_create_block(open => $el->block, $el->indentation);
646 172         581 my $close = $self->_create_block(close => $el->block, $el->indentation);
647 172         618 $el->block("");
648 172         558 push @out, $open, $el, $close;
649             }
650             else {
651 16823         34733 push @out, $el;
652             }
653             }
654              
655 627         1201 my @pile;
656 627         1627 while (@out) {
657 17339         26644 my $el = shift @out;
658 17339 100 100     33187 if ($el->type eq 'startblock') {
    100          
    100          
659 2442         5238 push @pile, $self->_create_block(close => $el->block, $el->indentation);
660 2442         6083 $self->_debug("Pushing " . $el->block);
661 2442 50       5520 die "Uh?\n" unless $el->block;
662             }
663             elsif ($el->type eq 'stopblock') {
664 2441         3644 my $exp = pop @pile;
665 2441 100 66     6509 unless ($exp and $exp->block eq $el->block) {
666 25         67 warn "Couldn't retrieve " . $el->block . " from the pile\n";
667             # put it back
668 25 50       160 push @pile, $exp if $exp;
669             # so what to do here? just removed it
670 25         212 next;
671             }
672             }
673             elsif (@pile and $el->should_close_blocks) {
674              
675 31         78 my @carry_on;
676 31         98 my %close_rtl = map { $_ => 1 } (qw/h1 h2 h3 h4 h5 h6 newpage/);
  217         483  
677              
678 31         130 while (@pile) {
679 31         82 my $block = pop @pile;
680 31 100 100     92 if (($block->block eq 'rtl' || $block->block eq 'ltr') and !$close_rtl{$el->type}) {
      100        
681 15         63 push @carry_on, $block;
682             }
683             else {
684 16         50 warn "Forcing the closing of " . $block->block . "\n";
685 16         140 push @parsed, $block;
686             }
687             }
688 31         152 push @pile, reverse @carry_on;
689             }
690 17314         37361 push @parsed, $el;
691             }
692             # do we still have things into the pile?
693 627         1979 while (@pile) {
694 10         42 push @parsed, pop @pile;
695             }
696 627         2167 return \@parsed;
697             }
698              
699             =item elements
700              
701             Return the list of the elements which compose the body, once they have
702             properly parsed and packed. Footnotes are removed. (To get the
703             footnotes use the accessor below).
704              
705             =cut
706              
707             sub elements {
708 1008     1008 1 3329 my $self = shift;
709 1008 100       2949 unless (defined $self->{_parsed_document}) {
710 627         1754 $self->{_parsed_document} = $self->_parse_body;
711             }
712 1008 50       2678 if (defined wantarray) {
713 1008         1550 return @{$self->{_parsed_document}};
  1008         7236  
714             }
715             else {
716 0         0 return;
717             }
718             }
719              
720             =item get_footnote
721              
722             Accessor to the internal footnotes hash. You can access the footnote
723             with a numerical argument or even with a string like [123]
724              
725             =cut
726              
727             sub get_footnote {
728 1905     1905 1 3877 my ($self, $arg) = @_;
729 1905 100       4340 return undef unless $arg;
730 1904 50       10838 if ($arg =~ m/(\{[1-9][0-9]*\}|\[[1-9][0-9]*\])/) {
731 1904         5114 $arg = $1;
732             }
733             else {
734 0         0 return undef;
735             }
736 1904 100       3921 if (exists $self->_raw_footnotes->{$arg}) {
737 1628         3011 return $self->_raw_footnotes->{$arg};
738             }
739 276         1163 else { return undef }
740             }
741              
742             sub _raw_footnotes {
743 4644     4644   6798 my $self = shift;
744 4644         16400 return $self->{_raw_footnotes};
745             }
746              
747             sub _current_footnote_stack {
748 2494     2494   5914 return shift->{_current_footnote_stack};
749             }
750              
751             sub _current_footnote_number {
752 2560     2560   3582 my $self = shift;
753 2560 100       4895 if (@_) {
754 2507         3912 $self->{_current_footnote_number} = shift;
755             }
756 2560         4092 return $self->{_current_footnote_number};
757             }
758              
759             sub _current_footnote_indent {
760 19600     19600   27360 my $self = shift;
761 19600 100       33825 if (@_) {
762 2507         4110 $self->{_current_footnote_indent} = shift;
763             }
764 19600         34042 return $self->{_current_footnote_indent};
765             }
766              
767              
768              
769             sub _parse_string {
770 19600     19600   37905 my ($self, $l, %opts) = @_;
771 19600 50       38738 die unless defined $l;
772 19600         48665 my %element = (
773             rawline => $l,
774             raw_without_anchors => $l,
775             );
776 19600 100       54602 if ($l =~ m/\A
777             (\s*)
778             (\#([A-Za-z][A-Za-z0-9-]+)\x{20}*)
779             (.*)
780             \z
781             /sx) {
782 420         1347 $element{anchor} = $3;
783 420         1237 $l = $1 . $4;
784 420         821 $element{raw_without_anchors} = $l;
785             }
786 19600         57256 my $blockre = qr{(
787             biblio |
788             play |
789             comment |
790             verse |
791             center |
792             right |
793             example |
794             quote
795             )}x;
796              
797             # null line is default, do nothing
798 19600 100       73761 if ($l =~ m/^[\n\t ]*$/s) {
799             # do nothing, already default
800 7972         15029 $element{removed} = $l;
801 7972         41691 return %element;
802             }
803 11628 100       58299 if ($l =~ m!^(<($blockre)>\s*)$!s) {
804 394         1017 $element{type} = "startblock";
805 394         1271 $element{removed} = $1;
806 394         852 $element{block} = $2;
807 394         2522 return %element;
808             }
809 11234 100       32569 if ($l =~ m/^((\<\<\<|\>\>\>)\s*)$/s) {
810             # here turn them into language switch
811 45         108 $element{type} = "bidimarker";
812 45         137 $element{removed} = $1;
813 45         98 $element{block} = $2;
814 45         325 return %element;
815             }
816 11189 100       24391 if ($l =~ m/^(
817             (
818             \<
819             (\/?)
820             \[
821             ([a-zA-Z-]+)
822             \]
823             \>
824             )
825             \s*
826             )$/sx) {
827 15         75 my ($all, $full, $close, $lang) = ($1, $2, $3, $4);
828 15 100       48 $element{type} = $close ? "stopblock" : "startblock";
829 15         36 $element{language} = $lang;
830 15         34 $element{removed} = $l;
831 15         50 $self->_add_to_other_language_codes($lang);
832 15         271 $element{block} = "languageswitch";
833 15         139 return %element;
834             }
835 11174 100       22201 if ($l =~ m/^(\{\{\{)\s*$/s) {
836 75         248 $element{type} = "startblock";
837 75         177 $element{removed} = $l;
838 75         173 $element{block} = 'example';
839 75         165 $element{style} = '{{{}}}';
840 75         601 return %element;
841             }
842 11099 100       21390 if ($l =~ m/^(\}\}\})\s*$/s) {
843 75         218 $element{type} = "stopblock";
844 75         148 $element{removed} = $l;
845 75         169 $element{block} = 'example';
846 75         163 $element{style} = '{{{}}}';
847 75         539 return %element;
848             }
849 11024 100       40735 if ($l =~ m!^(\s*)$!s) {
850 369         1013 $element{type} = "stopblock";
851 369         1047 $element{removed} = $1;
852 369         821 $element{block} = $2;
853 369         2311 return %element;
854             }
855             # headers
856 10655 100       27092 if ($l =~ m!^((\*{1,5}) )(.+)$!s) {
857 787         3415 $element{type} = "h" . length($2);
858 787         1958 $element{removed} = $1;
859 787         1843 $element{string} = $3;
860 787         5639 return %element;
861             }
862 9868 100       19755 if ($l =~ m/^(\> )(.*)/s) {
863 182         663 $element{string} = $2;
864 182         448 $element{removed} = $1;
865 182         385 $element{type} = "versep";
866 182         1176 return %element;
867             }
868 9686 100       18027 if ($l =~ m/^(\>)$/s) {
869 35         106 $element{string} = "\n";
870 35         84 $element{removed} = ">";
871 35         73 $element{type} = "versep";
872 35         260 return %element;
873             }
874 9651 100 100     31782 if ($l =~ m/^(\x{20}+)/s and $l =~ m/\|/) {
875 544         1258 $element{type} = "table";
876 544         871 $element{string} = $l;
877 544         3089 return %element;
878             }
879             # line starting with pipe, gh-markdown style
880 9107 100       19383 if ($l =~ m/^\|+(\x{20}+|\+)/s) {
881 30         75 $element{type} = "table";
882 30         59 $element{string} = $l;
883 30         159 return %element;
884             }
885 9077 100       17433 if ($l =~ m/^(\;)(\x{20}+(.*))?$/s) {
886 92         327 $element{removed} = $1;
887 92         241 $element{string} = $3;
888 92 100       262 unless (defined ($element{string})) {
889 12         24 $element{string} = '';
890             }
891 92         179 $element{type} = "inlinecomment";
892 92         642 return %element;
893             }
894 8985 100       19692 if ($l =~ m/^((\[([1-9][0-9]*)\])\x{20}+)(.+)$/s) {
895 456         1234 $element{type} = "footnote";
896 456         1342 $element{removed} = $1;
897 456         1016 $element{footnote_symbol} = $2;
898 456         941 $element{footnote_number} = $3;
899 456         894 $element{footnote_index} = $3;
900 456         1191 $element{string} = $4;
901 456         4216 return %element;
902             }
903 8529 100       16813 if ($l =~ m/^((\{([1-9][0-9]*)\})\x{20}+)(.+)$/s) {
904 137         341 $element{type} = "secondary_footnote";
905 137         386 $element{removed} = $1;
906 137         288 $element{footnote_symbol} = $2;
907 137         297 $element{footnote_number} = $3;
908 137         404 $element{footnote_index} = 'b'. $3;
909 137         336 $element{string} = $4;
910 137         1332 return %element;
911             }
912 8392 100       17542 if ($l =~ m/^((\x{20}{6,})((\*\x{20}?){5})\s*)$/s) {
913 44         111 $element{type} = "newpage";
914 44         131 $element{removed} = $2;
915 44         110 $element{string} = $3;
916 44         293 return %element;
917             }
918 8348 100       18202 if ($l =~ m/\A
919             (\x{20}+) # 1. initial space and indentation
920             (.+) # 2. desc title
921             (\x{20}+) # 3. space
922             (\:\:) # 4 . separator
923             ((\x{20}?)(.*)) # 5 6. space 7. text
924             \z
925             /xs) {
926 150         415 $element{block} = 'dl';
927 150         306 $element{type} = 'dd';
928 150         461 $element{string} = $7;
929 150         366 $element{attribute} = $2;
930 150         319 $element{attribute_type} = 'dt';
931 150         707 $element{removed} = $1 . $2 . $3 . $4 . $6;
932 150         435 $element{indentation} = length($1) + 2;
933 150         268 $element{start_list_index} = 1;
934 150         1403 return %element;
935             }
936 8198 100       17776 if (!$opts{nolist}) {
937 8176 100       19929 if ($l =~ m/^((\x{20}+)\-\x{20}+)(.*)/s) {
938 481         1215 $element{type} = "li";
939 481         1311 $element{removed} = $1;
940 481         1121 $element{string} = $3;
941 481         881 $element{block} = "ul";
942 481         1151 $element{indentation} = length($2) + 2;
943 481         793 $element{start_list_index} = 1;
944 481         4006 return %element;
945             }
946 7695 100       20779 if ($l =~ m/^((\x{20}+) # leading space and type $1
947             ( # the type $2
948             [0-9]+ |
949             [a-zA-Z] |
950             [ixvl]+ |
951             [IXVL]+
952             )
953             \. # single dot
954             \x{20}+) # space
955             (.*) # the string itself $4
956             /sx) {
957 1059         4811 my ($remove, $whitespace, $prefix, $text) = ($1, $2, $3, $4);
958              
959             # validate roman numbers, so we don't end up with random strings
960 1059 50       2526 if (my $list_index = $self->_get_start_list_index($prefix)) {
961 1059         2172 $element{type} = "li";
962 1059         1877 $element{removed} = $remove;
963 1059         2089 $element{string} = $text;
964 1059         2219 my $list_type = $self->_identify_list_type($prefix);
965 1059         3027 $element{indentation} = length($whitespace) + 2;
966 1059         2003 $element{block} = $list_type;
967 1059         1700 $element{start_list_index} = $list_index;
968 1059         9303 return %element;
969             }
970             }
971             }
972 6658 100       14170 if ($l =~ m/^(\x{20}{20,})([^ ].+)$/s) {
973 65         199 $element{block} = "right";
974 65         167 $element{type} = "regular";
975 65         218 $element{removed} = $1;
976 65         173 $element{string} = $2;
977 65         535 return %element;
978             }
979 6593 100       15368 if ($l =~ m/^(\x{20}{6,})([^ ].+)$/s) {
980 487         1245 $element{block} = "center";
981 487         907 $element{type} = "regular";
982 487         1453 $element{removed} = $1;
983 487         1098 $element{string} = $2;
984 487         3489 return %element;
985             }
986 6106 100       14103 if ($l =~ m/^(\x{20}{2,})([^ ].+)$/s) {
987 966         2262 $element{block} = "quote";
988 966         1660 $element{type} = "regular";
989 966         2613 $element{removed} = $1;
990 966         2146 $element{string} = $2;
991 966         7014 return %element;
992             }
993             # anything else is regular
994 5140         9643 $element{type} = "regular";
995 5140         8758 $element{string} = $l;
996 5140         28825 return %element;
997             }
998              
999              
1000             sub _identify_list_type {
1001 1059     1059   2128 my ($self, $list_type) = @_;
1002 1059         1459 my $type;
1003 1059 100       6267 if ($list_type =~ m/\A[0-9]+\z/) {
    100          
    100          
    100          
    50          
1004 390         739 $type = "oln";
1005             } elsif ($list_type =~ m/\A[ixvl]+\z/) {
1006 166         335 $type = "oli";
1007             } elsif ($list_type =~ m/\A[IXVL]+\z/) {
1008 77         163 $type = "olI";
1009             } elsif ($list_type =~ m/\A[a-z]\z/) {
1010 273         571 $type = "ola";
1011             } elsif ($list_type =~ m/\A[A-Z]\z/) {
1012 153         311 $type = "olA";
1013             } else {
1014 0         0 die "$list_type unrecognized, fix your code\n";
1015             }
1016 1059         1986 return $type;
1017             }
1018              
1019             sub _get_start_list_index {
1020 1064     1064   2099 my ($self, $prefix) = @_;
1021 1064         2769 my $map = $self->_list_index_map;
1022 1064 100       3205 if (exists $map->{$prefix}) {
1023 1062         3206 return $map->{$prefix};
1024             }
1025             else {
1026 2         59 warn "$prefix doesn't map exactly to a list index!\n";
1027 2         19 return 0;
1028             }
1029             }
1030              
1031             sub _list_element_can_be_first {
1032 337     337   744 my ($self, $el) = @_;
1033             # every dd can be the first
1034 337 100       723 return 1 if $el->type eq 'dd';
1035 297 50       686 return unless $el->type eq 'li';
1036             # first element, can't be too indented
1037 297 100       804 if ($el->indentation > 8) {
1038 22         75 return 0;
1039             }
1040             else {
1041 275         792 return $el->start_list_index;
1042             }
1043             }
1044              
1045             sub _current_el {
1046 33318     33318   48287 my $self = shift;
1047 33318 100       63205 if (@_) {
1048 13842         21883 $self->{_current_el} = shift;
1049             }
1050 33318         52241 return $self->{_current_el};
1051             }
1052              
1053             sub _reparse_nolist {
1054 22     22   53 my ($self, $element) = @_;
1055 22         60 my %args = $self->_parse_string($element->rawline, nolist => 1);
1056 22         115 my $el = Text::Amuse::Element->new(%args);
1057 22 50       73 if ($el->type eq 'regular') {
1058 22         117 return $el;
1059             }
1060             else {
1061 0         0 die "Reparsing of " . $element->rawline . " led to " . $el->type;
1062             }
1063             }
1064              
1065             sub _construct_element {
1066 19476     19476   41276 my ($self, $line) = @_;
1067 19476         33193 my $current = $self->_current_el;
1068 19476         41351 my %args = $self->_parse_string($line);
1069 19476         77862 my $element = Text::Amuse::Element->new(%args);
1070              
1071 19476 100 100     64093 if ($current and ($current->type eq 'null' or $current->type eq 'startblock') and $current->anchors) {
      100        
      100        
1072             # previous element is null, carry on
1073 188         496 $current->move_anchors_to($element);
1074             }
1075 19476 100 100     43745 if ($element->type eq 'null' and
      100        
      100        
1076             $element->anchors and
1077             $current->type ne 'null' and
1078             $current->type ne 'example') {
1079             # incoming has anchors
1080 108         345 $element->move_anchors_to($current);
1081             # null element with anchors. it was fully merged, so return
1082 108         768 return;
1083             }
1084              
1085             # catch the examples, comments and the verse in bloks.
1086             # is greedy, and will stop only at another or
1087             # at the end of input. Same is true for verse and comments.
1088              
1089 19368         37160 foreach my $block (qw/example comment verse/) {
1090 53597 100 100     127119 if ($current && $current->type eq $block) {
    100          
1091 2336 100       5368 if ($element->is_stop_element($current)) {
1092 316         944 $self->_current_el(undef);
1093 316         944 return Text::Amuse::Element->new(type => 'null',
1094             removed => $element->rawline,
1095             anchors => [ $element->anchors ],
1096             rawline => $element->rawline);
1097             }
1098             else {
1099             # remove inlined comments from verse environments
1100 2020 100 100     4194 if ($current->type eq 'verse' and
1101             $element->type eq 'inlinecomment') {
1102             }
1103             else {
1104 2014         4515 $current->append($element);
1105             }
1106 2020         11909 return;
1107             }
1108             }
1109             elsif ($element->is_start_block($block)) {
1110 352         1171 $current = Text::Amuse::Element->new(type => $block,
1111             style => $element->style,
1112             anchors => [ $element->anchors ],
1113             removed => $element->rawline,
1114             raw_without_anchors => $element->raw_without_anchors,
1115             rawline => $element->rawline);
1116 352         1264 $self->_current_el($current);
1117 352         2753 return $current;
1118             }
1119             }
1120             # Pack the lines
1121 16680 100 100     47421 if ($current && $current->can_append($element)) {
1122             # print "Packing " . Dumper($element) . ' into ' . Dumper($current);
1123 4133         12182 $current->append($element);
1124 4133         25839 return;
1125             }
1126              
1127 12547         31650 $self->_current_el($element);
1128 12547         40863 return $element;
1129             }
1130              
1131             sub _create_block {
1132 7148     7148   13258 my ($self, $open_close, $block, $indentation) = @_;
1133 7148 50 33     21700 die unless $open_close && $block;
1134 7148         9392 my $type;
1135 7148 100       14871 if ($open_close eq 'open') {
    50          
1136 2353         3627 $type = 'startblock';
1137             }
1138             elsif ($open_close eq 'close') {
1139 4795         7139 $type = 'stopblock';
1140             }
1141             else {
1142 0         0 die "$open_close is not a valid op";
1143             }
1144 7148         9725 my $removed = '';
1145 7148 50       13209 if ($indentation) {
1146 7148         12656 $removed = ' ' x $indentation;
1147             }
1148 7148         16693 return Text::Amuse::Element->new(block => $block,
1149             type => $type,
1150             removed => $removed);
1151             }
1152              
1153             sub _opening_blocks {
1154 1416     1416   2627 my ($self, $el) = @_;
1155 1416         2032 my @out;
1156 1416 100 66     3098 if ($el->attribute && $el->attribute_type) {
1157 128         374 @out = ($self->_create_block(open => $el->attribute_type, $el->indentation),
1158             Text::Amuse::Element->new(type => 'dt', string => $el->attribute),
1159             $self->_create_block(close => $el->attribute_type, $el->indentation));
1160             }
1161 1416         3630 push @out, $self->_create_block(open => $el->type, $el->indentation);
1162 1416         3512 return @out;
1163             }
1164              
1165             sub _closing_blocks {
1166 1416     1416   3027 my ($self, $el) = @_;
1167 1416         3488 my @out = ($self->_create_block(close => $el->type, $el->indentation));
1168 1416         2976 return @out;
1169             }
1170             sub _opening_blocks_new_level {
1171 637     637   1168 my ($self, $el) = @_;
1172 637         1603 my @out = ($self->_create_block(open => $el->block, $el->indentation),
1173             $self->_opening_blocks($el));
1174 637 50       1651 if (my $list_index = $el->start_list_index) {
1175 637         1868 $out[0]->start_list_index($list_index);
1176 637         1374 $out[1]->start_list_index($list_index);
1177             }
1178 637         1494 return @out;
1179             }
1180             sub _closing_blocks_new_level {
1181 637     637   1131 my ($self, $el) = @_;
1182 637         1420 my @out = ($self->_create_block(close => $el->block, $el->indentation),
1183             $self->_closing_blocks($el));
1184 637         1383 return @out;
1185             }
1186              
1187             sub _indentation_kinda_minor {
1188 1722     1722   3087 return _indentation_compare(@_) == IMINOR;
1189             }
1190              
1191             sub _indentation_kinda_major {
1192 1101     1101   2402 return _indentation_compare(@_) == IMAJOR;
1193             }
1194              
1195             sub _indentation_kinda_equal {
1196 271     271   510 return _indentation_compare(@_) == IEQUAL;
1197             }
1198              
1199             sub _kinda_equal {
1200 106     106   234 return _compare_tolerant(@_) == IEQUAL;
1201             }
1202              
1203             sub _indentation_compare {
1204 3094     3094   4709 my ($first, $second) = @_;
1205 3094         6019 my $one_indent = $first->indentation;
1206 3094         5489 my $two_indent = $second->indentation;
1207 3094         5295 return _compare_tolerant($one_indent, $two_indent);
1208             }
1209              
1210             sub _compare_tolerant {
1211 3212     3212   4955 my ($one_indent, $two_indent) = @_;
1212             # tolerance is zero if one of them is 0
1213 3212         4150 my $tolerance = 0;
1214 3212 100 100     9495 if ($one_indent && $two_indent) {
1215 2865         3884 $tolerance = 1;
1216             }
1217 3212         4514 my $diff = $one_indent - $two_indent;
1218 3212 100       7332 if ($diff - $tolerance > 0) {
    100          
1219 282         1062 return IMAJOR;
1220             }
1221             elsif ($diff + $tolerance < 0) {
1222 753         2644 return IMINOR;
1223             }
1224             else {
1225 2177         7906 return IEQUAL;
1226             }
1227             }
1228              
1229              
1230             sub _list_element_is_same_kind_as_in_list {
1231 893     893   1669 my ($self, $el) = @_;
1232 893         1467 my $list = $self->_list_element_pile;
1233 893         1935 my $find = $el->block;
1234 893         1488 my $found = 0;
1235 893         2315 for (my $i = $#$list; $i >= 0; $i--) {
1236 1786         3923 my $block = $list->[$i]->block;
1237 1786 100 100     5951 next if ($block eq 'li' or $block eq 'dd');
1238 893 100       2021 if ($block eq $find) {
1239 823         1252 $found = 1;
1240             }
1241 893         1334 last;
1242             }
1243 893         2725 return $found;
1244             }
1245              
1246             sub _register_footnote {
1247 556     556   1014 my ($self, $el) = @_;
1248 556         1263 my $fn_num = $el->footnote_symbol;
1249 556 50       1269 if (defined $fn_num) {
1250 556 50       1134 if ($self->_raw_footnotes->{$fn_num}) {
1251 0         0 warn "Overwriting footnote number $fn_num!\n";
1252             }
1253 556         1396 $self->_flush_current_footnote;
1254 556         1543 $self->_current_footnote_indent($el->indentation);
1255 556         1339 $self->_current_footnote_number($fn_num);
1256 556         1140 $self->_raw_footnotes->{$fn_num} = $el;
1257             }
1258             else {
1259 0         0 die "Something is wrong here! <" . $el->removed . ">"
1260             . $el->string . "!";
1261             }
1262             }
1263              
1264             sub _flush_current_footnote {
1265 1951     1951   2944 my $self = shift;
1266 1951 100       2910 if (@{$self->_current_footnote_stack}) {
  1951         3708  
1267 53         135 my $footnote = $self->get_footnote($self->_current_footnote_number);
1268 53 50       153 die "Missing current footnote to append " . Dumper($self->_current_footnote_stack) unless $footnote;
1269 53         107 while (@{$self->_current_footnote_stack}) {
  249         461  
1270 196         268 my $append = shift @{$self->_current_footnote_stack};
  196         673  
1271 196         471 $footnote->append($append);
1272             }
1273             }
1274 1951         4834 $self->_current_footnote_indent(0);
1275 1951         3879 $self->_current_footnote_number(undef)
1276             }
1277              
1278             # list parsing
1279              
1280             sub _list_element_pile {
1281 22568     22568   54382 return shift->{_list_element_pile};
1282             }
1283              
1284             sub _list_parsing_output {
1285 51774     51774   115857 return shift->{_list_parsing_output};
1286             }
1287              
1288             sub _list_pile_count {
1289 13396     13396   17927 my $self = shift;
1290 13396         16614 return scalar(@{$self->_list_element_pile});
  13396         20401  
1291             }
1292              
1293             sub _list_pile_last_element {
1294 4810     4810   6635 my $self = shift;
1295 4810         7184 return $self->_list_element_pile->[-1];
1296             }
1297              
1298             sub _reset_list_parsing_output {
1299 627     627   1235 my $self = shift;
1300 627         1751 $self->{_list_parsing_output} = [];
1301             }
1302              
1303             sub _list_open_new_list_level {
1304 637     637   1162 my ($self, $el) = @_;
1305 637         953 push @{$self->_list_parsing_output}, $self->_opening_blocks_new_level($el);
  637         1195  
1306 637         1737 my @pile = $self->_closing_blocks_new_level($el);
1307 637 50       1509 if (my $list_index = $el->start_list_index) {
1308 637         1791 $_->start_list_index($list_index) for @pile;
1309             }
1310 637         1051 push @{$self->_list_element_pile}, @pile;
  637         1266  
1311             }
1312              
1313             sub _list_continuation {
1314 779     779   1345 my ($self, $el) = @_;
1315 779         1347 my $current = $self->_list_pile_last_element->start_list_index + 1;
1316 779         1178 push @{$self->_list_parsing_output}, pop @{$self->_list_element_pile}, $self->_opening_blocks($el);
  779         1399  
  779         1240  
1317 779         1902 my @pile = $self->_closing_blocks($el);
1318 779 50       1757 if (my $list_index = $el->start_list_index) {
1319 779         2034 $_->start_list_index($current) for @pile;
1320             }
1321 779         1158 push @{$self->_list_element_pile}, @pile;
  779         1483  
1322             }
1323              
1324             sub _close_list_level {
1325 1274     1274   1846 my $self = shift;
1326 1274         1791 push @{$self->_list_parsing_output}, pop @{$self->_list_element_pile};
  1274         2043  
  1274         2159  
1327             }
1328              
1329             sub _append_element_to_list_parsing_output {
1330 13159     13159   21360 my ($self, $el) = @_;
1331 13159         16914 push @{$self->_list_parsing_output}, $el;
  13159         20940  
1332             }
1333              
1334             sub _list_close_until_indentation {
1335 3913     3913   6663 my ($self, $el) = @_;
1336 3913   100     7191 while ($self->_list_pile_count and
1337             _indentation_kinda_minor($el, $self->_list_pile_last_element)) {
1338 644         1365 $self->_close_list_level;
1339             }
1340             }
1341              
1342             sub _list_flush {
1343 2744     2744   3767 my $self = shift;
1344 2744         4822 while ($self->_list_pile_count) {
1345 402         768 $self->_close_list_level;
1346             }
1347             }
1348              
1349             sub _list_element_is_a_progression {
1350 823     823   1444 my ($self, $el) = @_;
1351             # not defined, not needed.
1352 823         1527 my $last = $self->_list_pile_last_element->start_list_index;
1353 823         1625 my $current = $el->start_list_index;
1354             # no index from one or another, we can't compare
1355 823 50 33     4247 if (!$last or !$current) {
    100 66        
1356 0         0 return 1;
1357             }
1358             elsif ($last > 0 and $current > 1) {
1359 317 100       795 if (($current - $last) == 1) {
1360 273         782 return 1;
1361             }
1362             else {
1363 44         145 return 0;
1364             }
1365             }
1366             else {
1367 506         1475 return 1;
1368             }
1369             }
1370              
1371             =back
1372              
1373             =cut
1374              
1375             1;