File Coverage

blib/lib/Markdown/Compiler/Parser.pm
Criterion Covered Total %
statement 276 373 73.9
branch 111 172 64.5
condition 23 56 41.0
subroutine 22 25 88.0
pod 0 2 0.0
total 432 628 68.7


line stmt bran cond sub pod time code
1             package Markdown::Compiler::Parser;
2             BEGIN {
3             {
4             package Markdown::Compiler::Parser::Node;
5 17     17   132 use Moo;
  17         38  
  17         110  
6              
7 17         70 has tokens => (
8             is => 'ro',
9             required => 1,
10             );
11            
12 17         12672 has children => (
13             is => 'ro',
14             );
15              
16 17         4576 has content => (
17             is => 'ro',
18             );
19              
20 17         4313 1;
21             }
22              
23             {
24 17     17   5514 package Markdown::Compiler::Parser::Node::Metadata;
25 17     17   6023 use Moo;
  17         470  
  17         99  
26 17         79 extends 'Markdown::Compiler::Parser::Node';
27              
28 17         2548 has data => (
29             is => 'ro',
30             );
31              
32             # content => $content,
33             # tokens => [ @tree ],
34             # data => $struct,
35 17         12134 1;
36             }
37              
38             {
39 17         44 package Markdown::Compiler::Parser::Node::Metadata::Key;
40 17     17   6816 use Moo;
  17         69  
  17         79  
41 17         61 extends 'Markdown::Compiler::Parser::Node';
42              
43 17         2270 1;
44             }
45              
46             {
47 17         33 package Markdown::Compiler::Parser::Node::Metadata::Value;
  17         50  
48 17     17   5960 use Moo;
  17         56  
  17         70  
49 17         74 extends 'Markdown::Compiler::Parser::Node';
50              
51 17         2666 1;
52             }
53             }
54 17     17   125 use Moo;
  17         44  
  17         88  
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 65     65   26322 my ( $self ) = @_;
82              
83 65         125 my @tokens = @{$self->stream};
  65         246  
