File Coverage

blib/lib/Markua/Parser.pm
Criterion Covered Total %
statement 100 103 97.0
branch 29 32 90.6
condition 9 12 75.0
subroutine 6 6 100.0
pod 0 3 0.0
total 144 156 92.3


line stmt bran cond sub pod time code
1             package Markua::Parser;
2 1     1   67210 use strict;
  1         3  
  1         23  
3 1     1   3 use warnings;
  1         2  
  1         20  
4 1     1   4 use Path::Tiny qw(path);
  1         1  
  1         815  
5              
6             our $VERSION = 0.01;
7              
8             sub new {
9 1     1 0 840 my ($class) = @_;
10 1         3 my $self = bless {}, $class;
11 1         2 return $self;
12             }
13              
14             sub parse_file {
15 7     7 0 11955 my ($self, $filename) = @_;
16 7         17 my $path = path($filename);
17 7         179 my $dir = $path->parent->stringify;
18 7         357 my @entries;
19             my @errors;
20 7         9 my $cnt = 0;
21              
22 7         11 $self->{text} = '';
23              
24 7         16 for my $line ($path->lines_utf8) {
25 75         11758 $cnt++;
26 75 100       131 if ($line =~ /^(#{1,6}) (\S.*)/) {
27 7         26 push @entries, {
28             tag => 'h' . length($1),
29             text => $2,
30             };
31 7         9 next;
32             }
33              
34             # numbered list
35 68 100       105 if ($line =~ m{\A(\d+)([.\)])( {1,4}|\t)(\S.*)}) {
36 6         21 my ($number, $sep, $space, $text) = ($1, $2, $3, $4);
37 6 100       10 if (not $self->{tag}) {
38 2         4 $self->{tag} = 'numbered-list';
39 2         4 $self->{list} = [];
40             }
41              
42 6 50       9 if ($self->{tag} eq 'numbered-list') {
43 6         7 push @{ $self->{list} }, {
  6         20  
44             number => $number,
45             sep => $sep,
46             space => $space,
47             text => $text,
48             raw => $line,
49             };
50 6         9 next;
51             }
52              
53 0         0 die "What to do if a numbered list starts in the middle of another element?";
54             }
55              
56             # bulleted list
57 62 100       98 if ($line =~ m{\A([\*-])( {1,4}|\t)(\S.*)}) {
58 12         27 my ($bullet, $space, $text) = ($1, $2, $3);
59 12 100       22 if (not $self->{tag}) {
60 8         9 $self->{tag} = 'list';
61 8         10 $self->{list}{type} = 'bulleted';
62 8         13 $self->{list}{bullet} = $bullet;
63 8         11 $self->{list}{space} = $space;
64 8         9 $self->{list}{ok} = 1;
65 8         20 $self->{list}{items} = [$text];
66 8         12 $self->{list}{raw} = [$line];
67 8         13 next;
68             }
69              
70 4 50       6 if ($self->{tag} eq 'list') {
71 4 100 33     19 if ($self->{list}{type} ne 'bulleted' or
      66        
72             $self->{list}{bullet} ne $bullet or
73             $self->{list}{space} ne $space) {
74 2         2 $self->{list}{ok} = 0;
75             }
76 4         5 push @{ $self->{list}{raw} }, $line;
  4         9  
77 4         4 push @{ $self->{list}{items} }, $text;
  4         5  
78 4         7 next;
79             }
80              
81 0         0 die "What to do if a bulleted list starts in the middle of another element?";
82             }
83              
84             # I should remember to always use \A instead of ^ even thoygh here we are really parsing lines so those two are the same
85 50 100       72 if ($line =~ /\A ! \[([^\]]*)\] \(([^\)]+)\) \s* \Z/x) {
86 4         9 my $title = $1;
87 4         7 my $file_to_include = $2;
88 4         5 eval {
89 4         16 my $text = path("$dir/$file_to_include")->slurp_utf8;
90 3         438 push @entries, {
91             tag => 'code',
92             title => $title,
93             text => $text,
94             };
95             };
96 4 100       342 if ($@) {
97 1         10 push @errors, {
98             row => $cnt,
99             line => $line,
100             error => "Could not read included file '$file_to_include'",
101             };
102             }
103 4         8 next;
104             }
105              
106             # anything else defaults to paragraph
107 46 100       82 if ($line =~ /\S/) {
108 9         11 $self->{tag} = 'p';
109 9         16 $self->{text} .= $line;
110 9         11 next;
111             }
112              
113 37 50       83 if ($line =~ /^\s*$/) {
114 37         76 $self->save_tag(\@entries);
115 37         51 next;
116             }
117              
118 0         0 push @errors, {
119             row => $cnt,
120             line => $line,
121             }
122             }
123 7         18 $self->save_tag(\@entries);
124 7         26 return \@entries, \@errors;
125             }
126              
127             sub save_tag {
128 44     44 0 57 my ($self, $entries) = @_;
129              
130 44 100 100     94 if ($self->{tag} and $self->{tag} eq 'numbered-list') {
131             # TODO: verify that it is a proper list
132 2         2 for my $row (@{ $self->{list} }) {
  2         4  
133 6         6 delete $row->{raw};
134 6         7 delete $row->{sep};
135 6         8 delete $row->{space};
136             }
137             push @$entries, {
138             tag => $self->{tag},
139             list => $self->{list},
140 2         5 };
141 2         3 $self->{tag} = undef;
142 2         4 delete $self->{list};
143 2         2 return;
144             }
145              
146              
147 42 100 100     74 if ($self->{tag} and $self->{tag} eq 'list') {
148 8 100       13 if ($self->{list}{ok}) {
149 6         16 delete $self->{list}{raw};
150 6         7 delete $self->{list}{ok};
151 6         6 delete $self->{list}{space};
152 6         7 delete $self->{list}{bullet};
153             push @$entries, {
154             tag => $self->{tag},
155             list => $self->{list},
156 6         16 };
157 6         7 $self->{tag} = undef;
158 6         6 delete $self->{list};
159 6         8 return;
160             }
161              
162             # If it is a failed list, convert it to paragraph
163 2         3 $self->{tag} = 'p';
164 2         2 $self->{text} = join '', @{ $self->{list}{raw} };
  2         5  
165 2         6 delete $self->{list};
166             }
167              
168 36 100       46 if ($self->{tag}) {
169 9         35 $self->{text} =~ s/\n+\Z//;
170             push @$entries, {
171             tag => $self->{tag},
172             text => $self->{text},
173 9         26 };
174 9         11 $self->{tag} = undef;
175 9         9 $self->{text} = '';
176             }
177 36         37 return;
178             }
179              
180             1;
181              
182             __END__