File Coverage

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


\n$tds\n";
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 "
\n$list
\n";
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 "
$dt
\n
$dd
\n";
179             }
180              
181             sub dt {
182             my $class = shift;
183             my $items = shift->{items};
184             my $dt = $class->expand($items->[1]);
185             return "
$dt
\n";
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\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] . "
  • $li$sl
  • \n";
    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 "\n$trs
    \n";
    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\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__