84              
85 65         219 return $self->make_hash($self->_parse(\@tokens));
86             }
87              
88             sub make_hash {
89 175     175 0 454 my ( $self, $tokens ) = @_;
90              
91 175         270 my @stream;
92              
93 175         319 foreach my $token ( @{$tokens} ) {
  175         349  
94              
95 428 100       908 if ( ref($token) eq 'HASH' ) {
96 427 100 66     1981 if ( $token->{children} && @{$token->{children}} >= 1 ) {
  110         366  
97 110         391 $token->{children} = [ $self->make_hash( $token->{children} ) ];
98             }
99 427         811 push @stream, $token;
100             } else {
101 1 50 33     81 push @stream, {
    50 33        
    50 33        
    50 33        
    50 33        
    50 33        
    50 33        
    50 33        
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 175         1632 return [ @stream ];
117             }
118              
119             sub _build_metadata {
120 1     1   1391 my ( $self ) = @_;
121              
122 1 50 33     20 if ( $self->tree->[0] and $self->tree->[0]->{class} eq 'Markdown::Compiler::Parser::Node::Metadata' ) {
123 1         26 return $self->tree->[0]->{data};
124             }
125 0         0 return undef;
126             }
127              
128             sub _parse {
129 65     65   158 my ( $self, $tokens ) = @_;
130 65         117 my @tree;
131              
132 65         105 while ( defined ( my $token = shift @{ $tokens } ) ) {
  144         425  
133             # Header
134 79 100       295 if ( $token->type eq 'Header' ) {
    100          
    100          
    100          
    100          
    100          
    100          
    50          
135 1         21 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         5 next;
144             }
145              
146             # Paragraphs
147 702         1283 elsif ( grep { $token->type eq $_ } ( qw( EscapedChar Image Link Word Char Bold Italic BoldItalic InlineCode ) ) ) {
148 58         99 unshift @{$tokens}, $token; # Put the token back and go to paragraph context.
  58         192  
149 58         184 push @tree, {
150             class => 'Markdown::Compiler::Parser::Node::Paragraph',
151             children => [ $self->_parse_paragraph( $tokens ) ],
152             };
153              
154 58         130 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 1 50       9 if ( $token->start == 0 ) {
163             push @tree, Markdown::Compiler::Parser::Node::Metadata->new(
164 1         3 %{ $self->_parse_metadata($tokens) },
  1         5  
165             );
166             # language => $token->language,
167             # tokens => [ $token ],
168             # children => [ $self->_parse_metadata( $tokens ) ],
169 1         1424 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         9 unshift @{$tokens}, $token; # Put the token back and go to table context.
  3         9  
183 3         18 push @tree, {
184             class => 'Markdown::Compiler::Parser::Node::Table',
185             # tokens => [ $token ],
186             children => [ $self->_parse_table( $tokens ) ],
187             };
188 3         10 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         4 next;
199             }
200            
201             # Code Blocks
202             elsif ( $token->type eq 'CodeBlock' ) {
203 2         38 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         6 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         10 unshift @{$tokens}, $token;
  4         12  
216 4         22 push @tree, $self->_parse_list( $tokens );
217 4         14 next;
218             }
219              
220             # Tokens To Ignore
221 9         27 elsif ( grep { $token->type eq $_ } ( qw( LineBreak ) ) ) {
222             # Do Nothing.
223 9         35 next;
224             }
225              
226             # Unknown Token?
227             else {
228 17     17   29835 use Data::Dumper::Concise;
  17         6165  
  17         54130  
229 0         0 die "Parser::_parse() could not handle token " . $token->type . " on line " . $token->line;
230             }
231             }
232 65         271 return [ @tree ];
233             }
234              
235             sub _parse_paragraph {
236 78     78   215 my ( $self, $tokens ) = @_;
237              
238 78         119 my @tree;
239              
240 78         123 while ( defined ( my $token = shift @{ $tokens } ) ) {
  385         932  
241             # Exit Conditions:
242             #
243             # - No more tokens (after while loop)
244             # - Two new line tokens in a rwo (first one is eaten)
245 313 100       728 if ( $token->type eq 'LineBreak' ) {
246 8 100 100     95 if ( exists $tokens->[0] and $tokens->[0]->type eq 'LineBreak' ) {
247             # Double Line Break, Bail Out
248 6         32 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 305 50       560 if ( grep { $token->type eq $_ } (qw(TableStart CodeBlock BlockQuote List HR Header)) ) {
  1830         3178  
257 0         0 unshift @$tokens, $token;
258 0         0 return @tree;
259             }
260              
261              
262             # Parsing
263 305 100       516 if ( grep { $token->type eq $_ } (qw(EscapedChar Space Word Char)) ) {
  1220         2197  
264 277         4920 push @tree, {
265             class => 'Markdown::Compiler::Parser::Node::Paragraph::String',
266             content => $token->content,
267             # tokens => [ $token ],
268             };
269 277         678 next;
270             }
271              
272 28 100       55 if ( grep { $token->type eq $_ } (qw(Link)) ) {
  28         87  
273 9         159 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         105 next;
281             }
282            
283 19 100       51 if ( $token->type eq 'Image' ) {
284 4         74 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       35 if ( $token->type eq 'InlineCode' ) {
295 2         4 my @todo;
296              
297             # Eat tokens until the next Bold block, these tokens will be recursively processed.
298 2         3 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  5         12  
299 5 100       13 last if $todo_token->type eq 'InlineCode';
300              
301             # Don't cross linebreak boundries
302 3 50       17 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         45 map { +{
317 3         51 class => 'Markdown::Compiler::Parser::Node::Paragraph::String',
318             content => $_->content,
319             tokens => [ $_ ],
320             } } @todo
321             ],
322             };
323 2         6 next;
324             }
325            
326 13 50       42 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       45 if ( $token->type eq 'Bold' ) {
353 6         10 my @todo;
354              
355             # Eat tokens until the next Bold block, these tokens will be recursively processed.
356 6         13 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  16         43  
357 16 100       36 last if $todo_token->type eq 'Bold';
358              
359             # Don't cross linebreak boundries
360 10 50       19 if ( $todo_token->type eq 'LineBreak' ) {
361 0         0 unshift @{$tokens}, $todo_token;
  0         0  
362 0         0 last;
363             }
364              
365 10         23 push @todo, $todo_token;
366             }
367              
368             # Process the children with _parse_paragraph.
369 6         127 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         17 next;
376             }
377              
378 7 50       16 if ( $token->type eq 'Italic' ) {
379 7         12 my @todo;
380              
381             # Eat tokens until the next Italic block, these tokens will be recursively processed.
382 7         57 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  20         46  
383 20 100       78 last if $todo_token->type eq 'Italic';
384              
385             # Don't cross linebreak boundries
386 13 50       25 if ( $todo_token->type eq 'LineBreak' ) {
387 0         0 unshift @{$tokens}, $todo_token;
  0         0  
388 0         0 last;
389             }
390              
391 13         28 push @todo, $todo_token;
392             }
393              
394             # Process the children with _parse_paragraph.
395 7         182 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         19 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         315 return @tree;
410             }
411              
412             sub _parse_table_row {
413 3     3   38 my ( $self, $tokens ) = @_;
414            
415 3         16 my @tree;
416              
417             # We must eat from here to
418 3         7 while ( my $token = shift @{ $tokens } ) {
  6         38  
419 3 50       14 last if $token->type eq 'LineBreak';
420              
421 3         19 my @todo;
422             # Eat all of the tokens from here until the next |
423 3         8 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  6         23  
424 6 100 66     19 last if $todo_token->type eq 'Char' and $todo_token->content eq '|';
425 3 50       18 last if $todo_token->type eq 'LineBreak';
426 3         10 push @todo, $todo_token;
427             }
428 3         81 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         10 next;
435             }
436              
437 3         20 return @tree;
438             }
439              
440             sub _parse_table_header_row {
441 3     3   43 my ( $self, $tokens ) = @_;
442            
443 3         7 my @tree;
444              
445             # We must eat from here to
446 3         7 while ( my $token = shift @{ $tokens } ) {
  6         24  
447 3 50       13 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         7 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  6         32  
452 6 100 66     19 last if $todo_token->type eq 'Char' and $todo_token->content eq '|';
453 3 50       11 last if $todo_token->type eq 'LineBreak';
454 3         8 push @todo, $todo_token;
455             }
456 3         93 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         20 next;
463             }
464              
465 3         15 return @tree;
466             }
467              
468             sub _parse_table {
469 3     3   14 my ( $self, $tokens ) = @_;
470            
471 3         6 my @tree;
472              
473 3         8 my $is_first_row = 1;
474 3         6 while ( defined ( my $token = shift @{ $tokens } ) ) {
  20         50  
475             # Exit Conditions:
476             #
477             # - Line break and no more tokens (after while loop)
478             # - Line break, and another line break.
479 20 100       48 if ( $token->type eq 'LineBreak' ) {
480 7 50       21 return @tree unless @$tokens;
481 7 100       51 return @tree if $tokens->[0]->type eq 'LineBreak';
482             }
483              
484 17 100       40 if ( $token->type eq 'TableStart' ) {
485 6         11 my @todo;
486              
487             # Eat tokens until the next Italic block, these tokens will be recursively processed.
488 6         26 while ( defined ( my $todo_token = shift @{ $tokens } ) ) {
  24         53  
489 24 50       57 last if $todo_token->type eq 'TableStart';
490              
491             # Don't cross linebreak boundries
492 24 100       51 if ( $todo_token->type eq 'LineBreak' ) {
493 6         15 unshift @{$tokens}, $todo_token;
  6         12  
494 6         17 last;
495             }
496              
497 18         41 push @todo, $todo_token;
498             }
499              
500             # Process the children with _parse_paragraph.
501 6 100       30 if ( $is_first_row ) {
502 3         94 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         10 $is_first_row = 0;
509             } else {
510 3         74 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         18 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   4 my ( $self, $tokens ) = @_;
551              
552 1         2 my @tree;
553              
554 1         4 while ( defined ( my $token = shift @{ $tokens } ) ) {
  7         22  
555             # Exit Conditions:
556             #
557             # - Line break and no more tokens (after while loop)
558             # - Line break, and another line break.
559 7 100       22 if ( $token->type eq 'LineBreak' ) {
560 2 100       13 return @tree unless @$tokens;
561 1 50       5 return @tree if $tokens->[0]->type eq 'LineBreak';
562             }
563              
564 6 100       14 next if $token->type eq 'BlockQuote';
565              
566 5         113 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   6 my ( $self, $tokens ) = @_;
577              
578 2         3 my @tree;
579              
580 2         4 while ( defined ( my $token = shift @{ $tokens } ) ) {
  8         25  
581             # Exit Conditions:
582             #
583             # - No more tokens (after while loop)
584             # - Run into the next CodeBlock token.
585 8 100       25 if ( $token->type eq 'CodeBlock' ) {
586 2         12 return @tree;
587             }
588            
589 6         111 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   32 my ( $self, $tokens ) = @_;
619              
620 13         19 my @tree;
621              
622 13         23 while ( defined ( my $token = shift @{ $tokens } ) ) {
  26         73  
623             # Exit Conditions:
624             #
625             # - No more tokens (after while loop)
626             # - Run into the next CodeBlock token.
627 25 100       92 if ( $token->type eq 'LineBreak' ) {
628 12         52 return @tree;
629             }
630              
631             # Handle links in list
632 13 100       37 if ( $token->type eq 'Link' ) {
633 1         41 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         13 next;
641             }
642              
643 12         246 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   3 my ( $self, $lvl, $tokens ) = @_;
655              
656 1         2 my @tree;
657              
658 1         2 while ( defined ( my $token = shift @{ $tokens } ) ) {
  5         16  
659             # Exit Conditions.
660             #
661             # If we hit any linebreak we go back to _parse_list to handle it.
662 4 50 33     10 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         20 push @tree, {
671             class => 'Markdown::Compiler::Parser::Node::List::Ordered::Item',
672             # tokens => [ $token ],
673             children => [ $self->_parse_list_item( $tokens ) ],
674             };
675 4         9 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         14 return @tree;
741             }
742              
743             sub _parse_list_unordered {
744 4     4   21 my ( $self, $lvl, $tokens ) = @_;
745              
746 4         17 my @tree;
747              
748 4         10 while ( defined ( my $token = shift @{ $tokens } ) ) {
  14         42  
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         24 push @tree, {
761             class => 'Markdown::Compiler::Parser::Node::List::Unordered::Item',
762             # tokens => [ $token ],
763             children => [ $self->_parse_list_item( $tokens ) ],
764             };
765 8         20 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         2 unshift @{$tokens}, $token;
  1         3  
773 1         4 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     39 if ( $token->length == $lvl ) {
    50          
    50          
781 1         9 $token = shift @{$tokens};
  1         3  
782 1 50       5 if ( $token->type eq 'Char' ) { # Golden, correct stay-in-list level
783 1 50       6 $token = shift @{$tokens}
  1         3  
784             if $tokens->[0]->type eq 'Space'; # The space before the Item
785 1         14 push @tree, {
786             class => 'Markdown::Compiler::Parser::Node::List::Unordered::Item',
787             # tokens => [ $token ],
788             children => [ $self->_parse_list_item( $tokens ) ],
789             };
790 1         5 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       25 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         14 unshift @{$tokens}, $token;
  1         5  
814 1         18 push @tree, {
815             class => 'Markdown::Compiler::Parser::Node::List::Unordered',
816             # tokens => [ ],
817             children => [ $self->_parse_list_unordered( $token->length, $tokens ) ]
818             };
819 1         4 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         15 return @tree;
831             }
832              
833             sub _parse_list {
834 4     4   14 my ( $self, $tokens ) = @_;
835              
836 4         9 my @tree;
837              
838 4         8 while ( defined ( my $token = shift @{ $tokens } ) ) {
  8         33  
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       14 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       16 if ( grep { $token->type eq $_ } (qw(Char Word TableStart CodeBlock BlockQuote List HR Header)) ) {
  32         84  
856 0         0 unshift @$tokens, $token;
857 0         0 return @tree;
858             }
859            
860 4 50       16 if ( $token->type eq 'Item' ) {
861 4 100       118 if ( $token->content =~ /^\d+\.\s+$/ ) {
862 1         3 unshift @{$tokens}, $token;
  1         3  
863 1         6 push @tree, {
864             class => 'Markdown::Compiler::Parser::Node::List::Ordered',
865             # tokens => [ ],
866             children => [ $self->_parse_list_ordered( 0, $tokens ) ]
867             };
868 1         6 next;
869             } else {
870 3         11 unshift @{$tokens}, $token;
  3         9  
871 3         17 push @tree, {
872             class => 'Markdown::Compiler::Parser::Node::List::Unordered',
873             # tokens => [ ],
874             children => [ $self->_parse_list_unordered( 0, $tokens ) ]
875             };
876 3         22 next;
877             }
878             }
879            
880 0         0 die "Parser::_parse_list() could not handle token " . $token->type;
881              
882             }
883 4         20 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 1     1   3 my ( $self, $tokens ) = @_;
910              
911 1         2 my @tree;
912              
913 1         2 while ( defined ( my $token = shift @{ $tokens } ) ) {
  6         20  
914             # Exit Conditions:
915             #
916             # - We run into the HR block.
917 6 100       18 if ( $token->type eq 'HR' ) {
918 1         2 last;
919             }
920              
921 5 50       11 if ( grep { $token->type eq $_ } ( qw( EscapedChar Space Word Char LineBreak ) ) ) {
  25         48  
922 5         11 push @tree, $token;
923 5         8 next;
924             }
925              
926 0         0 die "Parser::_parse_metadata() could not handle token " . $token->type;
927             }
928              
929              
930 1         4 my $content = join "", map { $_->content } @tree;
  5         97  
931              
932 1         624 require YAML::XS;
933 1         3053 my $struct = YAML::XS::Load( $content );
934              
935              
936             return {
937 1         23 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;