← Index
NYTProf Performance Profile   « block view • line view • sub view »
For -e
  Run on Wed Nov 17 21:45:08 2010
Reported on Wed Nov 17 22:15:00 2010

Filename/home/doy/perl5/perlbrew/perls/perl-5.10.1/lib/site_perl/5.10.1/Markdent/Dialect/Standard/SpanParser.pm
StatementsExecuted 100 statements in 24.8ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
647114.10ms4.10msClass::MOP::Method::Generated::::CORE:match Class::MOP::Method::Generated::CORE:match (opcode)
1112.00ms187msMarkdent::Dialect::Standard::SpanParser::::BEGIN@21Markdent::Dialect::Standard::SpanParser::BEGIN@21
1111.92ms197msMarkdent::Dialect::Standard::SpanParser::::BEGIN@25Markdent::Dialect::Standard::SpanParser::BEGIN@25
1111.89ms173msMarkdent::Dialect::Standard::SpanParser::::BEGIN@27Markdent::Dialect::Standard::SpanParser::BEGIN@27
1111.82ms168msMarkdent::Dialect::Standard::SpanParser::::BEGIN@22Markdent::Dialect::Standard::SpanParser::BEGIN@22
1111.82ms165msMarkdent::Dialect::Standard::SpanParser::::BEGIN@14Markdent::Dialect::Standard::SpanParser::BEGIN@14
1111.78ms167msMarkdent::Dialect::Standard::SpanParser::::BEGIN@26Markdent::Dialect::Standard::SpanParser::BEGIN@26
1111.74ms302msMarkdent::Dialect::Standard::SpanParser::::BEGIN@13Markdent::Dialect::Standard::SpanParser::BEGIN@13
1111.72ms167msMarkdent::Dialect::Standard::SpanParser::::BEGIN@17Markdent::Dialect::Standard::SpanParser::BEGIN@17
1111.69ms157msMarkdent::Dialect::Standard::SpanParser::::BEGIN@24Markdent::Dialect::Standard::SpanParser::BEGIN@24
1111.68ms163msMarkdent::Dialect::Standard::SpanParser::::BEGIN@20Markdent::Dialect::Standard::SpanParser::BEGIN@20
1111.68ms189msMarkdent::Dialect::Standard::SpanParser::::BEGIN@23Markdent::Dialect::Standard::SpanParser::BEGIN@23
1111.66ms150msMarkdent::Dialect::Standard::SpanParser::::BEGIN@15Markdent::Dialect::Standard::SpanParser::BEGIN@15
1111.63ms153msMarkdent::Dialect::Standard::SpanParser::::BEGIN@19Markdent::Dialect::Standard::SpanParser::BEGIN@19
1111.48ms167msMarkdent::Dialect::Standard::SpanParser::::BEGIN@18Markdent::Dialect::Standard::SpanParser::BEGIN@18
1111.44ms147msMarkdent::Dialect::Standard::SpanParser::::BEGIN@12Markdent::Dialect::Standard::SpanParser::BEGIN@12
1111.27ms158msMarkdent::Dialect::Standard::SpanParser::::BEGIN@16Markdent::Dialect::Standard::SpanParser::BEGIN@16
111140µs28.0msMarkdent::Dialect::Standard::SpanParser::::BEGIN@32Markdent::Dialect::Standard::SpanParser::BEGIN@32
11158µs21.8msMarkdent::Dialect::Standard::SpanParser::::BEGIN@33Markdent::Dialect::Standard::SpanParser::BEGIN@33
11157µs26.4msMarkdent::Dialect::Standard::SpanParser::::BEGIN@29Markdent::Dialect::Standard::SpanParser::BEGIN@29
11156µs126µsMarkdent::Dialect::Standard::SpanParser::::BEGIN@7Markdent::Dialect::Standard::SpanParser::BEGIN@7
11152µs52µsMarkdent::Dialect::Standard::SpanParser::::BEGIN@2Markdent::Dialect::Standard::SpanParser::BEGIN@2
11147µs461µsMarkdent::Dialect::Standard::SpanParser::::BEGIN@28Markdent::Dialect::Standard::SpanParser::BEGIN@28
11147µs27.2msMarkdent::Dialect::Standard::SpanParser::::BEGIN@34Markdent::Dialect::Standard::SpanParser::BEGIN@34
11146µs224µsMarkdent::Dialect::Standard::SpanParser::::BEGIN@31Markdent::Dialect::Standard::SpanParser::BEGIN@31
11141µs206µsMarkdent::Dialect::Standard::SpanParser::::BEGIN@11Markdent::Dialect::Standard::SpanParser::BEGIN@11
11141µs59µsMarkdent::Dialect::Standard::SpanParser::::BEGIN@6Markdent::Dialect::Standard::SpanParser::BEGIN@6
11137µs128µsMarkdent::Dialect::Standard::SpanParser::::BEGIN@9Markdent::Dialect::Standard::SpanParser::BEGIN@9
22120µs20µsMarkdent::Dialect::Standard::SpanParser::::CORE:qrMarkdent::Dialect::Standard::SpanParser::CORE:qr (opcode)
0000s0sClass::MOP::Method::Generated::::__ANON__[:55] Class::MOP::Method::Generated::__ANON__[:55]
0000s0sClass::MOP::Method::Generated::::__ANON__[:61] Class::MOP::Method::Generated::__ANON__[:61]
0000s0sClass::MOP::Method::Generated::::__ANON__[:63] Class::MOP::Method::Generated::__ANON__[:63]
0000s0sClass::MOP::Method::Generated::::__ANON__[:70] Class::MOP::Method::Generated::__ANON__[:70]
0000s0sClass::MOP::Method::Generated::::__ANON__[:71] Class::MOP::Method::Generated::__ANON__[:71]
0000s0sClass::MOP::Method::Generated::::__ANON__[:83] Class::MOP::Method::Generated::__ANON__[:83]
0000s0sClass::MOP::Method::Generated::::__ANON__[:92] Class::MOP::Method::Generated::__ANON__[:92]
0000s0sMarkdent::Dialect::Standard::SpanParser::::__ANON__[:42]Markdent::Dialect::Standard::SpanParser::__ANON__[:42]
0000s0sMarkdent::Dialect::Standard::SpanParser::::__ANON__[:68]Markdent::Dialect::Standard::SpanParser::__ANON__[:68]
0000s0sMarkdent::Dialect::Standard::SpanParser::::_build_escapable_charsMarkdent::Dialect::Standard::SpanParser::_build_escapable_chars
0000s0sMarkdent::Dialect::Standard::SpanParser::::_build_escape_reMarkdent::Dialect::Standard::SpanParser::_build_escape_re
0000s0sMarkdent::Dialect::Standard::SpanParser::::_convert_invalid_start_events_to_textMarkdent::Dialect::Standard::SpanParser::_convert_invalid_start_events_to_text
0000s0sMarkdent::Dialect::Standard::SpanParser::::_convert_start_event_to_textMarkdent::Dialect::Standard::SpanParser::_convert_start_event_to_text
0000s0sMarkdent::Dialect::Standard::SpanParser::::_debug_pending_eventsMarkdent::Dialect::Standard::SpanParser::_debug_pending_events
0000s0sMarkdent::Dialect::Standard::SpanParser::::_event_for_text_bufferMarkdent::Dialect::Standard::SpanParser::_event_for_text_buffer
0000s0sMarkdent::Dialect::Standard::SpanParser::::_link_match_resultsMarkdent::Dialect::Standard::SpanParser::_link_match_results
0000s0sMarkdent::Dialect::Standard::SpanParser::::_look_for_strong_and_emphasisMarkdent::Dialect::Standard::SpanParser::_look_for_strong_and_emphasis
0000s0sMarkdent::Dialect::Standard::SpanParser::::_markup_eventMarkdent::Dialect::Standard::SpanParser::_markup_event
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_auto_linkMarkdent::Dialect::Standard::SpanParser::_match_auto_link
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_code_endMarkdent::Dialect::Standard::SpanParser::_match_code_end
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_code_startMarkdent::Dialect::Standard::SpanParser::_match_code_start
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_delimiter_endMarkdent::Dialect::Standard::SpanParser::_match_delimiter_end
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_delimiter_startMarkdent::Dialect::Standard::SpanParser::_match_delimiter_start
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_emphasis_endMarkdent::Dialect::Standard::SpanParser::_match_emphasis_end
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_emphasis_startMarkdent::Dialect::Standard::SpanParser::_match_emphasis_start
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_escapeMarkdent::Dialect::Standard::SpanParser::_match_escape
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_html_commentMarkdent::Dialect::Standard::SpanParser::_match_html_comment
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_html_entityMarkdent::Dialect::Standard::SpanParser::_match_html_entity
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_html_tagMarkdent::Dialect::Standard::SpanParser::_match_html_tag
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_imageMarkdent::Dialect::Standard::SpanParser::_match_image
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_linkMarkdent::Dialect::Standard::SpanParser::_match_link
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_plain_textMarkdent::Dialect::Standard::SpanParser::_match_plain_text
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_strong_endMarkdent::Dialect::Standard::SpanParser::_match_strong_end
0000s0sMarkdent::Dialect::Standard::SpanParser::::_match_strong_startMarkdent::Dialect::Standard::SpanParser::_match_strong_start
0000s0sMarkdent::Dialect::Standard::SpanParser::::_merge_consecutive_text_eventsMarkdent::Dialect::Standard::SpanParser::_merge_consecutive_text_events
0000s0sMarkdent::Dialect::Standard::SpanParser::::_open_start_event_for_spanMarkdent::Dialect::Standard::SpanParser::_open_start_event_for_span
0000s0sMarkdent::Dialect::Standard::SpanParser::::_parse_textMarkdent::Dialect::Standard::SpanParser::_parse_text
0000s0sMarkdent::Dialect::Standard::SpanParser::::_parse_uri_and_titleMarkdent::Dialect::Standard::SpanParser::_parse_uri_and_title
0000s0sMarkdent::Dialect::Standard::SpanParser::::_possible_span_matchesMarkdent::Dialect::Standard::SpanParser::_possible_span_matches
0000s0sMarkdent::Dialect::Standard::SpanParser::::_process_id_for_linkMarkdent::Dialect::Standard::SpanParser::_process_id_for_link
0000s0sMarkdent::Dialect::Standard::SpanParser::::_splice_merged_text_eventMarkdent::Dialect::Standard::SpanParser::_splice_merged_text_event
0000s0sMarkdent::Dialect::Standard::SpanParser::::extract_link_idsMarkdent::Dialect::Standard::SpanParser::extract_link_ids
0000s0sMarkdent::Dialect::Standard::SpanParser::::parse_blockMarkdent::Dialect::Standard::SpanParser::parse_block
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1package Markdent::Dialect::Standard::SpanParser;
2
# spent 52µs within Markdent::Dialect::Standard::SpanParser::BEGIN@2 which was called: # once (52µs+0s) by Markdent::Parser::BEGIN@11 at line 4
BEGIN {
3130µs $Markdent::Dialect::Standard::SpanParser::VERSION = '0.17';
4174µs152µs}
5
6397µs277µs
# spent 59µs (41+18) within Markdent::Dialect::Standard::SpanParser::BEGIN@6 which was called: # once (41µs+18µs) by Markdent::Parser::BEGIN@11 at line 6
use strict;
# spent 59µs making 1 call to Markdent::Dialect::Standard::SpanParser::BEGIN@6 # spent 18µs making 1 call to strict::import
73117µs2196µs
# spent 126µs (56+70) within Markdent::Dialect::Standard::SpanParser::BEGIN@7 which was called: # once (56µs+70µs) by Markdent::Parser::BEGIN@11 at line 7
use warnings;
# spent 126µs making 1 call to Markdent::Dialect::Standard::SpanParser::BEGIN@7 # spent 70µs making 1 call to warnings::import
8
93117µs2219µs
# spent 128µs (37+91) within Markdent::Dialect::Standard::SpanParser::BEGIN@9 which was called: # once (37µs+91µs) by Markdent::Parser::BEGIN@11 at line 9
use re 'eval';
# spent 128µs making 1 call to Markdent::Dialect::Standard::SpanParser::BEGIN@9 # spent 91µs making 1 call to re::import
10
113107µs2372µs
# spent 206µs (41+165) within Markdent::Dialect::Standard::SpanParser::BEGIN@11 which was called: # once (41µs+165µs) by Markdent::Parser::BEGIN@11 at line 11
use List::AllUtils qw( uniq );
# spent 206µs making 1 call to Markdent::Dialect::Standard::SpanParser::BEGIN@11 # spent 165µs making 1 call to Exporter::import
123546µs1147ms
# spent 147ms (1.44+146) within Markdent::Dialect::Standard::SpanParser::BEGIN@12 which was called: # once (1.44ms+146ms) by Markdent::Parser::BEGIN@11 at line 12
use Markdent::Event::AutoLink;
133554µs1302ms
# spent 302ms (1.74+301) within Markdent::Dialect::Standard::SpanParser::BEGIN@13 which was called: # once (1.74ms+301ms) by Markdent::Parser::BEGIN@11 at line 13
use Markdent::Event::EndCode;
143644µs1165ms
# spent 165ms (1.82+164) within Markdent::Dialect::Standard::SpanParser::BEGIN@14 which was called: # once (1.82ms+164ms) by Markdent::Parser::BEGIN@11 at line 14
use Markdent::Event::EndEmphasis;
153653µs1150ms
# spent 150ms (1.66+149) within Markdent::Dialect::Standard::SpanParser::BEGIN@15 which was called: # once (1.66ms+149ms) by Markdent::Parser::BEGIN@11 at line 15
use Markdent::Event::EndHTMLTag;
163559µs1158ms
# spent 158ms (1.27+157) within Markdent::Dialect::Standard::SpanParser::BEGIN@16 which was called: # once (1.27ms+157ms) by Markdent::Parser::BEGIN@11 at line 16
use Markdent::Event::EndLink;
173541µs1167ms
# spent 167ms (1.72+165) within Markdent::Dialect::Standard::SpanParser::BEGIN@17 which was called: # once (1.72ms+165ms) by Markdent::Parser::BEGIN@11 at line 17
use Markdent::Event::EndStrong;
183564µs1167ms
# spent 167ms (1.48+166) within Markdent::Dialect::Standard::SpanParser::BEGIN@18 which was called: # once (1.48ms+166ms) by Markdent::Parser::BEGIN@11 at line 18
use Markdent::Event::HTMLComment;
193586µs1153ms
# spent 153ms (1.63+151) within Markdent::Dialect::Standard::SpanParser::BEGIN@19 which was called: # once (1.63ms+151ms) by Markdent::Parser::BEGIN@11 at line 19
use Markdent::Event::HTMLEntity;
203547µs1163ms
# spent 163ms (1.68+161) within Markdent::Dialect::Standard::SpanParser::BEGIN@20 which was called: # once (1.68ms+161ms) by Markdent::Parser::BEGIN@11 at line 20
use Markdent::Event::HTMLTag;
213566µs1187ms
# spent 187ms (2.00+185) within Markdent::Dialect::Standard::SpanParser::BEGIN@21 which was called: # once (2.00ms+185ms) by Markdent::Parser::BEGIN@11 at line 21
use Markdent::Event::Image;
223594µs1168ms
# spent 168ms (1.82+166) within Markdent::Dialect::Standard::SpanParser::BEGIN@22 which was called: # once (1.82ms+166ms) by Markdent::Parser::BEGIN@11 at line 22
use Markdent::Event::StartCode;
233550µs1189ms
# spent 189ms (1.68+187) within Markdent::Dialect::Standard::SpanParser::BEGIN@23 which was called: # once (1.68ms+187ms) by Markdent::Parser::BEGIN@11 at line 23
use Markdent::Event::StartEmphasis;
243554µs1157ms
# spent 157ms (1.69+155) within Markdent::Dialect::Standard::SpanParser::BEGIN@24 which was called: # once (1.69ms+155ms) by Markdent::Parser::BEGIN@11 at line 24
use Markdent::Event::StartHTMLTag;
253635µs1197ms
# spent 197ms (1.92+196) within Markdent::Dialect::Standard::SpanParser::BEGIN@25 which was called: # once (1.92ms+196ms) by Markdent::Parser::BEGIN@11 at line 25
use Markdent::Event::StartLink;
263607µs1167ms
# spent 167ms (1.78+165) within Markdent::Dialect::Standard::SpanParser::BEGIN@26 which was called: # once (1.78ms+165ms) by Markdent::Parser::BEGIN@11 at line 26
use Markdent::Event::StartStrong;
273602µs1173ms
# spent 173ms (1.89+171) within Markdent::Dialect::Standard::SpanParser::BEGIN@27 which was called: # once (1.89ms+171ms) by Markdent::Parser::BEGIN@11 at line 27
use Markdent::Event::Text;
283169µs2875µs
# spent 461µs (47+414) within Markdent::Dialect::Standard::SpanParser::BEGIN@28 which was called: # once (47µs+414µs) by Markdent::Parser::BEGIN@11 at line 28
use Markdent::Regexes qw( $HTMLComment );
# spent 461µs making 1 call to Markdent::Dialect::Standard::SpanParser::BEGIN@28 # spent 413µs making 1 call to Exporter::import
293162µs252.8ms
# spent 26.4ms (57µs+26.4) within Markdent::Dialect::Standard::SpanParser::BEGIN@29 which was called: # once (57µs+26.4ms) by Markdent::Parser::BEGIN@11 at line 29
use Markdent::Types qw( Str ArrayRef HashRef RegexpRef EventObject );
# spent 26.4ms making 1 call to Markdent::Dialect::Standard::SpanParser::BEGIN@29 # spent 26.4ms making 1 call to MooseX::Types::Combine::import
30
313110µs2403µs
# spent 224µs (46+179) within Markdent::Dialect::Standard::SpanParser::BEGIN@31 which was called: # once (46µs+179µs) by Markdent::Parser::BEGIN@11 at line 31
use namespace::autoclean;
# spent 224µs making 1 call to Markdent::Dialect::Standard::SpanParser::BEGIN@31 # spent 179µs making 1 call to namespace::autoclean::import
323212µs255.8ms
# spent 28.0ms (140µs+27.8) within Markdent::Dialect::Standard::SpanParser::BEGIN@32 which was called: # once (140µs+27.8ms) by Markdent::Parser::BEGIN@11 at line 32
use Moose;
# spent 28.0ms making 1 call to Markdent::Dialect::Standard::SpanParser::BEGIN@32 # spent 27.8ms making 1 call to Moose::Exporter::__ANON__[Moose/Exporter.pm:456]
333165µs243.6ms
# spent 21.8ms (58µs+21.8) within Markdent::Dialect::Standard::SpanParser::BEGIN@33 which was called: # once (58µs+21.8ms) by Markdent::Parser::BEGIN@11 at line 33
use MooseX::SemiAffordanceAccessor;
# spent 21.8ms making 1 call to Markdent::Dialect::Standard::SpanParser::BEGIN@33 # spent 21.8ms making 1 call to Moose::Exporter::__ANON__[Moose/Exporter.pm:456]
34313.1ms254.4ms
# spent 27.2ms (47µs+27.2) within Markdent::Dialect::Standard::SpanParser::BEGIN@34 which was called: # once (47µs+27.2ms) by Markdent::Parser::BEGIN@11 at line 34
use MooseX::StrictConstructor;
# spent 27.2ms making 1 call to Markdent::Dialect::Standard::SpanParser::BEGIN@34 # spent 27.2ms making 1 call to Moose::Exporter::__ANON__[Moose/Exporter.pm:456]
35
36117µs187.3mswith 'Markdent::Role::SpanParser';
# spent 87.3ms making 1 call to Moose::with
37
38has __pending_events => (
39 traits => ['Array'],
40 is => 'rw',
41 isa => ArrayRef[EventObject],
42 default => sub { [] },
43169µs31.71s init_arg => undef,
# spent 1.71s making 1 call to Moose::has # spent 5.17ms making 1 call to __TYPE__::ArrayRef # spent 328µs making 1 call to __TYPE__::Markdent::Types::Internal::EventObject
44 handles => {
45 _pending_events => 'elements',
46 _add_pending_event => 'push',
47 _clear_pending_events => 'clear',
48 },
49);
50
51145µs2820mshas _span_text_buffer => (
# spent 820ms making 1 call to Moose::has # spent 334µs making 1 call to __TYPE__::Str
52 traits => ['String'],
53 is => 'ro',
54 isa => Str,
55 default => q{},
56130µs init_arg => undef,
57 handles => {
58 _save_span_text => 'append',
59 _has_span_text_buffer => 'length',
60 _clear_span_text_buffer => 'clear',
61 },
62267µs);
63
64130µshas _links_by_id => (
65 traits => ['Hash'],
66 is => 'ro',
67 isa => HashRef[ArrayRef],
68 default => sub { {} },
69164µs340.2ms init_arg => undef,
# spent 33.0ms making 1 call to Moose::has # spent 6.89ms making 1 call to __TYPE__::HashRef # spent 334µs making 1 call to __TYPE__::ArrayRef
70 handles => {
71130µs _add_link_by_id => 'set',
72130µs _get_link_by_id => 'get',
73 },
74);
75
76126µs214.1mshas _escape_re => (
# spent 13.7ms making 1 call to Moose::has # spent 358µs making 1 call to __TYPE__::RegexpRef
77 is => 'ro',
78 isa => RegexpRef,
79 lazy => 1,
80 builder => '_build_escape_re',
81 init_arg => undef,
82);
83
84270µs319.5mshas _escapable_chars => (
# spent 14.0ms making 1 call to Moose::has # spent 5.13ms making 1 call to __TYPE__::ArrayRef # spent 331µs making 1 call to __TYPE__::Str
85 is => 'ro',
86 isa => ArrayRef [Str],
87 lazy => 1,
88 builder => '_build_escapable_chars',
89);
90
91sub extract_link_ids {
92 my $self = shift;
93146µs my $text = shift;
94
95 ${$text} =~ s/ ^
96 \p{SpaceSeparator}{0,3}
97 \[ ([^]]+) \]
98 :
99 \p{SpaceSeparator}*
100 \n?
101 \p{SpaceSeparator}*
102 (.+)
103 \n
104 /
105 $self->_process_id_for_link( $1, $2 );
106 /egxm;
107}
108
109sub _process_id_for_link {
110 my $self = shift;
111 my $id = shift;
112 my $id_text = shift;
113
114 $id_text =~ s/\s+$//;
115
116 my ( $uri, $title ) = $self->_parse_uri_and_title($id_text);
117
118 $self->_add_link_by_id( $id => [ $uri, $title ] );
119
120 return q{};
121}
122
123sub _parse_uri_and_title {
124 my $self = shift;
125 my $text = shift;
126
127 $text =~ s/^\s+|\s+$//g;
128
129 my ( $uri, $title ) = split /(?:\p{SpaceSeparator}|\t)+/, $text, 2;
130
131 $uri = q{}
132 unless defined $uri;
133
134 $uri =~ s/^<|>$//g;
135 $title =~ s/^"|"$//g
136 if defined $title;
137
138 return ( $uri, $title );
139}
140
141sub parse_block {
142 my $self = shift;
143 my $text = shift;
144
145 $self->_print_debug( "Parsing text for span-level markup\n\n$text\n" )
146 if $self->debug();
147
148 # Note that we have to pass a _reference_ to text in order to make sure
149 # that we are matching the same variable with /g regexes each time.
150 $self->_parse_text(\$text);
151
152 # This catches any bad start events that were found after the last end
153 # event, or if there were _no_ end events at all.
154 $self->_convert_invalid_start_events_to_text('is done');
155
156 $self->_debug_pending_events('before text merging');
157
158 $self->_merge_consecutive_text_events();
159
160 $self->_debug_pending_events('after text merging');
161
162 $self->handler()->handle_event($_)
163 for $self->_pending_events();
164
165 $self->_clear_pending_events();
166
167 return;
168}
169
170sub _parse_text {
171 my $self = shift;
172 my $text = shift;
173
174 PARSE:
175 while (1) {
176 if ( $self->debug() && pos ${$text} ) {
177 $self->_print_debug( "Remaining text:\n[\n"
178 . substr( ${$text}, pos ${$text} )
179 . "\n]\n" );
180 }
181
182 if ( ${$text} =~ /\G\z/gc ) {
183 $self->_event_for_text_buffer();
184 last;
185 }
186
187 my @look_for = $self->_possible_span_matches();
188
189 $self->_debug_look_for(@look_for);
190
191 for my $span (@look_for) {
192 my ( $markup, @args ) = ref $span ? @{$span} : $span;
193
194 my $meth = '_match_' . $markup;
195
196 $self->$meth( $text, @args )
197 and next PARSE;
198 }
199
200 $self->_match_plain_text($text);
201 }
202}
203
204sub _possible_span_matches {
205 my $self = shift;
206
207 if ( my $event = $self->_open_start_event_for_span('code') ) {
208 return [ 'code_end', $event->delimiter() ];
209 }
210
211 my @look_for = 'escape';
212
213 push @look_for, $self->_look_for_strong_and_emphasis();
214
215 push @look_for, 'code_start';
216
217 unless ( $self->_open_start_event_for_span('link') ) {
218 push @look_for, qw( auto_link link image );
219 }
220
221 push @look_for, 'html_comment', 'html_tag', 'html_entity';
222
223 return @look_for;
224}
225
226sub _look_for_strong_and_emphasis {
227 my $self = shift;
228
229 my %start;
230 $start{strong} = $self->_open_start_event_for_span('strong');
231 $start{emphasis} = $self->_open_start_event_for_span('emphasis');
232
233 # If we are in both, we need to try to end the most recent one first.
234 if ( $start{strong} && $start{emphasis} ) {
235 my $last_saw;
236 for my $event ( $self->_pending_events() ) {
237 if ( $event->event_name() eq 'start_strong' ) {
238 $last_saw = 'strong';
239 }
240 elsif ( $event->event_name() eq 'start_emphasis' ) {
241 $last_saw = 'emphasis';
242 }
243 }
244
245 my @order
246 = $last_saw eq 'strong'
247 ? qw( strong emphasis )
248 : qw( emphasis strong );
249
250 return
251 map { [ $_ . '_end', $start{$_}->delimiter() ] }
252 @order;
253 }
254 elsif ( $start{emphasis} ) {
255 return ( 'strong_start',
256 [ 'emphasis_end', $start{emphasis}->delimiter() ] );
257 }
258 elsif ( $start{strong} ) {
259 return (
260 [ 'strong_end', $start{strong}->delimiter() ],
261 'emphasis_start'
262 );
263 }
264
265 # We look for strong first since it's a longer version of emphasis (we
266 # need to try to match ** before *).
267 return ( 'strong_start', 'emphasis_start' );
268}
269
270sub _open_start_event_for_span {
271 my $self = shift;
272 my $type = shift;
273
274 my $in;
275 for my $event ( $self->_pending_events() ) {
276 $in = $event
277 if $event->event_name eq 'start_' . $type;
278
279 undef $in
280 if $event->event_name eq 'end_' . $type;
281 }
282
283 return $in;
284}
285
286sub _build_escapable_chars {
287 return [ qw( \ ` * _ { } [ ] ( ) + - . ! < > ), '#' ];
288}
289
290sub _build_escape_re {
291 my $self = shift;
292
293 my $chars = join q{}, uniq( @{ $self->_escapable_chars() } );
294
295 return qr/\\([\Q$chars\E])/;
296}
297
298sub _match_escape {
299 my $self = shift;
300 my $text = shift;
301
302 my $escape_re = $self->_escape_re();
303
304 return unless ${$text} =~ / \G
305 ($escape_re)
306 /xgc;
307
308 $self->_print_debug( "Interpreting as escaped character\n\n[$1]\n" )
309 if $self->debug();
310
311 $self->_save_span_text($2);
312
313 return 1;
314}
315
316sub _match_strong_start {
317 my $self = shift;
318 my $text = shift;
319
320 my ($delim) = $self->_match_delimiter_start( $text, qr/(?:\*\*|__)/ )
321 or return;
322
323 my $event = $self->_make_event( StartStrong => delimiter => $delim );
324
325 $self->_markup_event($event);
326
327 return 1;
328}
329
330sub _match_strong_end {
331 my $self = shift;
332 my $text = shift;
333 my $delim = shift;
334
335 $self->_match_delimiter_end( $text, qr/\Q$delim\E/ )
336 or return;
337
338 my $event = $self->_make_event( EndStrong => delimiter => $delim );
339
340 $self->_markup_event($event);
341
342 return 1;
343}
344
345sub _match_emphasis_start {
346 my $self = shift;
347 my $text = shift;
348
349 my ($delim) = $self->_match_delimiter_start( $text, qr/(?:\*|_)/ )
350 or return;
351
352 my $event = $self->_make_event( StartEmphasis => delimiter => $delim );
353
354 $self->_markup_event($event);
355
356 return 1;
357}
358
359sub _match_emphasis_end {
360 my $self = shift;
361 my $text = shift;
362 my $delim = shift;
363
364 $self->_match_delimiter_end( $text, qr/\Q$delim\E/ )
365 or return;
366
367 my $event = $self->_make_event( EndEmphasis => delimiter => $delim );
368
369 $self->_markup_event($event);
370
371 return 1;
372}
373
374sub _match_code_start {
375 my $self = shift;
376 my $text = shift;
377
378 my ($delim) = $self->_match_delimiter_start( $text, qr/\`+\p{SpaceSeparator}*/ )
379 or return;
380
381 $delim =~ s/\p{SpaceSeparator}*$//;
382
383 my $event = $self->_make_event( StartCode => delimiter => $delim );
384
385 $self->_markup_event($event);
386
387 return 1;
388}
389
390sub _match_code_end {
391 my $self = shift;
392 my $text = shift;
393 my $delim = shift;
394
395 $self->_match_delimiter_end( $text, qr/\p{SpaceSeparator}*\Q$delim/ )
396 or return;
397
398 my $event = $self->_make_event( EndCode => delimiter => $delim );
399
400 $self->_markup_event($event);
401
402 return 1;
403}
404
405sub _match_delimiter_start {
406 my $self = shift;
407 my $text = shift;
408 my $delim = shift;
409
410 return unless ${$text} =~ / \G ($delim)/xgc;
411
412 return $1;
413}
414
415sub _match_delimiter_end {
416 my $self = shift;
417 my $text = shift;
418 my $delim = shift;
419
420 return unless ${$text} =~ /\G $delim /xgc;
421
422 return 1;
423}
424
425sub _match_auto_link {
426 my $self = shift;
427 my $text = shift;
428
429 return unless ${$text} =~ /\G <( (?:https?|mailto|ftp): [^>]+ ) >/xgc;
430
431 my $link = $self->_make_event( AutoLink => uri => $1 );
432
433 $self->_markup_event($link);
434
435 return 1;
436}
437
438# Stolen from Text::Markdown
43912µsmy $nested_brackets;
440136µs113µs$nested_brackets = qr{
441 (?> # Atomic matching
442 [^\[\]]+ # Anything other than brackets
443 |
444 \[
445 (??{ $nested_brackets }) # Recursive set of nested brackets
446 \]
447 )*
448}x;
449
450# Also stolen from Text::Markdown
45112µsmy $nested_parens;
452120µs17µs$nested_parens = qr{
453 (?> # Atomic matching
454 [^()]+ # Anything other than parens
455 |
456 \(
457 (??{ $nested_parens }) # Recursive set of nested parens
458 \)
459 )*
460}x;
461
462sub _match_link {
463 my $self = shift;
464 my $text = shift;
465
466 my $pos = pos ${$text} || 0;
467
468 # For some inexplicable reason, this regex needs to be recreated each time
469 # the method is called or $nested_brackets && $nested_parens are
470 # undef. Presumably this has something to do with using it in a
471 # subroutine's lexical scope (resetting the stack on each invocation?)
472 return unless
473 ${$text} =~ / \G
474 \[ ($nested_brackets) \] # link or alt text
475 (?:
476 \( ($nested_parens) \)
477 |
478 \s*
479 \[ ( [^]]* ) \] # an id (can be empty)
480 )? # with no id or explicit uri, use text as id
481 /xgc;
482
483 my ( $link_text, $attr ) =
484 $self->_link_match_results( $1, $2, $3 );
485
486 unless ( defined $attr->{uri} ) {
487 pos ${$text} = $pos
488 if defined $pos;
489
490 return;
491 }
492
493 my $start = $self->_make_event( StartLink => %{$attr} );
494
495 $self->_markup_event($start);
496
497 $self->_parse_text( \$link_text );
498
499 my $end = $self->_make_event('EndLink');
500
501 $self->_markup_event($end);
502
503 return 1;
504}
505
506sub _match_image {
507 my $self = shift;
508 my $text = shift;
509
510 my $pos = pos ${$text} || 0;
511
512 return unless
513 ${$text} =~ / \G
514 !
515 \[ ($nested_brackets) \] # link or alt text
516 (?:
517 \( ($nested_parens) \)
518 |
519 \s*
520 \[ ( [^]]* ) \] # an id (can be empty)
521 )? # with no id or explicit uri, use text as id
522 /xgc;
523
524 my ( $alt_text, $attr ) =
525 $self->_link_match_results( $1, $2, $3 );
526
527 unless ( defined $attr->{uri} ) {
528 pos ${$text} = $pos
529 if defined $pos;
530
531 return;
532 }
533
534 $attr->{alt_text} = $alt_text;
535
536 my $image = $self->_make_event( Image => %{$attr} );
537
538 $self->_markup_event($image);
539
540 return 1;
541}
542
543sub _link_match_results {
544 my $self = shift;
545 my $text = shift;
546 my $uri_and_title = shift;
547 my $id = shift;
548
549 my %attr;
550 if ( defined $uri_and_title ) {
551 my ( $uri, $title ) = $self->_parse_uri_and_title($uri_and_title);
552
553 $attr{uri} = $uri;
554 $attr{title} = $title
555 if defined $title;
556 }
557 else {
558 unless ( defined $id && length $id ) {
559 $id = $text;
560 $attr{is_implicit_id} = 1;
561 }
562
563 $id =~ s/\s+/ /g;
564
565 my $link = $self->_get_link_by_id($id) || [];
566
567 $attr{uri} = $link->[0];
568 $attr{title} = $link->[1]
569 if defined $link->[1];
570 $attr{id} = $id;
571 }
572
573 return ( $text, \%attr );
574}
575
576sub _match_html_comment {
577 my $self = shift;
578 my $text = shift;
579
580 return unless ${$text} =~ / \G
581 $HTMLComment
582 /xgcs;
583
584 my $comment = $1;
585
586 $self->_detab_text(\$comment);
587
588 my $event = $self->_make_event( HTMLComment => text => $comment );
589
590 $self->_markup_event($event);
591
592 return 1;
593}
594
595171µsmy %InlineTags = map { $_ => 1 }
596 qw( area base basefont br col frame hr img input link meta param );
597
598sub _match_html_tag {
599 my $self = shift;
600 my $text = shift;
601
602 return unless ${$text} =~ /\G (< [^>]+ >)/xgc;
603
604 my $tag = $1;
605
606 my $event;
607 if ( $tag =~ m{^</(\w+)>$} ) {
608 $event = $self->_make_event( EndHTMLTag => tag => $1 );
609 }
610 else {
611 $tag =~ s/^<|>$//g;
612
613 my ( $name, $attr ) = split /\s+/, $tag, 2;
614
615 $attr =~ s{/\s*$}{}
616 if defined $attr;
617
618 my %attr;
619 if ( defined $attr && $attr =~ /\S/ ) {
620 for my $attr ( split /\s+/, $attr ) {
621 if ( $attr =~ /=/ ) {
622 my ( $name, $val ) = split /=/, $attr;
623
624 $val =~ s/^([\"\'])(.+)\1$/$2/g;
625
626 $attr{$name} = $val;
627 }
628 else {
629 # A value-less attribute like in
630 # <option value="1" selected>
631 $attr{$name} = undef;
632 }
633 }
634 }
635
636 if ( $InlineTags{$name} ) {
637 $event = $self->_make_event(
638 HTMLTag => (
639 tag => $name,
640 attributes => \%attr,
641 ),
642 );
643 }
644 else {
645 $event = $self->_make_event(
646 StartHTMLTag => (
647 tag => $name,
648 attributes => \%attr,
649 ),
650 );
651 }
652 }
653
654 $self->_markup_event($event);
655
656 return 1;
657}
658
659sub _match_html_entity {
660 my $self = shift;
661 my $text = shift;
662
663 return unless ${$text} =~ / \G
664 &(\S+);
665 /xgcs;
666
667 my $event = $self->_make_event( HTMLEntity => entity => $1 );
668
669 $self->_markup_event($event);
670
671 return 1;
672}
673
674sub _match_plain_text {
675 my $self = shift;
676 my $text = shift;
677
678 my $escape_re = $self->_escape_re();
679
680 # Note that we're careful not to consume any of the characters marking the
681 # (possible) end of the plain text. If those things turn out to _not_ be
682 # markup, we'll get them on the next pass, because we always match at
683 # least one character, so we should never get stuck in a loop.
684 return unless
685 ${$text} =~ /\G
686 ( .+? ) # at least one character followed by ...
687 (?=
688 $escape_re
689 |
690 \* # possible span markup
691 |
692 _
693 |
694 \p{SpaceSeparator}* \`
695 |
696 !?\[ # possible image or link
697 |
698 < [^>]+ > # an HTML tag
699 |
700 &\S+; # an HTML entity
701 |
702 \z # or the end of the string
703 )
704 /xgcs;
705
706 $self->_print_debug( "Interpreting as plain text\n\n[$1]\n" )
707 if $self->debug();
708
709 $self->_save_span_text($1);
710
711 return 1;
712}
713
714sub _markup_event {
715 my $self = shift;
716 my $event = shift;
717
718 $self->_event_for_text_buffer();
719
720 if ( $self->debug() ) {
721 my $msg = 'Found markup: ' . $event->event_name();
722
723 if ( $event->can('delimiter') ) {
724 $msg .= ' - delimiter: [' . $event->delimiter() . ']';
725 }
726
727 $msg .= "\n";
728
729 $self->_print_debug($msg);
730 }
731
732 $self->_add_pending_event($event);
733
734 $self->_convert_invalid_start_events_to_text()
735 if $event->is_end();
736}
737
738sub _event_for_text_buffer {
739 my $self = shift;
740
741 return unless $self->_has_span_text_buffer();
742
743 my $text = $self->_span_text_buffer();
744
745 $self->_detab_text(\$text);
746
747 my $event = $self->_make_event( Text => text => $text );
748
749 $self->_add_pending_event($event);
750
751 $self->_clear_span_text_buffer();
752}
753
754sub _convert_invalid_start_events_to_text {
755 my $self = shift;
756 my $is_done = shift;
757
758 # We want to operate directly on the reference so we can convert
759 # individual events in place
760 my $events = $self->__pending_events();
761
762 my @starts;
763EVENT:
764 for my $i ( 0 .. $#{$events} ) {
765 my $event = $events->[$i];
766
767 next unless $event->does('Markdent::Role::BalancedEvent');
768
769 if ( $event->is_start() ) {
770 push @starts, [ $i, $event ];
771 }
772 elsif ( $event->is_end() ) {
773 while ( my $start = pop @starts ) {
774 next EVENT
775 if $event->balances_event( $start->[1] );
776
777 $events->[ $start->[0] ]
778 = $self->_convert_start_event_to_text( $start->[1] );
779 }
780 }
781 }
782
783 return unless $is_done;
784
785 for my $start (@starts) {
786 $events->[ $start->[0] ] = $self->_convert_start_event_to_text( $start->[1] );
787 }
788}
789
790sub _convert_start_event_to_text {
791 my $self = shift;
792 my $event = shift;
793
794 if ( $self->debug() ) {
795 my $msg = 'Found bad start event for ' . $event->name();
796
797 if ( $event->can('delimiter') ) {
798 $msg .= q{ with "} . $event->delimiter() . q{" as the delimiter};
799 }
800
801 $msg .= "\n";
802
803 $self->_print_debug($msg);
804 }
805
806 return $self->_make_event(
807 Text => (
808 text => $event->as_text(),
809 _converted_from => $event->event_name(),
810 )
811 );
812}
813
814sub _merge_consecutive_text_events {
815 my $self = shift;
816
817 my $events = $self->__pending_events();
818
819 my $merge_start;
820
821 my @to_merge;
822 for my $i ( 0 .. $#{$events} ) {
823 my $event = $events->[$i];
824
825 if ( $event->event_name() eq 'text' ) {
826 $merge_start = $i
827 unless defined $merge_start;
828 }
829 else {
830 push @to_merge, [ $merge_start, $i - 1 ]
831 if defined $merge_start && $i - 1 > $merge_start;
832
833 undef $merge_start;
834 }
835 }
836
837 # If $merge_start is still defined, then the last event was a text event
838 # which may need to be merged.
839 push @to_merge, [ $merge_start, $#{$events} ]
840 if defined $merge_start && $#{$events} > $merge_start;
841
842 my $already_merged = 0;
843 for my $pair (@to_merge) {
844 $pair->[0] -= $already_merged;
845 $pair->[1] -= $already_merged;
846
847 $self->_splice_merged_text_event(
848 $events,
849 @{$pair},
850 );
851
852 $already_merged += $pair->[1] - $pair->[0];
853 }
854}
855
856sub _splice_merged_text_event {
857 my $self = shift;
858 my $events = shift;
859 my $start = shift;
860 my $end = shift;
861
862 my @to_merge = map { $_->text() } @{$events}[ $start .. $end ];
863
864 $self->_print_debug( "Merging consecutive text events ($start-$end) for: \n"
865 . ( join q{}, map {" - [$_]\n"} @to_merge ) )
866 if $self->debug();
867
868 my $merged_text = join q{}, @to_merge;
869
870 my $event = $self->_make_event(
871 Text => (
872 text => $merged_text,
873 _merged_from => \@to_merge,
874 ),
875 );
876
877 splice @{$events}, $start, ( $end - $start ) + 1, $event;
878}
879
880sub _debug_pending_events {
881 my $self = shift;
882 my $desc = shift;
883
884 return unless $self->debug();
885
886 my $msg = "Pending event stream $desc:\n";
887
888 for my $event ( $self->_pending_events() ) {
889 $msg .= $event->debug_dump() . "\n";
890 }
891
892 $self->_print_debug($msg);
893}
894
895127µs221.4ms__PACKAGE__->meta()->make_immutable();
# spent 21.3ms making 1 call to Class::MOP::Class::make_immutable # spent 104µs making 1 call to Markdent::Dialect::Standard::SpanParser::meta
896
8971219µs1;
898
899# ABSTRACT: Span parser for standard Markdown
900
- -
903=pod
904
- -
9591128µs140.5ms__END__
 
# spent 4.10ms within Class::MOP::Method::Generated::CORE:match which was called 647 times, avg 6µs/call: # 647 times (4.10ms+0s) by Class::MOP::Method::Generated::_eval_closure at line 37 of Class/MOP/Method/Generated.pm, avg 6µs/call
sub Class::MOP::Method::Generated::CORE:match; # opcode
# spent 20µs within Markdent::Dialect::Standard::SpanParser::CORE:qr which was called 2 times, avg 10µs/call: # once (13µs+0s) by Markdent::Parser::BEGIN@11 at line 440 # once (7µs+0s) by Markdent::Parser::BEGIN@11 at line 452
sub Markdent::Dialect::Standard::SpanParser::CORE:qr; # opcode