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 46     46   300539 use strict;
  46         132  
  46         1128  
4 46     46   198 use warnings;
  46         68  
  46         911  
5 46     46   16609 use Text::Amuse::Element;
  46         128  
  46         1359  
6 46     46   18499 use Text::Amuse::Utils;
  46         108  
  46         1245  
7 46     46   251 use File::Spec;
  46         83  
  46         1345  
8             use constant {
9 46         4580 IMAJOR => 1,
10             IEQUAL => 0,
11             IMINOR => -1,
12 46     46   226 };
  46         84  
13              
14 46     46   21563 use Data::Dumper;
  46         240636  
  46         303805  
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 44204 my $class = shift;
36 731         1314 my %args;
37 731         8197 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       2948 if (@_ % 2 == 0) {
    0          
51 731         3283 %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       15381 if (-f $args{file}) {
60 731         3682 $self->{filename} = $args{file};
61             } else {
62 0         0 die "Wrong argument! $args{file} doesn't exist!\n"
63             }
64 731 100       2442 if ($args{include_paths}) {
65 4         7 my @includes;
66 4 50       10 if (ref($args{include_paths}) eq 'ARRAY') {
67 4         5 push @includes, @{$args{include_paths}};
  4         9  
68             }
69             else {
70 0         0 push @includes, $args{include_paths};
71             }
72 4 100       8 $self->{include_paths} = [ grep { length($_) && -d $_ } @includes ];
  7         98  
73             }
74 731 100       2040 $self->{debug} = 1 if $args{debug};
75 731         3525 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 1026 return @{ shift->{include_paths} };
  659         2842  
91             }
92              
93             sub included_files {
94 7     7 1 9 return @{ shift->{included_files} };
  7         53  
95             }
96              
97             sub _add_to_included_files {
98 2     2   7 my ($self, @files) = @_;
99 2         3 push @{shift->{included_files}}, @files;
  2         7  
100             }
101              
102             sub _list_index_map {
103             # numerals
104 1075     1075   1463 my $self = shift;
105 1075 100       2438 unless ($self->{_list_index_map}) {
106 96         418 my %map = map { $_ => $_ } (1..200); # never seen lists so long
  19200         34671  
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         3031 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         602 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         276 foreach my $list (\@alpha, \@romans) {
124 192         268 my $lcount = 0;
125 192         328 foreach my $letter (@$list) {
126 11040         10705 $lcount++;
127 11040         15777 $map{$letter} = $lcount;
128 11040         19229 $map{uc($letter)} = $lcount;
129             }
130             }
131 96         886 $self->{_list_index_map} = \%map;
132             }
133 1075         1867 return $self->{_list_index_map};
134             }
135              
136              
137             sub _debug {
138 3696     3696   886297 my $self = shift;
139 3696         7372 my @args = @_;
140 3696 100 66     14310 if ((@args) && $self->{debug}) {
141 10         3861 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 1357 my $self = shift;
154             return $self->{filename}
155 675         1747 }
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 748 my ($self, $arg) = @_;
167 422 100       896 unless (defined $self->{_attached_files}) {
168 24         72 $self->{_attached_files} = {};
169             }
170 422 100       767 if (defined $arg) {
171 420         937 $self->{_attached_files}->{$arg} = 1;
172 420         682 return;
173             }
174             else {
175 2         3 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 219 shift->{_bidi_document};
199             }
200              
201             sub set_bidi_document {
202 131     131 1 214 shift->{_bidi_document} = 1;
203             }
204              
205             sub set_has_ruby {
206 18     18 1 38 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   3249 return Text::Amuse::Utils::language_mapping();
239             }
240              
241             sub language_code {
242 1029     1029 1 1848 my $self = shift;
243 1029 100       2999 unless (defined $self->{_doc_language_code}) {
244 622         2542 my %header = $self->raw_header;
245 622   100     3812 my $lang = $header{lang} || $header{language} || "en";
246 622         996 my $real = "en";
247             # check if language exists;
248 622 100       1826 if ($self->_language_mapping->{$lang}) {
249 597         1405 $real = $lang;
250             }
251 622         12765 $self->{_doc_language_code} = $real;
252             }
253 1029         4188 return $self->{_doc_language_code};
254             }
255              
256             sub language {
257 64     64 1 120 my $self = shift;
258 64 50       245 unless (defined $self->{_doc_language}) {
259 64         138 my $lc = $self->language_code;
260             # guaranteed not to return undef
261 64         149 $self->{_doc_language} = $self->_language_mapping->{$lc};
262             }
263 64         1302 return $self->{_doc_language};
264             }
265              
266             sub other_language_codes {
267 161     161 1 225 my $self = shift;
268 161         216 my @out = @{ $self->{_other_doc_language_codes} };
  161         418  
269 161 100       892 return @out ? \@out : undef;
270             }
271              
272             sub other_languages {
273 34     34 1 54 my $self = shift;
274 34         110 my $map = $self->_language_mapping;
275 34 100       123 my @out = map { $map->{$_} } @{ $self->other_language_codes || [] };
  5         12  
  34         76  
276 34 100       714 return @out ? \@out : undef;
277             }
278              
279             sub _add_to_other_language_codes {
280 62     62   131 my ($self, $lang) = @_;
281 62 50       150 return unless $lang;
282 62         135 $lang = lc($lang);
283 62 100       137 if ($self->_language_mapping->{$lang}) {
284 61 100       187 if ($lang ne $self->language_code) {
285 60 100       92 unless (grep { $_ eq $lang } @{ $self->other_language_codes || [] }) {
  114 100       298  
  60         132  
286 11         24 push @{$self->{_other_doc_language_codes}}, $lang;
  11         32  
287 11         25 return $lang;
288             }
289             }
290             }
291             else {
292 1         127 warn "Unknown language $lang";
293             }
294 51         1410 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 32 my $self = shift;
305 19         44 my ($directives, $body) = $self->_parse_body_and_directives(directives_only => 1);
306 19         83 return $directives;
307             }
308              
309              
310             sub _parse_body_and_directives {
311 674     674   1640 my ($self, %options) = @_;
312 674         1966 my $file = $self->filename;
313 674 50   21   27086 open (my $fh, "<:encoding(UTF-8)", $file) or die "Couldn't open $file! $!\n";
  21         165  
  21         35  
  21         306  
314              
315 674         247436 my $in_meta = 1;
316 674         2117 my ($lastdirective, %directives, @body);
317 674         0 my @directives_array;
318             RAWLINE:
319 674         28528 while (my $line = <$fh>) {
320             # EOL
321 19871         38935 $line =~ s/\r\n/\n/gs;
322 19871         24024 $line =~ s/\r/\n/gs;
323             # TAB
324 19871         23715 $line =~ s/\t/ /g;
325             # trailing
326 19871         36725 $line =~ s/ +$//mg;
327              
328 19871 100       27918 if ($in_meta) {
329             # reset the directives on blank lines
330 2234 100       10226 if ($line =~ m/^\s*$/s) {
    100          
    100          
331 632         862 $lastdirective = undef;
332             } elsif ($line =~ m/^\#([A-Za-z0-9_-]+)(\s+(.+))?$/s) {
333 842         2873 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         2006 $dir =~ s/[_-]//g;
341 842 100       2120 unless (length($dir)) {
342 24         1347 warn "$file: Found empty directive $line, it will be removed\n";
343             }
344 842 100       1895 if (exists $directives{$dir}) {
345 17         1003 warn "$file: Overwriting directive '$dir' $directives{$dir} with $line\n";
346             }
347 842 100       1634 if (defined $material) {
348 783         2087 $directives{$dir} = $material;
349             }
350             else {
351 59         152 $directives{$dir} = '';
352             }
353 842         2392 push @directives_array, [ $dir, $directives{$dir} ];
354              
355 842         1901 $lastdirective = $dir;
356             } elsif ($lastdirective) {
357 86         219 $directives{$lastdirective} .= $line;
358 86         146 $directives_array[-1][1] .= $line;
359             } else {
360 674         1072 $in_meta = 0
361             }
362             }
363 19871 100       28738 if ($in_meta) {
    100          
364 1560         4197 next RAWLINE;
365             }
366             elsif ($options{directives_only}) {
367 19         31 last RAWLINE;
368             }
369             else {
370 18292         48603 push @body, $line;
371             }
372             }
373 674         2402 push @body, "\n"; # append a newline
374 674         9041 close $fh;
375              
376             # before returning, let's clean the %directives from EOLs and from
377             # empty ones, e.g. #---------------------
378 674         2001 delete $directives{''};
379              
380 674         2868 foreach my $key (keys %directives) {
381 813         4405 $directives{$key} =~ s/\s+/ /gs;
382 813         3214 $directives{$key} =~ s/\s+\z//gs;
383 813         2104 $directives{$key} =~ s/\A\s+//gs;
384             }
385 674         4615 return (\%directives, \@body, \@directives_array);
386             }
387              
388             sub _split_body_and_directives {
389 655     655   987 my $self = shift;
390 655         2058 my ($directives, $body, $dir_array) = $self->_parse_body_and_directives;
391              
392 655 100       2879 if (my @include_paths = $self->include_paths) {
393             # rescan the body and do the inclusion
394 4         6 my @full_body;
395             LINE:
396 4         7 foreach my $l (@$body) {
397 38 100       103 if ($l =~ m/^#include\s+(.+?)\s*$/) {
398 11 100       26 if (my $lines = $self->_resolve_include($1, \@include_paths)) {
399 2         5 push @full_body, @$lines;
400 2         5 next LINE;
401             }
402             }
403 36         65 push @full_body, $l;
404             }
405 4         11 $body = \@full_body;
406             }
407 655         1711 $self->{raw_body} = $body;
408 655         1466 $self->{raw_header} = $directives;
409 655         1693 $self->{directives_array} = $dir_array;
410             }
411              
412             sub _resolve_include {
413 11     11   31 my ($self, $filename, $include_paths) = @_;
414 11         99 my ($volume, $directories, $file) = File::Spec->splitpath($filename);
415 11         68 my @dirs = grep { length $_ } File::Spec->splitdir($directories);
  71         110  
416             # if hidden files or traversals are passed, bail out.
417 11 100       25 if (grep { /^\./ } @dirs, $file) {
  66         128  
418 6         133 warn "Directory traversal or hidden file found in included $filename!";
419 6         39 return;
420             }
421             # if we have slash (unix) or backslash (windows), it's not good
422 5 100       10 if (grep { /[\/\\]/ } @dirs, $file) {
  8         19  
423 2         15 warn "Invalid file or directory name (slashes?) found in included $filename!";
424 2         11 return;
425             }
426             # just in case
427 3 100       8 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         2 my @out;
432              
433             INCLUDEFILE:
434 2         5 foreach my $base (@$include_paths) {
435 2         17 my $final = File::Spec->catfile($base, @dirs, $file);
436 2 50 33     152 if (-e $final && -T $final) {
437 2 50       61 open (my $fh, "<:encoding(UTF-8)", $final) or die "Couldn't open $final! $!\n";
438 2         154 while (my $line = <$fh>) {
439 6         27 $line =~ s/\r\n/\n/gs;
440 6         11 $line =~ s/\r/\n/gs;
441             # TAB
442 6         9 $line =~ s/\t/ /g;
443             # trailing
444 6         12 $line =~ s/ +$//mg;
445 6         29 push @out, $line;
446             }
447 2         21 close $fh;
448 2         9 $self->_add_to_included_files($final);
449 2         6 last INCLUDEFILE;
450             }
451             }
452 2 50       6 if (@out) {
453 2         8 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 1347 my $self = shift;
469 794 100       2165 unless (defined $self->{raw_header}) {
470 636         1899 $self->_split_body_and_directives;
471             }
472 794         1178 return %{$self->{raw_header}}
  794         3619  
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 977 my $self = shift;
489 638 100       1732 unless (defined $self->{raw_body}) {
490 17         50 $self->_split_body_and_directives;
491             }
492 638         883 return @{$self->{raw_body}}
  638         4825  
493             }
494              
495             sub directives_array {
496 2     2 1 4 my $self = shift;
497 2 50       8 unless (defined $self->{directives_array}) {
498 2         3 $self->_split_body_and_directives;
499             }
500 2         3 return @{$self->{directives_array}}
  2         6  
501             }
502              
503             sub _parse_body {
504 627     627   1146 my $self = shift;
505 627         2573 $self->_debug("Parsing body");
506              
507             # be sure to start with a null block and reset the state
508 627         2524 my @parsed = ($self->_construct_element(""));
509 627         1823 $self->_current_el(undef);
510              
511 627         1826 foreach my $l ($self->raw_body) {
512             # if doesn't return anything, the thing got merged
513 18793 100       30214 if (my $el = $self->_construct_element($l)) {
514 12532         25453 push @parsed, $el;
515             }
516             }
517 627         7595 $self->_debug(Dumper(\@parsed));
518              
519             # turn the versep into verse now that the merging is done
520 627         13189 foreach my $el (@parsed) {
521 13159 100       20783 if ($el->type eq 'versep') {
522 63         192 $el->type('verse');
523             }
524             }
525             # turn the direction switching into proper open/close blocks
526             {
527 627         1419 my $current_direction = '';
  627         1415  
528 627         3134 my %dirs = (
529             '<<<' => 'rtl',
530             '>>>' => 'ltr',
531             );
532 627         1417 foreach my $el (@parsed) {
533 13159 100       19716 if ($el->type eq 'bidimarker') {
534 45         120 $self->set_bidi_document;
535 45 50       95 my $dir = $dirs{$el->block} or die "Invalid bidimarker " . $el->block;
536 45 100 100     170 if ($current_direction and $current_direction ne $dir) {
537 15         47 $el->type('stopblock');
538 15         37 $el->block($current_direction);
539 15         27 $current_direction = '';
540             }
541             else {
542 30 100       777 warn "Direction already set to $current_direction!" if $current_direction;
543 30         102 $el->type('startblock');
544 30         71 $el->block($dir);
545 30         56 $current_direction = $dir;
546             }
547             }
548             }
549             }
550 627         2621 $self->_reset_list_parsing_output;
551             LISTP:
552 627         1865 while (@parsed) {
553 13159         17289 my $el = shift @parsed;
554 13159 100 100     22088 if ($el->type eq 'li' or $el->type eq 'dd') {
    100          
    100          
555 1438 100       2485 if ($self->_list_pile_count) {
    100          
556             # indentation is major, open a new level
557 1101 100       1901 if (_indentation_kinda_major($el, $self->_list_pile_last_element)) {
558 208         433 $self->_list_open_new_list_level($el);
559             }
560             else {
561             # close the lists until we get the right level
562 893         1821 $self->_list_close_until_indentation($el);
563 893 50       1803 if ($self->_list_pile_count) { # continue if open
564 893 100 100     1492 if ($self->_list_element_is_same_kind_as_in_list($el) and
565             $self->_list_element_is_a_progression($el)) {
566 779         1386 $self->_list_continuation($el);
567             }
568             else {
569 114         240 my $top = $self->_list_pile_last_element;
570 114   100     246 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         485 $self->_close_list_level;
574             }
575             # and open a new level
576 114         285 $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         822 $self->_list_open_new_list_level($el);
587             }
588             else {
589             # reparse and should become quote/center/right
590 22         65 $self->_append_element_to_list_parsing_output($self->_reparse_nolist($el));
591 22         152 next LISTP; # call next to avoid being mangled.
592             }
593 1416         3463 $el->become_regular;
594             }
595             elsif ($el->type eq 'regular') {
596             # the type is regular: It can only close or continue
597 3020         5837 $self->_list_close_until_indentation($el);
598 3020 100       4899 if ($self->_list_pile_count) {
599 185         359 $el->become_regular;
600             }
601             }
602             elsif ($el->type ne 'null') { # something else: close the pile
603 2117         3320 $self->_list_flush;
604             }
605 13137         21273 $self->_append_element_to_list_parsing_output($el);
606             }
607             # end of input, flush what we have.
608 627         1885 $self->_list_flush;
609              
610             # now we use parsed as output
611 627         1953 $self->_flush_current_footnote;
612 627         835 my @out;
613 627         999 my $elnum = 0;
614 627         980 while (@{$self->_list_parsing_output}) {
  18276         23217  
615 17649         18821 my $el = shift @{$self->_list_parsing_output};
  17649         20977  
616 17649         19418 $elnum++;
617 17649         33361 $el->_set_element_number($elnum);
618 17649 100 100     26765 if ($el->type eq 'footnote' or $el->type eq 'secondary_footnote') {
    100          
619 556         1055 $self->_register_footnote($el);
620             }
621             elsif (my $fn_indent = $self->_current_footnote_indent) {
622 883 100 66     1551 if ($el->type eq 'null') {
    100 100        
623 644         1033 push @parsed, $el;
624             }
625             elsif ($el->can_be_regular and
626             $el->indentation and
627             _kinda_equal($el->indentation, $fn_indent)) {
628 98         127 push @{$self->_current_footnote_stack}, Text::Amuse::Element->new($self->_parse_string("
\n")), $el;
  98         177  
629             }
630             else {
631 141         342 $self->_flush_current_footnote;
632 141         314 push @parsed, $el;
633             }
634             }
635             else {
636 16210         23411 push @parsed, $el;
637             }
638             }
639 627         1836 $self->_flush_current_footnote;
640              
641             # unroll the quote/center/right blocks
642 627         1802 while (@parsed) {
643 16995         21145 my $el = shift @parsed;
644 16995 100       27128 if ($el->can_be_regular) {
645 172         412 my $open = $self->_create_block(open => $el->block, $el->indentation);
646 172         469 my $close = $self->_create_block(close => $el->block, $el->indentation);
647 172         490 $el->block("");
648 172         428 push @out, $open, $el, $close;
649             }
650             else {
651 16823         27662 push @out, $el;
652             }
653             }
654              
655 627         1127 my @pile;
656 627         1545 while (@out) {
657 17339         22282 my $el = shift @out;
658 17339 100 100     27875 if ($el->type eq 'startblock') {
    100          
    100          
659 2442         4240 push @pile, $self->_create_block(close => $el->block, $el->indentation);
660 2442         5196 $self->_debug("Pushing " . $el->block);
661 2442 50       4770 die "Uh?\n" unless $el->block;
662             }
663             elsif ($el->type eq 'stopblock') {
664 2441         3144 my $exp = pop @pile;
665 2441 100 66     5248 unless ($exp and $exp->block eq $el->block) {
666 25         58 warn "Couldn't retrieve " . $el->block . " from the pile\n";
667             # put it back
668 25 50       143 push @pile, $exp if $exp;
669             # so what to do here? just removed it
670 25         214 next;
671             }
672             }
673             elsif (@pile and $el->should_close_blocks) {
674              
675 31         64 my @carry_on;
676 31         94 my %close_rtl = map { $_ => 1 } (qw/h1 h2 h3 h4 h5 h6 newpage/);
  217         409  
677              
678 31         107 while (@pile) {
679 31         62 my $block = pop @pile;
680 31 100 100     74 if (($block->block eq 'rtl' || $block->block eq 'ltr') and !$close_rtl{$el->type}) {
      100        
681 15         58 push @carry_on, $block;
682             }
683             else {
684 16         46 warn "Forcing the closing of " . $block->block . "\n";
685 16         126 push @parsed, $block;
686             }
687             }
688 31         110 push @pile, reverse @carry_on;
689             }
690 17314         30542 push @parsed, $el;
691             }
692             # do we still have things into the pile?
693 627         1852 while (@pile) {
694 10         26 push @parsed, pop @pile;
695             }
696 627         2047 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 1663 my $self = shift;
709 1008 100       2651 unless (defined $self->{_parsed_document}) {
710 627         1895 $self->{_parsed_document} = $self->_parse_body;
711             }
712 1008 50       2526 if (defined wantarray) {
713 1008         1372 return @{$self->{_parsed_document}};
  1008         7322  
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 3233 my ($self, $arg) = @_;
729 1905 100       3554 return undef unless $arg;
730 1904 50       9633 if ($arg =~ m/(\{[1-9][0-9]*\}|\[[1-9][0-9]*\])/) {
731 1904         4599 $arg = $1;
732             }
733             else {
734 0         0 return undef;
735             }
736 1904 100       3677 if (exists $self->_raw_footnotes->{$arg}) {
737 1628         2414 return $self->_raw_footnotes->{$arg};
738             }
739 276         939 else { return undef }
740             }
741              
742             sub _raw_footnotes {
743 4644     4644   5342 my $self = shift;
744 4644         13237 return $self->{_raw_footnotes};
745             }
746              
747             sub _current_footnote_stack {
748 2494     2494   5595 return shift->{_current_footnote_stack};
749             }
750              
751             sub _current_footnote_number {
752 2560     2560   3105 my $self = shift;
753 2560 100       4376 if (@_) {
754 2507         3378 $self->{_current_footnote_number} = shift;
755             }
756 2560         3632 return $self->{_current_footnote_number};
757             }
758              
759             sub _current_footnote_indent {
760 19600     19600   22408 my $self = shift;
761 19600 100       28252 if (@_) {
762 2507         3278 $self->{_current_footnote_indent} = shift;
763             }
764 19600         28327 return $self->{_current_footnote_indent};
765             }
766              
767              
768              
769             sub _parse_string {
770 19600     19600   30812 my ($self, $l, %opts) = @_;
771 19600 50       32267 die unless defined $l;
772 19600         41248 my %element = (
773             rawline => $l,
774             raw_without_anchors => $l,
775             );
776 19600 100       46352 if ($l =~ m/\A
777             (\s*)
778             (\#([A-Za-z][A-Za-z0-9-]+)\x{20}*)
779             (.*)
780             \z
781             /sx) {
782 420         1202 $element{anchor} = $3;
783 420         1069 $l = $1 . $4;
784 420         703 $element{raw_without_anchors} = $l;
785             }
786 19600         48309 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       61021 if ($l =~ m/^[\n\t ]*$/s) {
799             # do nothing, already default
800 7972         13179 $element{removed} = $l;
801 7972         34424 return %element;
802             }
803 11628 100       49491 if ($l =~ m!^(<($blockre)>\s*)$!s) {
804 394         872 $element{type} = "startblock";
805 394         1033 $element{removed} = $1;
806 394         795 $element{block} = $2;
807 394         2226 return %element;
808             }
809 11234 100       25728 if ($l =~ m/^((\<\<\<|\>\>\>)\s*)$/s) {
810             # here turn them into language switch
811 45         101 $element{type} = "bidimarker";
812 45         121 $element{removed} = $1;
813 45         91 $element{block} = $2;
814 45         271 return %element;
815             }
816 11189 100       19780 if ($l =~ m/^(
817             (
818             \<
819             (\/?)
820             \[
821             ([a-zA-Z-]+)
822             \]
823             \>
824             )
825             \s*
826             )$/sx) {
827 15         73 my ($all, $full, $close, $lang) = ($1, $2, $3, $4);
828 15 100       46 $element{type} = $close ? "stopblock" : "startblock";
829 15         32 $element{language} = $lang;
830 15         31 $element{removed} = $l;
831 15         46 $self->_add_to_other_language_codes($lang);
832 15         260 $element{block} = "languageswitch";
833 15         110 return %element;
834             }
835 11174 100       19333 if ($l =~ m/^(\{\{\{)\s*$/s) {
836 75         233 $element{type} = "startblock";
837 75         141 $element{removed} = $l;
838 75         148 $element{block} = 'example';
839 75         129 $element{style} = '{{{}}}';
840 75         487 return %element;
841             }
842 11099 100       17547 if ($l =~ m/^(\}\}\})\s*$/s) {
843 75         216 $element{type} = "stopblock";
844 75         146 $element{removed} = $l;
845 75         138 $element{block} = 'example';
846 75         115 $element{style} = '{{{}}}';
847 75         400 return %element;
848             }
849 11024 100       34191 if ($l =~ m!^(\s*)$!s) {
850 369         985 $element{type} = "stopblock";
851 369         908 $element{removed} = $1;
852 369         674 $element{block} = $2;
853 369         2009 return %element;
854             }
855             # headers
856 10655 100       21026 if ($l =~ m!^((\*{1,5}) )(.+)$!s) {
857 787         2949 $element{type} = "h" . length($2);
858 787         1823 $element{removed} = $1;
859 787         1501 $element{string} = $3;
860 787         4656 return %element;
861             }
862 9868 100       16095 if ($l =~ m/^(\> )(.*)/s) {
863 182         551 $element{string} = $2;
864 182         346 $element{removed} = $1;
865 182         316 $element{type} = "versep";
866 182         1047 return %element;
867             }
868 9686 100       15101 if ($l =~ m/^(\>)$/s) {
869 35         82 $element{string} = "\n";
870 35         72 $element{removed} = ">";
871 35         70 $element{type} = "versep";
872 35         188 return %element;
873             }
874 9651 100 100     28400 if ($l =~ m/^(\x{20}+)/s and $l =~ m/\|/) {
875 544         1121 $element{type} = "table";
876 544         839 $element{string} = $l;
877 544         2624 return %element;
878             }
879             # line starting with pipe, gh-markdown style
880 9107 100       15480 if ($l =~ m/^\|+(\x{20}+|\+)/s) {
881 30         51 $element{type} = "table";
882 30         47 $element{string} = $l;
883 30         145 return %element;
884             }
885 9077 100       15468 if ($l =~ m/^(\;)(\x{20}+(.*))?$/s) {
886 92         298 $element{removed} = $1;
887 92         221 $element{string} = $3;
888 92 100       226 unless (defined ($element{string})) {
889 12         19 $element{string} = '';
890             }
891 92         146 $element{type} = "inlinecomment";
892 92         546 return %element;
893             }
894 8985 100       16640 if ($l =~ m/^((\[([1-9][0-9]*)\])\x{20}+)(.+)$/s) {
895 456         903 $element{type} = "footnote";
896 456         1158 $element{removed} = $1;
897 456         936 $element{footnote_symbol} = $2;
898 456         825 $element{footnote_number} = $3;
899 456         782 $element{footnote_index} = $3;
900 456         1015 $element{string} = $4;
901 456         3563 return %element;
902             }
903 8529 100       14771 if ($l =~ m/^((\{([1-9][0-9]*)\})\x{20}+)(.+)$/s) {
904 137         314 $element{type} = "secondary_footnote";
905 137         341 $element{removed} = $1;
906 137         237 $element{footnote_symbol} = $2;
907 137         255 $element{footnote_number} = $3;
908 137         341 $element{footnote_index} = 'b'. $3;
909 137         305 $element{string} = $4;
910 137         1069 return %element;
911             }
912 8392 100       14951 if ($l =~ m/^((\x{20}{6,})((\*\x{20}?){5})\s*)$/s) {
913 44         85 $element{type} = "newpage";
914 44         116 $element{removed} = $2;
915 44         85 $element{string} = $3;
916 44         241 return %element;
917             }
918 8348 100       14844 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         353 $element{block} = 'dl';
927 150         265 $element{type} = 'dd';
928 150         427 $element{string} = $7;
929 150         295 $element{attribute} = $2;
930 150         251 $element{attribute_type} = 'dt';
931 150         642 $element{removed} = $1 . $2 . $3 . $4 . $6;
932 150         357 $element{indentation} = length($1) + 2;
933 150         267 $element{start_list_index} = 1;
934 150         1253 return %element;
935             }
936 8198 100       14702 if (!$opts{nolist}) {
937 8176 100       15686 if ($l =~ m/^((\x{20}+)\-\x{20}+)(.*)/s) {
938 481         915 $element{type} = "li";
939 481         1114 $element{removed} = $1;
940 481         987 $element{string} = $3;
941 481         833 $element{block} = "ul";
942 481         1028 $element{indentation} = length($2) + 2;
943 481         712 $element{start_list_index} = 1;
944 481         3357 return %element;
945             }
946 7695 100       15897 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         4024 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       2200 if (my $list_index = $self->_get_start_list_index($prefix)) {
961 1059         2026 $element{type} = "li";
962 1059         1617 $element{removed} = $remove;
963 1059         1634 $element{string} = $text;
964 1059         1963 my $list_type = $self->_identify_list_type($prefix);
965 1059         2702 $element{indentation} = length($whitespace) + 2;
966 1059         1588 $element{block} = $list_type;
967 1059         1474 $element{start_list_index} = $list_index;
968 1059         7739 return %element;
969             }
970             }
971             }
972 6658 100       12113 if ($l =~ m/^(\x{20}{20,})([^ ].+)$/s) {
973 65         181 $element{block} = "right";
974 65         121 $element{type} = "regular";
975 65         198 $element{removed} = $1;
976 65         142 $element{string} = $2;
977 65         452 return %element;
978             }
979 6593 100       11416 if ($l =~ m/^(\x{20}{6,})([^ ].+)$/s) {
980 487         919 $element{block} = "center";
981 487         799 $element{type} = "regular";
982 487         1162 $element{removed} = $1;
983 487         911 $element{string} = $2;
984 487         3010 return %element;
985             }
986 6106 100       11273 if ($l =~ m/^(\x{20}{2,})([^ ].+)$/s) {
987 966         1715 $element{block} = "quote";
988 966         1606 $element{type} = "regular";
989 966         2425 $element{removed} = $1;
990 966         1888 $element{string} = $2;
991 966         5897 return %element;
992             }
993             # anything else is regular
994 5140         8198 $element{type} = "regular";
995 5140         7388 $element{string} = $l;
996 5140         24148 return %element;
997             }
998              
999              
1000             sub _identify_list_type {
1001 1059     1059   1919 my ($self, $list_type) = @_;
1002 1059         1261 my $type;
1003 1059 100       5104 if ($list_type =~ m/\A[0-9]+\z/) {
    100          
    100          
    100          
    50          
1004 390         614 $type = "oln";
1005             } elsif ($list_type =~ m/\A[ixvl]+\z/) {
1006 166         294 $type = "oli";
1007             } elsif ($list_type =~ m/\A[IXVL]+\z/) {
1008 77         152 $type = "olI";
1009             } elsif ($list_type =~ m/\A[a-z]\z/) {
1010 273         433 $type = "ola";
1011             } elsif ($list_type =~ m/\A[A-Z]\z/) {
1012 153         267 $type = "olA";
1013             } else {
1014 0         0 die "$list_type unrecognized, fix your code\n";
1015             }
1016 1059         1601 return $type;
1017             }
1018              
1019             sub _get_start_list_index {
1020 1064     1064   1819 my ($self, $prefix) = @_;
1021 1064         2383 my $map = $self->_list_index_map;
1022 1064 100       2989 if (exists $map->{$prefix}) {
1023 1062         2955 return $map->{$prefix};
1024             }
1025             else {
1026 2         153 warn "$prefix doesn't map exactly to a list index!\n";
1027 2         17 return 0;
1028             }
1029             }
1030              
1031             sub _list_element_can_be_first {
1032 337     337   663 my ($self, $el) = @_;
1033             # every dd can be the first
1034 337 100       830 return 1 if $el->type eq 'dd';
1035 297 50       614 return unless $el->type eq 'li';
1036             # first element, can't be too indented
1037 297 100       706 if ($el->indentation > 8) {
1038 22         53 return 0;
1039             }
1040             else {
1041 275         650 return $el->start_list_index;
1042             }
1043             }
1044              
1045             sub _current_el {
1046 33318     33318   37954 my $self = shift;
1047 33318 100       52162 if (@_) {
1048 13842         18476 $self->{_current_el} = shift;
1049             }
1050 33318         44164 return $self->{_current_el};
1051             }
1052              
1053             sub _reparse_nolist {
1054 22     22   37 my ($self, $element) = @_;
1055 22         73 my %args = $self->_parse_string($element->rawline, nolist => 1);
1056 22         98 my $el = Text::Amuse::Element->new(%args);
1057 22 50       70 if ($el->type eq 'regular') {
1058 22         77 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   33424 my ($self, $line) = @_;
1067 19476         28461 my $current = $self->_current_el;
1068 19476         35406 my %args = $self->_parse_string($line);
1069 19476         63796 my $element = Text::Amuse::Element->new(%args);
1070              
1071 19476 100 100     54119 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         473 $current->move_anchors_to($element);
1074             }
1075 19476 100 100     34122 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         359 $element->move_anchors_to($current);
1081             # null element with anchors. it was fully merged, so return
1082 108         670 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         29051 foreach my $block (qw/example comment verse/) {
1090 53597 100 100     105533 if ($current && $current->type eq $block) {
    100          
1091 2336 100       4394 if ($element->is_stop_element($current)) {
1092 316         790 $self->_current_el(undef);
1093 316         836 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     3465 if ($current->type eq 'verse' and
1101             $element->type eq 'inlinecomment') {
1102             }
1103             else {
1104 2014         3685 $current->append($element);
1105             }
1106 2020         10024 return;
1107             }
1108             }
1109             elsif ($element->is_start_block($block)) {
1110 352         1043 $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         1085 $self->_current_el($current);
1117 352         1987 return $current;
1118             }
1119             }
1120             # Pack the lines
1121 16680 100 100     38865 if ($current && $current->can_append($element)) {
1122             # print "Packing " . Dumper($element) . ' into ' . Dumper($current);
1123 4133         9483 $current->append($element);
1124 4133         21939 return;
1125             }
1126              
1127 12547         26541 $self->_current_el($element);
1128 12547         33536 return $element;
1129             }
1130              
1131             sub _create_block {
1132 7148     7148   11006 my ($self, $open_close, $block, $indentation) = @_;
1133 7148 50 33     19118 die unless $open_close && $block;
1134 7148         8115 my $type;
1135 7148 100       12032 if ($open_close eq 'open') {
    50          
1136 2353         2770 $type = 'startblock';
1137             }
1138             elsif ($open_close eq 'close') {
1139 4795         5642 $type = 'stopblock';
1140             }
1141             else {
1142 0         0 die "$open_close is not a valid op";
1143             }
1144 7148         8059 my $removed = '';
1145 7148 50       10057 if ($indentation) {
1146 7148         11359 $removed = ' ' x $indentation;
1147             }
1148 7148         14210 return Text::Amuse::Element->new(block => $block,
1149             type => $type,
1150             removed => $removed);
1151             }
1152              
1153             sub _opening_blocks {
1154 1416     1416   2356 my ($self, $el) = @_;
1155 1416         1875 my @out;
1156 1416 100 66     2668 if ($el->attribute && $el->attribute_type) {
1157 128         304 @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         3156 push @out, $self->_create_block(open => $el->type, $el->indentation);
1162 1416         3180 return @out;
1163             }
1164              
1165             sub _closing_blocks {
1166 1416     1416   2243 my ($self, $el) = @_;
1167 1416         2661 my @out = ($self->_create_block(close => $el->type, $el->indentation));
1168 1416         2442 return @out;
1169             }
1170             sub _opening_blocks_new_level {
1171 637     637   1100 my ($self, $el) = @_;
1172 637         1374 my @out = ($self->_create_block(open => $el->block, $el->indentation),
1173             $self->_opening_blocks($el));
1174 637 50       1465 if (my $list_index = $el->start_list_index) {
1175 637         1557 $out[0]->start_list_index($list_index);
1176 637         1206 $out[1]->start_list_index($list_index);
1177             }
1178 637         1348 return @out;
1179             }
1180             sub _closing_blocks_new_level {
1181 637     637   945 my ($self, $el) = @_;
1182 637         1289 my @out = ($self->_create_block(close => $el->block, $el->indentation),
1183             $self->_closing_blocks($el));
1184 637         1103 return @out;
1185             }
1186              
1187             sub _indentation_kinda_minor {
1188 1722     1722   2556 return _indentation_compare(@_) == IMINOR;
1189             }
1190              
1191             sub _indentation_kinda_major {
1192 1101     1101   1858 return _indentation_compare(@_) == IMAJOR;
1193             }
1194              
1195             sub _indentation_kinda_equal {
1196 271     271   428 return _indentation_compare(@_) == IEQUAL;
1197             }
1198              
1199             sub _kinda_equal {
1200 106     106   202 return _compare_tolerant(@_) == IEQUAL;
1201             }
1202              
1203             sub _indentation_compare {
1204 3094     3094   3920 my ($first, $second) = @_;
1205 3094         5278 my $one_indent = $first->indentation;
1206 3094         4823 my $two_indent = $second->indentation;
1207 3094         4293 return _compare_tolerant($one_indent, $two_indent);
1208             }
1209              
1210             sub _compare_tolerant {
1211 3212     3212   4243 my ($one_indent, $two_indent) = @_;
1212             # tolerance is zero if one of them is 0
1213 3212         3453 my $tolerance = 0;
1214 3212 100 100     7496 if ($one_indent && $two_indent) {
1215 2865         3251 $tolerance = 1;
1216             }
1217 3212         3713 my $diff = $one_indent - $two_indent;
1218 3212 100       5800 if ($diff - $tolerance > 0) {
    100          
1219 282         824 return IMAJOR;
1220             }
1221             elsif ($diff + $tolerance < 0) {
1222 753         2264 return IMINOR;
1223             }
1224             else {
1225 2177         6530 return IEQUAL;
1226             }
1227             }
1228              
1229              
1230             sub _list_element_is_same_kind_as_in_list {
1231 893     893   1286 my ($self, $el) = @_;
1232 893         1187 my $list = $self->_list_element_pile;
1233 893         1646 my $find = $el->block;
1234 893         1291 my $found = 0;
1235 893         2164 for (my $i = $#$list; $i >= 0; $i--) {
1236 1786         3110 my $block = $list->[$i]->block;
1237 1786 100 100     4885 next if ($block eq 'li' or $block eq 'dd');
1238 893 100       1604 if ($block eq $find) {
1239 823         1054 $found = 1;
1240             }
1241 893         1107 last;
1242             }
1243 893         2348 return $found;
1244             }
1245              
1246             sub _register_footnote {
1247 556     556   877 my ($self, $el) = @_;
1248 556         1031 my $fn_num = $el->footnote_symbol;
1249 556 50       1056 if (defined $fn_num) {
1250 556 50       972 if ($self->_raw_footnotes->{$fn_num}) {
1251 0         0 warn "Overwriting footnote number $fn_num!\n";
1252             }
1253 556         1140 $self->_flush_current_footnote;
1254 556         1217 $self->_current_footnote_indent($el->indentation);
1255 556         1056 $self->_current_footnote_number($fn_num);
1256 556         889 $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   2498 my $self = shift;
1266 1951 100       2210 if (@{$self->_current_footnote_stack}) {
  1951         3794  
1267 53         105 my $footnote = $self->get_footnote($self->_current_footnote_number);
1268 53 50       131 die "Missing current footnote to append " . Dumper($self->_current_footnote_stack) unless $footnote;
1269 53         83 while (@{$self->_current_footnote_stack}) {
  249         400  
1270 196         240 my $append = shift @{$self->_current_footnote_stack};
  196         261  
1271 196         358 $footnote->append($append);
1272             }
1273             }
1274 1951         4447 $self->_current_footnote_indent(0);
1275 1951         3679 $self->_current_footnote_number(undef)
1276             }
1277              
1278             # list parsing
1279              
1280             sub _list_element_pile {
1281 22568     22568   44711 return shift->{_list_element_pile};
1282             }
1283              
1284             sub _list_parsing_output {
1285 51774     51774   97429 return shift->{_list_parsing_output};
1286             }
1287              
1288             sub _list_pile_count {
1289 13396     13396   14931 my $self = shift;
1290 13396         14051 return scalar(@{$self->_list_element_pile});
  13396         16783  
1291             }
1292              
1293             sub _list_pile_last_element {
1294 4810     4810   5608 my $self = shift;
1295 4810         5959 return $self->_list_element_pile->[-1];
1296             }
1297              
1298             sub _reset_list_parsing_output {
1299 627     627   1309 my $self = shift;
1300 627         1774 $self->{_list_parsing_output} = [];
1301             }
1302              
1303             sub _list_open_new_list_level {
1304 637     637   1013 my ($self, $el) = @_;
1305 637         865 push @{$self->_list_parsing_output}, $self->_opening_blocks_new_level($el);
  637         966  
1306 637         1506 my @pile = $self->_closing_blocks_new_level($el);
1307 637 50       1237 if (my $list_index = $el->start_list_index) {
1308 637         1525 $_->start_list_index($list_index) for @pile;
1309             }
1310 637         875 push @{$self->_list_element_pile}, @pile;
  637         1166  
1311             }
1312              
1313             sub _list_continuation {
1314 779     779   1179 my ($self, $el) = @_;
1315 779         1230 my $current = $self->_list_pile_last_element->start_list_index + 1;
1316 779         971 push @{$self->_list_parsing_output}, pop @{$self->_list_element_pile}, $self->_opening_blocks($el);
  779         1152  
  779         1183  
1317 779         1720 my @pile = $self->_closing_blocks($el);
1318 779 50       1560 if (my $list_index = $el->start_list_index) {
1319 779         1784 $_->start_list_index($current) for @pile;
1320             }
1321 779         1035 push @{$self->_list_element_pile}, @pile;
  779         1311  
1322             }
1323              
1324             sub _close_list_level {
1325 1274     1274   1690 my $self = shift;
1326 1274         1366 push @{$self->_list_parsing_output}, pop @{$self->_list_element_pile};
  1274         1665  
  1274         1660  
1327             }
1328              
1329             sub _append_element_to_list_parsing_output {
1330 13159     13159   17525 my ($self, $el) = @_;
1331 13159         13798 push @{$self->_list_parsing_output}, $el;
  13159         16860  
1332             }
1333              
1334             sub _list_close_until_indentation {
1335 3913     3913   5659 my ($self, $el) = @_;
1336 3913   100     6487 while ($self->_list_pile_count and
1337             _indentation_kinda_minor($el, $self->_list_pile_last_element)) {
1338 644         1259 $self->_close_list_level;
1339             }
1340             }
1341              
1342             sub _list_flush {
1343 2744     2744   3342 my $self = shift;
1344 2744         4279 while ($self->_list_pile_count) {
1345 402         674 $self->_close_list_level;
1346             }
1347             }
1348              
1349             sub _list_element_is_a_progression {
1350 823     823   1313 my ($self, $el) = @_;
1351             # not defined, not needed.
1352 823         1265 my $last = $self->_list_pile_last_element->start_list_index;
1353 823         1467 my $current = $el->start_list_index;
1354             # no index from one or another, we can't compare
1355 823 50 33     3840 if (!$last or !$current) {
    100 66        
1356 0         0 return 1;
1357             }
1358             elsif ($last > 0 and $current > 1) {
1359 317 100       662 if (($current - $last) == 1) {
1360 273         637 return 1;
1361             }
1362             else {
1363 44         134 return 0;
1364             }
1365             }
1366             else {
1367 506         1234 return 1;
1368             }
1369             }
1370              
1371             =back
1372              
1373             =cut
1374              
1375             1;