File Coverage

blib/lib/Markdown/Compiler/Parser.pm
Criterion Covered Total %
statement 253 373 67.8
branch 96 172 55.8
condition 14 56 25.0
subroutine 20 25 80.0
pod 0 2 0.0
total 383 628 60.9


line stmt bran cond sub pod time code
1             package Markdown::Compiler::Parser;
2             BEGIN {
3             {
4             package Markdown::Compiler::Parser::Node;
5 18     18   114 use Moo;
  18         35  
  18         105  
6              
7 18         94 has tokens => (
8             is => 'ro',
9             required => 1,
10             );
11            
12 18         12802 has children => (
13             is => 'ro',
14             );
15              
16 18         3825 has content => (
17             is => 'ro',
18             );
19              
20 18         3888 1;
21             }
22              
23             {
24 18     18   5105 package Markdown::Compiler::Parser::Node::Metadata;
25 18     18   5412 use Moo;
  18         35  
  18         64  
26 18         106 extends 'Markdown::Compiler::Parser::Node';
27              
28 18         2287 has data => (
29             is => 'ro',
30             );
31              
32             # content => $content,
33             # tokens => [ @tree ],
34             # data => $struct,
35 18         10778 1;
36             }
37              
38             {
39 18         48 package Markdown::Compiler::Parser::Node::Metadata::Key;
40 18     18   5483 use Moo;
  18         51  
  18         82  
41 18         91 extends 'Markdown::Compiler::Parser::Node';
42              
43 18         2134 1;
44             }
45              
46             {
47 18         39 package Markdown::Compiler::Parser::Node::Metadata::Value;
  18         48  
48 18     18   5491 use Moo;
  18         41  
  18         82  
49 18         64 extends 'Markdown::Compiler::Parser::Node';
50              
51 18         2336 1;
52             }
53             }
54 18     18   118 use Moo;
  18         31  
  18         72  
