lib/Text/Hatena.pm | |||
---|---|---|---|
Criterion | Covered | Total | % |
statement | 19 | 21 | 90.4 |
branch | n/a | ||
condition | n/a | ||
subroutine | 7 | 7 | 100.0 |
pod | n/a | ||
total | 26 | 28 | 92.8 |
line | stmt | bran | cond | sub | pod | time | code |
---|---|---|---|---|---|---|---|
1 | package Text::Hatena; | ||||||
2 | 3 | 3 | 536909 | use strict; | |||
3 | 8 | ||||||
3 | 103 | ||||||
3 | 3 | 3 | 17 | use warnings; | |||
3 | 5 | ||||||
3 | 79 | ||||||
4 | 3 | 3 | 15 | use Carp; | |||
3 | 10 | ||||||
3 | 1034 | ||||||
5 | 3 | 3 | 15 | use base qw(Class::Data::Inheritable); | |||
3 | 5 | ||||||
3 | 1599 | ||||||
6 | 2 | 2 | 2005 | use vars qw($VERSION); | |||
2 | 4 | ||||||
2 | 88 | ||||||
7 | 2 | 2 | 3417 | use Parse::RecDescent; | |||
2 | 377143 | ||||||
2 | 17 | ||||||
8 | 2 | 2 | 1680 | use Text::Hatena::AutoLink; | |||
0 | |||||||
0 | |||||||
9 | |||||||
10 | $VERSION = '0.20'; | ||||||
11 | |||||||
12 | my ($parser, $syntax); | ||||||
13 | |||||||
14 | __PACKAGE__->mk_classdata('syntax'); | ||||||
15 | |||||||
16 | #$::RD_HINT = 1; | ||||||
17 | #$::RD_TRACE = 1; | ||||||
18 | #$::RD_WARN = undef; | ||||||
19 | $Parse::RecDescent::skip = ''; | ||||||
20 | $syntax = q( | ||||||
21 | body : section(s) | ||||||
22 | section : h3(?) block(s?) | ||||||
23 | # Block Elements | ||||||
24 | block : h5 | ||||||
25 | | h4 | ||||||
26 | | blockquote | ||||||
27 | | dl | ||||||
28 | | list | ||||||
29 | | super_pre | ||||||
30 | | pre | ||||||
31 | | table | ||||||
32 | | cdata | ||||||
33 | | p | ||||||
34 | h3 : "\n*" inline(s) | ||||||
35 | h4 : "\n**" inline(s) | ||||||
36 | h5 : "\n***" inline(s) | ||||||
37 | blockquote : "\n>" http(?) ">" block(s) "\n<<" ..."\n" | ||||||
38 | dl : dl_item(s) | ||||||
39 | dl_item : "\n:" inline[term => ':'](s) ':' inline(s) | ||||||
40 | list : list_item[level => $arg{level} || 1](s) | ||||||
41 | list_item : "\n" /[+-]{$arg{level}}/ inline(s) list[level => $arg{level} + 1](?) | ||||||
42 | super_pre : /\n>\|(\w*)\|/o text_line(s) "\n||<" ..."\n" | ||||||
43 | text_line : ...!"\n||<\n" "\n" /[^\n]*/o | ||||||
44 | pre : "\n>|" pre_line(s) "\n|<" ..."\n" | ||||||
45 | pre_line : ...!"\n|<" "\n" inline(s?) | ||||||
46 | table : table_row(s) | ||||||
47 | table_row : "\n|" td(s /\|/) '|' | ||||||
48 | td : /\*?/o inline[term => '\|'](s) | ||||||
49 | cdata : "\n><" /.+?(?=><\n)/so "><" ..."\n" | ||||||
50 | p : ...!p_terminal "\n" inline(s?) | ||||||
51 | p_terminal : h3 | "\n<<\n" | ||||||
52 | # Inline Elements | ||||||
53 | inline : /[^\n$arg{term}]+/ | ||||||
54 | http : /https?:\/\/[A-Za-z0-9~\/._\?\&=\-%#\+:\;,\@\']+(?::title=[^\]]+)?/ | ||||||
55 | ); | ||||||
56 | |||||||
57 | sub parse { | ||||||
58 | my $class = shift; | ||||||
59 | my $text = shift or return; | ||||||
60 | $text =~ s/\r//g; | ||||||
61 | $text = "\n" . $text unless $text =~ /^\n/; | ||||||
62 | $text .= "\n" unless $text =~ /\n$/; | ||||||
63 | my $node = shift || 'body'; | ||||||
64 | my $html = $class->parser->$node($text); | ||||||
65 | # warn $html; | ||||||
66 | return $html; | ||||||
67 | } | ||||||
68 | |||||||
69 | sub parser { | ||||||
70 | my $class = shift; | ||||||
71 | unless (defined $parser) { | ||||||
72 | $::RD_AUTOACTION = q|my $method = shift @item;| . | ||||||
73 | $class . q|->$method({items => \@item});|; | ||||||
74 | $parser = Parse::RecDescent->new($syntax); | ||||||
75 | if ($class->syntax) { | ||||||
76 | $parser->Replace($class->syntax); | ||||||
77 | } | ||||||
78 | } | ||||||
79 | return $parser; | ||||||
80 | } | ||||||
81 | |||||||
82 | sub expand { | ||||||
83 | my $class = shift; | ||||||
84 | my $array = shift or return; | ||||||
85 | ref($array) eq 'ARRAY' or return; | ||||||
86 | my $ret = ''; | ||||||
87 | while (my $item = shift @$array) { | ||||||
88 | if (ref($item) eq 'ARRAY') { | ||||||
89 | my $c = $class->expand($item); | ||||||
90 | $ret .= $c if $c; | ||||||
91 | } else { | ||||||
92 | $ret .= $item if $item; | ||||||
93 | } | ||||||
94 | } | ||||||
95 | return $ret; | ||||||
96 | } | ||||||
97 | |||||||
98 | # Nodes | ||||||
99 | # Block Nodes | ||||||
100 | sub abstract { | ||||||
101 | my $class = shift; | ||||||
102 | my $items = shift->{items}; | ||||||
103 | return $class->expand($items); | ||||||
104 | } | ||||||
105 | |||||||
106 | *body = \&abstract; | ||||||
107 | *block = \&abstract; | ||||||
108 | *line = \&abstract; | ||||||
109 | |||||||
110 | sub section { | ||||||
111 | my $class = shift; | ||||||
112 | my $items = shift->{items}; | ||||||
113 | my $body = $class->expand($items) || ''; | ||||||
114 | $body =~ s/\n\n$/\n/; | ||||||
115 | return $body ? qq| \n| . $body . qq| \n| : ''; |
||||||
116 | } | ||||||
117 | |||||||
118 | sub h3 { | ||||||
119 | my $class = shift; | ||||||
120 | my $items = shift->{items}; | ||||||
121 | my $title = $class->expand($items->[1]); | ||||||
122 | return if $title =~ /^\*/; | ||||||
123 | return "$title\n"; |
||||||
124 | } | ||||||
125 | |||||||
126 | sub h4 { | ||||||
127 | my $class = shift; | ||||||
128 | my $items = shift->{items}; | ||||||
129 | my $title = $class->expand($items->[1]); | ||||||
130 | return if $title =~ /^\*/; | ||||||
131 | return "$title\n"; |
||||||
132 | } | ||||||
133 | |||||||
134 | sub h5 { | ||||||
135 | my $class = shift; | ||||||
136 | my $items = shift->{items}; | ||||||
137 | my $title = $class->expand($items->[1]); | ||||||
138 | return "$title\n"; |
||||||
139 | } | ||||||
140 | |||||||
141 | sub blockquote { | ||||||
142 | my $class = shift; | ||||||
143 | my $items = shift->{items}; | ||||||
144 | my $body = $class->expand($items->[3]); | ||||||
145 | my $http = $items->[1]->[0]; | ||||||
146 | my $ret = ''; | ||||||
147 | if ($http) { | ||||||
148 | $ret = qq|\n|; |
||||||
149 | } else { | ||||||
150 | $ret = "\n"; |
||||||
151 | } | ||||||
152 | $ret .= $body; | ||||||
153 | if ($http) { | ||||||
154 | $ret .= qq|$http->{title}\n|; | ||||||
155 | } | ||||||
156 | $ret .= "\n"; | ||||||
157 | return $ret; | ||||||
158 | } | ||||||
159 | |||||||
160 | sub bq_block { | ||||||
161 | my $class = shift; | ||||||
162 | my $items = shift->{items}; | ||||||
163 | return $class->expand($items->[0]); | ||||||
164 | } | ||||||
165 | |||||||
166 | sub dl { | ||||||
167 | my $class = shift; | ||||||
168 | my $items = shift->{items}; | ||||||
169 | my $list = $class->expand($items->[0]); | ||||||
170 | return "
|
||||||
171 | } | ||||||
172 | |||||||
173 | sub dl_item { | ||||||
174 | my $class = shift; | ||||||
175 | my $items = shift->{items}; | ||||||
176 | my $dt = $class->expand($items->[1]); | ||||||
177 | my $dd = $class->expand($items->[3]); | ||||||
178 | return " |
||||||
179 | } | ||||||
180 | |||||||
181 | sub dt { | ||||||
182 | my $class = shift; | ||||||
183 | my $items = shift->{items}; | ||||||
184 | my $dt = $class->expand($items->[1]); | ||||||
185 | return " |
||||||
186 | } | ||||||
187 | |||||||
188 | sub list { | ||||||
189 | my $class = shift; | ||||||
190 | my $items = shift->{items}; | ||||||
191 | my ($list,$tag); | ||||||
192 | for my $li (@{$items->[0]}) { | ||||||
193 | $tag ||= $li =~ /^\-/ ? 'ul' : 'ol'; | ||||||
194 | $li =~ s/^[+-]+//; | ||||||
195 | $list .= $li; | ||||||
196 | } | ||||||
197 | return "<$tag>\n$list$tag>\n"; | ||||||
198 | } | ||||||
199 | |||||||
200 | sub list_item { | ||||||
201 | my $class = shift; | ||||||
202 | my $items = shift->{items}; | ||||||
203 | my $li = $class->expand($items->[2]); | ||||||
204 | my $sl = $class->expand($items->[3]) || ''; | ||||||
205 | $sl = "\n" . $sl if $sl; | ||||||
206 | return $items->[1] . " |
||||||
207 | } | ||||||
208 | |||||||
209 | sub super_pre { | ||||||
210 | my $class = shift; | ||||||
211 | my $items = shift->{items}; | ||||||
212 | my $filter = $1 || ''; # todo | ||||||
213 | my $texts = $class->expand($items->[1]); | ||||||
214 | return "\n$texts\n"; |
||||||
215 | } | ||||||
216 | |||||||
217 | sub pre { | ||||||
218 | my $class = shift; | ||||||
219 | my $items = shift->{items}; | ||||||
220 | my $lines = $class->expand($items->[1]); | ||||||
221 | return "\n$lines\n"; |
||||||
222 | } | ||||||
223 | |||||||
224 | sub pre_line { | ||||||
225 | my $class = shift; | ||||||
226 | my $items = shift->{items}; | ||||||
227 | my $inlines = $class->expand($items->[2]); | ||||||
228 | return "$inlines\n"; | ||||||
229 | } | ||||||
230 | |||||||
231 | sub table { | ||||||
232 | my $class = shift; | ||||||
233 | my $items = shift->{items}; | ||||||
234 | my $trs = $class->expand($items->[0]); | ||||||
235 | return " |
||||||
236 | } | ||||||
237 | |||||||
238 | sub table_row { # we can't use tr! | ||||||
239 | my $class = shift; | ||||||
240 | my $items = shift->{items}; | ||||||
241 | my $tds = $class->expand($items->[1]); | ||||||
242 | return " | ||||||
243 | } | ||||||
244 | |||||||
245 | sub td { | ||||||
246 | my $class = shift; | ||||||
247 | my $items = shift->{items}; | ||||||
248 | my $tag = $items->[0] ? 'th' : 'td'; | ||||||
249 | my $inlines = $class->expand($items->[1]); | ||||||
250 | return "<$tag>$inlines$tag>\n"; | ||||||
251 | } | ||||||
252 | |||||||
253 | sub cdata { | ||||||
254 | my $class = shift; | ||||||
255 | my $items = shift->{items}; | ||||||
256 | my $data = $items->[1]; | ||||||
257 | return "<$data>\n"; | ||||||
258 | } | ||||||
259 | |||||||
260 | sub p { | ||||||
261 | my $class = shift; | ||||||
262 | my $items = shift->{items}; | ||||||
263 | my $inlines = $class->expand($items->[2]); | ||||||
264 | return $inlines ? " $inlines \n" : "\n"; |
||||||
265 | } | ||||||
266 | |||||||
267 | sub text_line { | ||||||
268 | my $class = shift; | ||||||
269 | my $text = shift->{items}->[2]; | ||||||
270 | return "$text\n"; | ||||||
271 | } | ||||||
272 | |||||||
273 | # Inline Nodes | ||||||
274 | sub inline { | ||||||
275 | my $class = shift; | ||||||
276 | my $items = shift->{items}; | ||||||
277 | my $item = $items->[0] or return; | ||||||
278 | $item = Text::Hatena::AutoLink->parse($item); | ||||||
279 | return $item; | ||||||
280 | } | ||||||
281 | |||||||
282 | sub http { | ||||||
283 | my $class = shift; | ||||||
284 | my $items = shift->{items}; | ||||||
285 | my $item = $items->[0] or return; | ||||||
286 | $item =~ s/:title=([^\]]+)$//; | ||||||
287 | my $title = $1 || $item; | ||||||
288 | return { | ||||||
289 | cite => $item, | ||||||
290 | title => $title, | ||||||
291 | } | ||||||
292 | } | ||||||
293 | |||||||
294 | 1; | ||||||
295 | |||||||
296 | __END__ |