55              
56             has stream => (
57             is => 'ro',
58             required => 1,
59             );
60              
61             has tree => (
62             is => 'ro',
63             lazy => 1,
64             builder => '_build_tree',
65             );
66              
67             has htree => (
68             is => 'ro',
69             lazy => 1,
70             builder => '_build_htree',
71             );
72              
73             has metadata => (
74             is => 'ro',
75             lazy => 1,
76             builder => '_build_metadata',
77             );
78              
79              
80             sub _build_tree {
81 64     64   21464 my ( $self ) = @_;
82              
83 64         113 my @tokens = @{$self->stream};
  64         219  
84              
85 64         223 return $self->make_hash($self->_parse(\@tokens));
86             }
87              
88             sub make_hash {
89 173     173 0 317 my ( $self, $tokens ) = @_;
90              
91 173         225 my @stream;
92              
93 173         210 foreach my $token ( @{$tokens} ) {
  173         287  
94              
95 421 50       705 if ( ref($token) eq 'HASH' ) {
96 421 100 66     811 if ( $token->{children} && @{$token->{children}} >= 1 ) {
  109         344  
97 109         412 $token->{children} = [ $self->make_hash( $token->{children} ) ];
98             }
99 421         757 push @stream, $token;
100             } else {
101 0 0 0     0 push @stream, {
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
    0 0        
102             class => ref($token),
103             tokens => [ $token->tokens ],
104             ( $token->can('data') && $token->data ? ( data => $token->data ) : () ),
105             ( $token->can('href') && $token->href ? ( href => $token->href ) : () ),
106             ( $token->can('title') && $token->title ? ( title => $token->title ) : () ),
107             ( $token->can('size') && $token->size ? ( size => $token->size ) : () ),
108             ( $token->can('text') && $token->text ? ( text => $token->text ) : () ),
109             ( $token->can('language') && $token->language ? ( language => $token->language ) : () ),
110             ( $token->can('content') && $token->content ? ( content => $token->content ) : () ),
111             ( $token->can('children') && $token->children ? ( children => [ $self->make_hash($token->children) ] ) : () ),
112             };
113             }
114             }
115              
116 173         1428 return [ @stream ];
117             }
118              
119             sub _build_metadata {
120 0     0   0 my ( $self ) = @_;
121              
122 0 0 0     0 if ( $self->tree->[0] and $self->tree->[0]->{class} eq 'Markdown::Compiler::Parser::Node::Metadata' ) {
123 0         0 return $self->tree->[0]->{data};
124             }
125 0         0 return undef;
126             }
127              
128             sub _parse {
129 64     64   144 my ( $self, $tokens ) = @_;
130 64         108 my @tree;
131              
132 64         96 while ( defined ( my $token = shift @{ $tokens } ) ) {
  140         328  
133             # Header
134 76 100       233 if ( $token->type eq 'Header' ) {
    100          
    50          
    100          
    100          
    100          
    100          
    50          
135 1         18 push @tree, {
136             class => 'Markdown::Compiler::Parser::Node::Header',
137             size => $token->size,
138             title => $token->title,
139             # tokens => [ $token ],
140             content => $token->content,
141             children => [ $self->_parse_paragraph(Markdown::Compiler->new( source => $token->title )->lexer->tokens) ],
142             };
143 1         6 next;
144             }
145              
146             # Paragraphs
147 675         1016 elsif ( grep { $token->type eq $_ } ( qw( EscapedChar Image Link Word Char Bold Italic BoldItalic InlineCode ) ) ) {
148 57         88 unshift @{$tokens}, $token; # Put the token back and go to paragraph context.
  57         100  
149 57         177 push @tree, {
150             class => 'Markdown::Compiler::Parser::Node::Paragraph',
151             children => [ $self->_parse_paragraph( $tokens ) ],
152             };
153              
154 57         117 next;
155             }
156            
157             # HR
158             elsif ( $token->type eq 'HR' ) {
159             # When is an HR not an HR? -- When it's actually the beginning
160             # of metadata. If this is the first token, then we are dealing
161             # with metadata, not an HR.
162 0 0       0 if ( $token->start == 0 ) {
163             push @tree, Markdown::Compiler::Parser::Node::Metadata->new(
164 0         0 %{ $self->_parse_metadata($tokens) },
  0         0  
165             );
166             # language => $token->language,
167             # tokens => [ $token ],
168             # children => [ $self->_parse_metadata( $tokens ) ],
169 0         0 next;
170             }
171              
172             # Otherwise, we just have a simple HR token.
173 0         0 push @tree, {
174             class => 'Markdown::Compiler::Parser::Node::HR',
175             # tokens => [ $token ],
176             };
177 0         0 next;
178             }
179              
180             # Tables
181             elsif ( $token->type eq 'TableStart' ) {
182 3         7 unshift @{$tokens}, $token; # Put the token back and go to table context.
  3         6  
183 3         12 push @tree, {
184             class => 'Markdown::Compiler::Parser::Node::Table',
185             # tokens => [ $token ],
186             children => [ $self->_parse_table( $tokens ) ],
187             };
188 3         8 next;
189             }
190            
191             # Blockquotes
192             elsif ( $token->type eq 'BlockQuote' ) {
193 1         6 push @tree, {
194             class => 'Markdown::Compiler::Parser::Node::BlockQuote',
195             # tokens => [ $token ],
196             children => [ $self->_parse_blockquote( $tokens ) ],
197             };
198 1         3 next;
199             }
200            
201             # Code Blocks
202             elsif ( $token->type eq 'CodeBlock' ) {
203 2         30 push @tree, {
204             class => 'Markdown::Compiler::Parser::Node::CodeBlock',
205             language => $token->language,
206             # tokens => [ $token ],
207             children => [ $self->_parse_codeblock( $tokens ) ],
208             };
209 2         5 next;
210             }
211            
212             # Lists
213             elsif ( $token->type eq 'Item' ) {
214             # Put the item token back so that _parse_list knows what kind it is.
215 4         8 unshift @{$tokens}, $token;
  4         11  
216 4         20 push @tree, $self->_parse_list( $tokens );
217 4         10 next;
218             }
219              
220             # Tokens To Ignore
221 8         16 elsif ( grep { $token->type eq $_ } ( qw( LineBreak ) ) ) {
222             # Do Nothing.
223 8         15 next;
224             }
225              
226             # Unknown Token?
227             else {
228 18     18   25969 use Data::Dumper::Concise;
  18         5614  
  18         47442  
229 0         0 die "Parser::_parse() could not handle token " . $token->type . " on line " . $token->line;
230             }
231             }
232 64         227 return [ @tree ];
233             }
234              
235             sub _parse_paragraph {
236 77     77   191 my ( $self, $tokens ) = @_;
237              
238 77         112 my @tree;
239              
240 77         103 while ( defined ( my $token = shift @{ $tokens } ) ) {
  379         786  
241             # Exit Conditions:
242             #
243             # - No more tokens (after while loop)
244             # - Two new line tokens in a rwo (first one is eaten)
245 307 100       631 if ( $token->type eq 'LineBreak' ) {
246 7 100 100     30 if ( exists $tokens->[0] and $tokens->[0]->type eq 'LineBreak' ) {
247             # Double Line Break, Bail Out
248 5         22 return @tree;
249             }
250             # Single Line Break - Ignore
251 2         4 next;
252             }
253             # Exit Conditions Continued:
254             #
255             # - Tokens which are invalid in this context, put the token back and return our @ree
256 300 50       466 if ( grep { $token->type eq $_ } (qw(TableStart CodeBlock BlockQuote List HR Header)) ) {
  1800         2620  
257 0         0 unshift @$tokens, $token;
258 0         0 return @tree;
259             }
260              
261              
262             # Parsing
263 300 100       421 if ( grep { $token->type eq $_ } (qw(EscapedChar Space Word Char)) ) {
  1200         1865  
264 272         3924 push @tree, {
265             class => 'Markdown::Compiler::Parser::Node::Paragraph::String',
266             content => $token->content,
267             # tokens => [ $token ],
268             };
269 272         671 next;
270             }
271              
272 28 100       50 if ( grep { $token->type eq $_ } (qw(Link)) ) {
  28         60  
273 9         154 push @tree, {
274             class => 'Markdown::Compiler::Parser::Node::Paragraph::Link',
275             text => $token->text,
276             title => $token->title,
277             href => $token->href,
278             # tokens => [ $token ],
279             };
280 9         85 next;
281             }
282            
283 19 100       53 if ( $token->type eq 'Image' ) {
284 4         68 push @tree, {
285             class => 'Markdown::Compiler::Parser::Node::Paragraph::Image',
286             text => $token->text,
287             title => $token->title,
288             href => $token->href,
289             # tokens => [ $token ],
290             };
291 4         44 next;
292             }
293              
294 15 100       47 if ( $token->type eq 'InlineCode' ) {
295 2         3 my @todo;
296              
297             # Eat tokens until the next Bold block, these tokens will be recursively processed.
298 2         4 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  5         11  
299 5 100       19 last if $todo_token->type eq 'InlineCode';
300              
301             # Don't cross linebreak boundries
302 3 50       8 if ( $todo_token->type eq 'LineBreak' ) {
303 0         0 unshift @{$tokens}, $todo_token;
  0         0  
304 0         0 last;
305             }
306              
307 3         7 push @todo, $todo_token;
308             }
309              
310             # Handle the children as plain strings.
311             push @tree, {
312             class => 'Markdown::Compiler::Parser::Node::Paragraph::InlineCode',
313             content => $token->content,
314             # tokens => [ $token ],
315             children => [
316 2         37 map { +{
317 3         43 class => 'Markdown::Compiler::Parser::Node::Paragraph::String',
318             content => $_->content,
319             tokens => [ $_ ],
320             } } @todo
321             ],
322             };
323 2         5 next;
324             }
325            
326 13 50       40 if ( $token->type eq 'BoldItalic' ) {
327 0         0 my @todo;
328              
329             # Eat tokens until the next BoldItalic block, these tokens will be recursively processed.
330 0         0 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  0         0  
331 0 0       0 last if $todo_token->type eq 'BoldItalic';
332              
333             # Don't cross linebreak boundries
334 0 0       0 if ( $todo_token->type eq 'LineBreak' ) {
335 0         0 unshift @{$tokens}, $todo_token;
  0         0  
336 0         0 last;
337             }
338              
339 0         0 push @todo, $todo_token;
340             }
341              
342             # Process the children with _parse_paragraph.
343 0         0 push @tree, {
344             class => 'Markdown::Compiler::Parser::Node::Paragraph::BoldItalic',
345             content => $token->content,
346             # tokens => [ $token ],
347             children => [ $self->_parse_paragraph( \@todo ) ],
348             };
349 0         0 next;
350             }
351            
352 13 100       35 if ( $token->type eq 'Bold' ) {
353 6         7 my @todo;
354              
355             # Eat tokens until the next Bold block, these tokens will be recursively processed.
356 6         8 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  16         36  
357 16 100       30 last if $todo_token->type eq 'Bold';
358              
359             # Don't cross linebreak boundries
360 10 50       17 if ( $todo_token->type eq 'LineBreak' ) {
361 0         0 unshift @{$tokens}, $todo_token;
  0         0  
362 0         0 last;
363             }
364              
365 10         21 push @todo, $todo_token;
366             }
367              
368             # Process the children with _parse_paragraph.
369 6         96 push @tree, {
370             class => 'Markdown::Compiler::Parser::Node::Paragraph::Bold',
371             content => $token->content,
372             # tokens => [ $token ],
373             children => [ $self->_parse_paragraph( \@todo ) ],
374             };
375 6         13 next;
376             }
377              
378 7 50       19 if ( $token->type eq 'Italic' ) {
379 7         15 my @todo;
380              
381             # Eat tokens until the next Italic block, these tokens will be recursively processed.
382 7         9 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  20         43  
383 20 100       36 last if $todo_token->type eq 'Italic';
384              
385             # Don't cross linebreak boundries
386 13 50       24 if ( $todo_token->type eq 'LineBreak' ) {
387 0         0 unshift @{$tokens}, $todo_token;
  0         0  
388 0         0 last;
389             }
390              
391 13         23 push @todo, $todo_token;
392             }
393              
394             # Process the children with _parse_paragraph.
395 7         124 push @tree, {
396             class => 'Markdown::Compiler::Parser::Node::Paragraph::Italic',
397             content => $token->content,
398             # tokens => [ $token ],
399             children => [ $self->_parse_paragraph( \@todo ) ],
400             };
401 7         16 next;
402             }
403            
404             # Unknown Token?
405             else {
406 0         0 die "Parser::_parse_paragraph() could not handle token " . $token->{type};
407             }
408             }
409 72         278 return @tree;
410             }
411              
412             sub _parse_table_row {
413 3     3   28 my ( $self, $tokens ) = @_;
414            
415 3         6 my @tree;
416              
417             # We must eat from here to
418 3         6 while ( my $token = shift @{ $tokens } ) {
  6         26  
419 3 50       10 last if $token->type eq 'LineBreak';
420              
421 3         7 my @todo;
422             # Eat all of the tokens from here until the next |
423 3         6 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  6         14  
424 6 100 66     14 last if $todo_token->type eq 'Char' and $todo_token->content eq '|';
425 3 50       8 last if $todo_token->type eq 'LineBreak';
426 3         7 push @todo, $todo_token;
427             }
428 3         65 push @tree, {
429             class => 'Markdown::Compiler::Parser::Node::Table::Cell',
430             content => $token->content,
431             # tokens => [ $token ],
432             children => [ $self->_parse_paragraph( \@todo ) ],
433             };
434 3         12 next;
435             }
436              
437 3         10 return @tree;
438             }
439              
440             sub _parse_table_header_row {
441 3     3   32 my ( $self, $tokens ) = @_;
442            
443 3         6 my @tree;
444              
445             # We must eat from here to
446 3         6 while ( my $token = shift @{ $tokens } ) {
  6         17  
447 3 50       8 last if $token->type eq 'LineBreak';
448              
449 3         7 my @todo;
450             # Eat all of the tokens from here until the next |
451 3         5 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  6         28  
452 6 100 66     16 last if $todo_token->type eq 'Char' and $todo_token->content eq '|';
453 3 50       9 last if $todo_token->type eq 'LineBreak';
454 3         17 push @todo, $todo_token;
455             }
456 3         73 push @tree, {
457             class => 'Markdown::Compiler::Parser::Node::Table::HeaderCell',
458             content => $token->content,
459             # tokens => [ $token ],
460             children => [ $self->_parse_paragraph( \@todo ) ],
461             };
462 3         8 next;
463             }
464              
465 3         11 return @tree;
466             }
467              
468             sub _parse_table {
469 3     3   7 my ( $self, $tokens ) = @_;
470            
471 3         5 my @tree;
472              
473 3         6 my $is_first_row = 1;
474 3         5 while ( defined ( my $token = shift @{ $tokens } ) ) {
  20         38  
475             # Exit Conditions:
476             #
477             # - Line break and no more tokens (after while loop)
478             # - Line break, and another line break.
479 20 100       39 if ( $token->type eq 'LineBreak' ) {
480 7 50       17 return @tree unless @$tokens;
481 7 100       15 return @tree if $tokens->[0]->type eq 'LineBreak';
482             }
483              
484 17 100       29 if ( $token->type eq 'TableStart' ) {
485 6         8 my @todo;
486              
487             # Eat tokens until the next Italic block, these tokens will be recursively processed.
488 6         9 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  24         46  
489 24 50       45 last if $todo_token->type eq 'TableStart';
490              
491             # Don't cross linebreak boundries
492 24 100       49 if ( $todo_token->type eq 'LineBreak' ) {
493 6         8 unshift @{$tokens}, $todo_token;
  6         11  
494 6         10 last;
495             }
496              
497 18         32 push @todo, $todo_token;
498             }
499              
500             # Process the children with _parse_paragraph.
501 6 100       15 if ( $is_first_row ) {
502 3         69 push @tree, {
503             class => 'Markdown::Compiler::Parser::Node::Table::Row',
504             content => $token->content,
505             # tokens => [ $token ],
506             children => [ $self->_parse_table_header_row( \@todo ) ],
507             };
508 3         6 $is_first_row = 0;
509             } else {
510 3         58 push @tree, {
511             class => 'Markdown::Compiler::Parser::Node::Table::Row',
512             content => $token->content,
513             # tokens => [ $token ],
514             children => [ $self->_parse_table_row( \@todo ) ],
515             };
516             }
517 6         12 next;
518             }
519             }
520 0         0 return @tree;
521             }
522              
523             sub _parse_table_2 {
524 0     0   0 my ( $self, $tokens ) = @_;
525             # Token Types:
526             # package Markdown::Compiler::Lexer;
527             # package Markdown::Compiler::Lexer::Token;
528             # package Markdown::Compiler::Lexer::Token::EscapedChar;
529             # package Markdown::Compiler::Lexer::Token::CodeBlock;
530             # package Markdown::Compiler::Lexer::Token::HR;
531             # package Markdown::Compiler::Lexer::Token::Image;
532             # package Markdown::Compiler::Lexer::Token::Link;
533             # package Markdown::Compiler::Lexer::Token::Item;
534             # package Markdown::Compiler::Lexer::Token::TableStart;
535             # package Markdown::Compiler::Lexer::Token::TableHeaderSep;
536             # package Markdown::Compiler::Lexer::Token::BlockQuote;
537             # package Markdown::Compiler::Lexer::Token::Header;
538             # package Markdown::Compiler::Lexer::Token::Bold;
539             # package Markdown::Compiler::Lexer::Token::Italic;
540             # package Markdown::Compiler::Lexer::Token::BoldItalic;
541             # package Markdown::Compiler::Lexer::Token::BoldItalicMaker;
542             # package Markdown::Compiler::Lexer::Token::LineBreak;
543             # package Markdown::Compiler::Lexer::Token::Space;
544             # package Markdown::Compiler::Lexer::Token::Word;
545             # package Markdown::Compiler::Lexer::Token::Char;
546              
547             }
548              
549             sub _parse_blockquote {
550 1     1   3 my ( $self, $tokens ) = @_;
551              
552 1         2 my @tree;
553              
554 1         1 while ( defined ( my $token = shift @{ $tokens } ) ) {
  7         19  
555             # Exit Conditions:
556             #
557             # - Line break and no more tokens (after while loop)
558             # - Line break, and another line break.
559 7 100       17 if ( $token->type eq 'LineBreak' ) {
560 2 100       10 return @tree unless @$tokens;
561 1 50       3 return @tree if $tokens->[0]->type eq 'LineBreak';
562             }
563              
564 6 100       13 next if $token->type eq 'BlockQuote';
565              
566 5         84 push @tree, {
567             class => 'Markdown::Compiler::Parser::Node::BlockQuote::String',
568             content => $token->content,
569             # tokens => [ $token ],
570             };
571             }
572 0         0 return @tree;
573             }
574              
575             sub _parse_codeblock {
576 2     2   7 my ( $self, $tokens ) = @_;
577              
578 2         3 my @tree;
579              
580 2         3 while ( defined ( my $token = shift @{ $tokens } ) ) {
  8         17  
581             # Exit Conditions:
582             #
583             # - No more tokens (after while loop)
584             # - Run into the next CodeBlock token.
585 8 100       19 if ( $token->type eq 'CodeBlock' ) {
586 2         14 return @tree;
587             }
588            
589 6         110 push @tree, {
590             class => 'Markdown::Compiler::Parser::Node::CodeBlock::String',
591             content => $token->content,
592             # tokens => [ $token ],
593             };
594             }
595 0         0 return @tree;
596             }
597              
598             # Lists are:
599             #
600             # Ordered ( Numbered )
601             # List Item (Paragraph-like Processing)
602             # New Line terminates (We'll ignore that space-carry-on bullshit for now)
603             # Match Order Preceeding (Spaces before Item), and go to next List Item OR return tree
604             #
605             # Unordered ( Bulleted)
606             #
607             #
608             # Functions:
609             #
610             # _parse_list_unordered( $offset_for_next_match, $tokens )
611             # _parse_list_ordered( $offset_for_next_match, $tokens )
612             # _parse_list_item( $tokens )
613             #
614             #
615             #
616              
617             sub _parse_list_item {
618 13     13   26 my ( $self, $tokens ) = @_;
619              
620 13         22 my @tree;
621              
622 13         26 while ( defined ( my $token = shift @{ $tokens } ) ) {
  26         115  
623             # Exit Conditions:
624             #
625             # - No more tokens (after while loop)
626             # - Run into the next CodeBlock token.
627 25 100       64 if ( $token->type eq 'LineBreak' ) {
628 12         43 return @tree;
629             }
630              
631             # Handle links in list
632 13 100       26 if ( $token->type eq 'Link' ) {
633 1         16 push @tree, {
634             class => 'Markdown::Compiler::Parser::Node::Paragraph::Link',
635             text => $token->text,
636             title => $token->title,
637             href => $token->href,
638             # tokens => [ $token ],
639             };
640 1         10 next;
641             }
642              
643 12         214 push @tree, {
644             class => 'Markdown::Compiler::Parser::Node::List::Item::String',
645             content => $token->content,
646             # tokens => [ $token ],
647             };
648             }
649              
650 1         5 return @tree;
651             }
652              
653             sub _parse_list_ordered {
654 1     1   2 my ( $self, $lvl, $tokens ) = @_;
655              
656 1         3 my @tree;
657              
658 1         2 while ( defined ( my $token = shift @{ $tokens } ) ) {
  5         12  
659             # Exit Conditions.
660             #
661             # If we hit any linebreak we go back to _parse_list to handle it.
662 4 50 33     8 if ( $token->type eq 'LineBreak' ) {
    50          
    0          
    0          
663 0         0 unshift @{$tokens}, $token;
  0         0  
664 0         0 return @tree;
665              
666             }
667              
668             # Handle the next item ( root level )
669             elsif ( $lvl == 0 and $token->type eq 'Item' ) {
670 4         10 push @tree, {
671             class => 'Markdown::Compiler::Parser::Node::List::Ordered::Item',
672             # tokens => [ $token ],
673             children => [ $self->_parse_list_item( $tokens ) ],
674             };
675 4         6 next;
676             }
677              
678             # Transitioning from level 1 to 0 doesn't use the space method below,
679             # it uses this one here.
680             elsif ( $token->type eq 'Item' ) {
681             # Put the space/item token back, return our tree.
682 0         0 unshift @{$tokens}, $token;
  0         0  
683 0         0 return @tree;
684             }
685              
686             # Handle Space
687             elsif ( $token->type eq 'Space' ) {
688             # warn "After this space token is a " . $tokens->[0]->type . " with " . $tokens->[0]->content . " content\n";
689             # Case: This is the ordering level for this invocation, stay in this list.
690 0 0 0     0 if ( $token->length == $lvl ) {
    0          
    0          
691 0         0 $token = shift @{$tokens};
  0         0  
692 0 0       0 if ( $token->type eq 'Word' ) { # Golden, correct stay-in-list level
693 0         0 $token = shift @{$tokens}
694 0 0       0 if $tokens->[0]->{type} eq 'Space'; # The space before the Item
695 0         0 push @tree, {
696             class => 'Markdown::Compiler::Parser::Node::List::Ordered::Item',
697             # tokens => [ $token ],
698             children => [ $self->_parse_list_item( $tokens ) ],
699             };
700 0         0 next;
701             }
702 0         0 die "Error: It shouldn't have gotten here, we're fucked";
703             }
704              
705             # Case: This list is now complete, the next request was for the next parent item.
706             elsif ( $token->length < $lvl or $token->type eq 'Item' ) {
707             # Put the space/item token back, return our tree.
708 0         0 unshift @{$tokens}, $token;
  0         0  
709 0         0 return @tree;
710             }
711              
712              
713             # Case: This is a new list, existing under the last Item
714             elsif ( $token->length > $lvl ) {
715 0 0       0 if ( $token->content =~ /^\d+\.\s+$/ ) {
716 0         0 unshift @{$tokens}, $token;
  0         0  
717 0         0 push @tree, {
718             class => 'Markdown::Compiler::Parser::Node::List::Ordered',
719             # tokens => [ ],
720             children => [ $self->_parse_list_ordered( $token->length, $tokens ) ]
721             };
722 0         0 next;
723             } else {
724 0         0 unshift @{$tokens}, $token;
  0         0  
725             push @tree, {
726             class => 'Markdown::Compiler::Parser::Node::List::Unordered',
727             # tokens => [ ],
728 0         0 children => [ $self->_parse_list_unordered( $token->{length}, $tokens ) ]
729             };
730 0         0 next;
731             }
732             }
733              
734             else {
735 0         0 die "Parser::_parse_list_unordered() could not handle token " . $token->type;
736             }
737              
738             }
739             }
740 1         4 return @tree;
741             }
742              
743             sub _parse_list_unordered {
744 4     4   28 my ( $self, $lvl, $tokens ) = @_;
745              
746 4         9 my @tree;
747              
748 4         15 while ( defined ( my $token = shift @{ $tokens } ) ) {
  14         40  
749             # Exit Conditions.
750             #
751             # If we hit any linebreak we go back to _parse_list to handle it.
752 11 50 100     31 if ( $token->type eq 'LineBreak' ) {
    100          
    100          
    50          
753 0         0 unshift @{$tokens}, $token;
  0         0  
754 0         0 return @tree;
755              
756             }
757              
758             # Handle the next item ( root level )
759             elsif ( $lvl == 0 and $token->type eq 'Item' ) {
760 8         25 push @tree, {
761             class => 'Markdown::Compiler::Parser::Node::List::Unordered::Item',
762             # tokens => [ $token ],
763             children => [ $self->_parse_list_item( $tokens ) ],
764             };
765 8         19 next;
766             }
767              
768             # Transitioning from level 1 to 0 doesn't use the space method below,
769             # it uses this one here.
770             elsif ( $token->type eq 'Item' ) {
771             # Put the space/item token back, return our tree.
772 1         5 unshift @{$tokens}, $token;
  1         46  
773 1         8 return @tree;
774             }
775              
776             # Handle Space
777             elsif ( $token->type eq 'Space' ) {
778             # warn "After this space token is a " . $tokens->[0]->type . " with " . $tokens->[0]->content . " content\n";
779             # Case: This is the ordering level for this invocation, stay in this list.
780 2 100 33     49 if ( $token->length == $lvl ) {
    50          
    50          
781 1         6 $token = shift @{$tokens};
  1         3  
782 1 50       6 if ( $token->type eq 'Char' ) { # Golden, correct stay-in-list level
783 1 50       4 $token = shift @{$tokens}
  1         2  
784             if $tokens->[0]->type eq 'Space'; # The space before the Item
785 1         3 push @tree, {
786             class => 'Markdown::Compiler::Parser::Node::List::Unordered::Item',
787             # tokens => [ $token ],
788             children => [ $self->_parse_list_item( $tokens ) ],
789             };
790 1         57 next;
791             }
792 0         0 die "Error: It shouldn't have gotten here, we're fucked";
793             }
794              
795             # Case: This list is now complete, the next request was for the next parent item.
796             elsif ( $token->length < $lvl or $token->type eq 'Item' ) {
797             # Put the space/item token back, return our tree.
798 0         0 unshift @{$tokens}, $token;
  0         0  
799 0         0 return @tree;
800             }
801              
802             # Case: This is a new list, existing under the last Item
803             elsif ( $token->length > $lvl ) {
804 1 50       22 if ( $token->content =~ /^\d+\.\s+$/ ) {
805 0         0 unshift @{$tokens}, $token;
  0         0  
806 0         0 push @tree, {
807             class => 'Markdown::Compiler::Parser::Node::List::Ordered',
808             # tokens => [ ],
809             children => [ $self->_parse_list_ordered( $token->length, $tokens ) ]
810             };
811 0         0 next;
812             } else {
813 1         8 unshift @{$tokens}, $token;
  1         3  
814 1         15 push @tree, {
815             class => 'Markdown::Compiler::Parser::Node::List::Unordered',
816             # tokens => [ ],
817             children => [ $self->_parse_list_unordered( $token->length, $tokens ) ]
818             };
819 1         3 next;
820             }
821             }
822              
823              
824             else {
825 0         0 die "Parser::_parse_list_unordered() could not handle token " . $token->type;
826             }
827              
828             }
829             }
830 3         14 return @tree;
831             }
832              
833             sub _parse_list {
834 4     4   12 my ( $self, $tokens ) = @_;
835              
836 4         8 my @tree;
837              
838 4         8 while ( defined ( my $token = shift @{ $tokens } ) ) {
  8         32  
839             # Exit Conditions:
840             #
841             # - No more tokens (after while loop)
842             # - Two new line tokens in a rwo (first one is eaten)
843 4 50       12 if ( $token->type eq 'LineBreak' ) {
844 0 0 0     0 if ( exists $tokens->[0] and $tokens->[0]->type eq 'LineBreak' ) {
845             # Double Line Break, Bail Out
846 0         0 warn "See the bail out condition.... in _parse_list\n";
847 0         0 return @tree;
848             }
849             # Single Line Break - Ignore
850 0         0 next;
851             }
852             # Exit Conditions Continued:
853             #
854             # - Tokens which are invalid in this context, put the token back and return our @ree
855 4 50       12 if ( grep { $token->type eq $_ } (qw(Char Word TableStart CodeBlock BlockQuote List HR Header)) ) {
  32         54  
856 0         0 unshift @$tokens, $token;
857 0         0 return @tree;
858             }
859            
860 4 50       19 if ( $token->type eq 'Item' ) {
861 4 100       89 if ( $token->content =~ /^\d+\.\s+$/ ) {
862 1         2 unshift @{$tokens}, $token;
  1         14  
863 1         5 push @tree, {
864             class => 'Markdown::Compiler::Parser::Node::List::Ordered',
865             # tokens => [ ],
866             children => [ $self->_parse_list_ordered( 0, $tokens ) ]
867             };
868 1         13 next;
869             } else {
870 3         6 unshift @{$tokens}, $token;
  3         8  
871 3         16 push @tree, {
872             class => 'Markdown::Compiler::Parser::Node::List::Unordered',
873             # tokens => [ ],
874             children => [ $self->_parse_list_unordered( 0, $tokens ) ]
875             };
876 3         7 next;
877             }
878             }
879            
880 0         0 die "Parser::_parse_list() could not handle token " . $token->type;
881              
882             }
883 4         12 return @tree;
884              
885             # Token Types:
886             # package Markdown::Compiler::Lexer;
887             # package Markdown::Compiler::Lexer::Token;
888             # package Markdown::Compiler::Lexer::Token::EscapedChar;
889             # package Markdown::Compiler::Lexer::Token::CodeBlock;
890             # package Markdown::Compiler::Lexer::Token::HR;
891             # package Markdown::Compiler::Lexer::Token::Image;
892             # package Markdown::Compiler::Lexer::Token::Link;
893             # package Markdown::Compiler::Lexer::Token::Item;
894             # package Markdown::Compiler::Lexer::Token::TableStart;
895             # package Markdown::Compiler::Lexer::Token::TableHeaderSep;
896             # package Markdown::Compiler::Lexer::Token::BlockQuote;
897             # package Markdown::Compiler::Lexer::Token::Header;
898             # package Markdown::Compiler::Lexer::Token::Bold;
899             # package Markdown::Compiler::Lexer::Token::Italic;
900             # package Markdown::Compiler::Lexer::Token::BoldItalic;
901             # package Markdown::Compiler::Lexer::Token::BoldItalicMaker;
902             # package Markdown::Compiler::Lexer::Token::LineBreak;
903             # package Markdown::Compiler::Lexer::Token::Space;
904             # package Markdown::Compiler::Lexer::Token::Word;
905             # package Markdown::Compiler::Lexer::Token::Char;
906             }
907              
908             sub _parse_metadata {
909 0     0     my ( $self, $tokens ) = @_;
910              
911 0           my @tree;
912              
913 0           while ( defined ( my $token = shift @{ $tokens } ) ) {
  0            
914             # Exit Conditions:
915             #
916             # - We run into the HR block.
917 0 0         if ( $token->type eq 'HR' ) {
918 0           last;
919             }
920              
921 0 0         if ( grep { $token->type eq $_ } ( qw( EscapedChar Space Word Char LineBreak ) ) ) {
  0            
922 0           push @tree, $token;
923 0           next;
924             }
925              
926 0           die "Parser::_parse_metadata() could not handle token " . $token->type;
927             }
928              
929              
930 0           my $content = join "", map { $_->content } @tree;
  0            
931              
932 0           require YAML::XS;
933 0           my $struct = YAML::XS::Load( $content );
934              
935              
936             return {
937 0           content => $content,
938             tokens => [ @tree ],
939             data => $struct,
940             };
941             }
942              
943             sub show_tree {
944 0     0 0   my ( $self ) = @_;
945              
946 0           print $self->_pretty_print(0, $self->tree);
947             }
948              
949             sub _pretty_print {
950 0     0     my ( $self, $index, $tokens ) = @_;
951              
952 0   0       $index ||= 0;
953 0           my $str;
954              
955 0           foreach my $token ( @{$tokens} ) {
  0            
956              
957 0           my $tab = " " x ( $index x 2 );
958              
959 0           my $class = ref($token);
960 0           $class =~ s|Markdown::Compiler::Parser::Node::||;
961              
962 0           my $content = join "", map { $_->content } (@{$token->tokens});
  0            
  0            
963 0           $content =~ s/\n/\\n/g;
964 0           $content =~ s/\r/\\n/g;
965              
966 0           $str .=
967             " " x ( $index * 2 ) .
968             sprintf( '%-' . (35 - ($index * 2)) . 's', $class ) .
969             "| $content\n";
970              
971 0 0         $str .= $self->_pretty_print( $index + 1, $token->children )
972             if $token->children;
973             }
974 0           return $str;
975             }
976              
977              
978              
979              
980             1;