File Coverage

blib/lib/Regexp/Common/Markdown.pm
Criterion Covered Total %
statement 49 49 100.0
branch n/a
condition n/a
subroutine 9 9 100.0
pod n/a
total 58 58 100.0


line stmt bran cond sub pod time code
1             ##----------------------------------------------------------------------------
2             ## Markdown Common Regular Expressions - ~/lib/Regexp/Common/Markdown.pm
3             ## Version v0.1.8
4             ## Copyright(c) 2025 DEGUEST Pte. Ltd.
5             ## Author: Jacques Deguest
6             ## Created 2020/08/01
7             ## Modified 2025/10/24
8             ## All rights reserved
9             ##
10             ## This program is free software; you can redistribute it and/or modify it
11             ## under the same terms as Perl itself.
12             ##----------------------------------------------------------------------------
13             package Regexp::Common::Markdown;
14             BEGIN
15             {
16 27     27   3652175 use strict;
  27         61  
  27         1323  
17 27     27   187 use warnings;
  27         210  
  27         1860  
18 27     27   180 use warnings::register;
  27         53  
  27         2070  
19 27     27   15832 use Regexp::Common qw( pattern );
  27         162809  
  27         176  
20 27     27   5307752 use Regexp::Common qw( URI );
  27         65  
  27         137  
21 27     27   5340 use Regexp::Common qw( Email::Address );
  27         59  
  27         129  
22 27     27   462099 use Regexp::Common::URI::RFC2396 qw /$host $port $path_segments $query/;
  27         127  
  27         5001  
23             ## We use URI::tel instead of Regexp::Common::tel because its regular expression is antiquated
24             ## URI::tel uses the latest rfc3966
25 27     27   25688 use URI::tel;
  27         615182  
  27         148214  
26             # no warnings qw( experimental::vlb );
27 27     27   182 our $VERSION = 'v0.1.8';
28              
29 27         147 our $SPACE_RE = qr/[[:blank:]\h]/;
30             ## Including vertical space like new lines
31 27         107 our $SPACE_EXTENDED_RE = qr/[[:blank:]\h\v]/;
32             # Guards to keep other patterns from firing inside code
33 27         83 our $MD_SKIP_FENCED = qr/
34             (?:
35             (?<=\n) | \A # start of line or start of string
36             )
37             [ ]{0,3} # up to 3 leading spaces
38             (?`{3,}|~{3,})[^\n]*\n # opening fence + optional info string
39             (?:
40             (?!^[ ]{0,3}\k[ \t]*\r?(?:\n|\z)) # not the closing fence line
41             [^\n]*+(?:\n|\z) # consume exactly one full line
42             )*
43             [ ]{0,3}\k[ \t]*\r?(?:\n|\z) # closing fence
44             (*SKIP)(*F)
45             /xms;
46              
47 27         77 our $MD_SKIP_INDENTED = qr/
48             (?:(?<=\n)|\A)
49             (?:
50             (?:[ ]{4}|\t).*(?:\n|\z)
51             )+
52             (*SKIP)(*F)
53             /xms;
54              
55             # `code` and `` code with ` inside ``
56 27         84 our $MD_SKIP_INLINE_CODE = qr/
57             (?
58             (`+)(?!`)
59             (?: .*? )
60             \1(?!`)
61             (*SKIP)(*F)
62             /xms;
63              
64 27         8203 our $ATTRIBUTE_RE = qr/
65             [\w\-]+
66             $SPACE_EXTENDED_RE*
67             =
68             $SPACE_EXTENDED_RE*
69             (?:
70             \"[^\"]+\" # attribute="value"
71             |
72             \'[^\']+\' # attribute='value'
73             |
74             \S+ # attribute=value
75             )
76             $SPACE_EXTENDED_RE*
77             /x;
78 27         142 our $LIST_TYPE_ORDERED = qr/(?\d+[\.])/;
79              
80 27         77 our $LIST_TYPE_UNORDERED_MINUS = qr/
81             (?
82             [\-]
83             (?!
84             (?:
85             [ ]?[\-][ ]?
86             ){2,}
87             )
88             )
89             /x;
90              
91 27         75 our $LIST_TYPE_UNORDERED_PLUS = qr/(?[\+])/;
92              
93 27         105 our $LIST_TYPE_UNORDERED_STAR = qr/
94             (?
95             [\*]
96             (?! # Make sure not to catch horizontal lines marked with stars
97             (?:
98             [ ]?[\*][ ]?
99             ){2,}
100             )
101             )
102             /x;
103              
104 27         2042 our $LIST_TYPE_UNORDERED = qr/
105             $LIST_TYPE_UNORDERED_STAR
106             |
107             $LIST_TYPE_UNORDERED_MINUS
108             |
109             $LIST_TYPE_UNORDERED_PLUS
110             /x;
111              
112             # Taken from Markdown original author, John Gruber's original regular expression
113             # See to see it in action
114 27         8566 our $LIST_ALL = qr/
115             (? # whole list
116             (? # list prefix and type
117             [\s]{0,3}
118             (? # Any of posible list prefix, but that aviod atching also horizontal rules
119             (?:
120             (?(?&list_unordered_star))
121             |
122             (?(?&list_unordered_minus))
123             |
124             (?(?&list_unordered_plus))
125             |
126             (?(?&list_ordered))
127             )
128             )
129             [ \t]+
130             )
131             (?(?s:.+?)) # list content
132             (?
133             \z
134             |
135             \n{2,}
136             (?=\S)
137             (?! # Negative lookahead for another list item marker
138             [ \t]*
139             (?
140             (?:
141             (?(?&list_unordered_star))
142             |
143             (?(?&list_unordered_minus))
144             |
145             (?(?&list_unordered_plus))
146             |
147             (?(?&list_ordered))
148             )
149             )
150             [ \t]+
151             )
152             )
153             )
154             (?(DEFINE) # Definition block for recursive pattern matching
155             $LIST_TYPE_UNORDERED_STAR
156             $LIST_TYPE_UNORDERED_MINUS
157             $LIST_TYPE_UNORDERED_PLUS
158             $LIST_TYPE_ORDERED
159             )
160             /mx;
161              
162             # NOTE: global variables to build URI regular expressions compatible with IDN (rfc3986)
163 27         3748 our $HTTP_RFC2396_HOST = qr/$host/;
164             # RFC 3986 / IDN host helpers
165             # Accept "." and the IDNA dot equivalents
166 27         175 our $IDN_DOT = qr/[.\x{3002}\x{FF0E}\x{FF61}]/;
167             # ACE punycode prefix
168 27         88 our $ACE = qr/xn--/i;
169              
170             # RFC 3986: IPv6 literal inside [ ... ]
171             # (very permissive; we only need the bracketed form to avoid false positives)
172 27         207 our $IP_LITERAL = qr/\[(?:[0-9A-Fa-f:.]+)\]/;
173              
174             # IDN label (≤63 chars) with ACE or Unicode 3rd–4th hyphen rule
175 27         14203 our $IDN_U_LABEL = qr/
176             (?: # punycode label… (xn--)
177             $ACE [\p{L}\p{N}\p{M}\p{Pc}-]{1,59}
178             |
179             (?! [\p{L}\p{N}]{2}-- ) # forbid "--" at pos 3–4 unless ACE
180             [\p{L}\p{N}] # first
181             [\p{L}\p{N}\p{M}\p{Pc}-]{0,61} # middle
182             (?
183             )
184             /ux;
185              
186             # IDN hostname: one or more labels separated by IDNA dots (atomic to reduce backtracking)
187 27         23010 our $IDN_HOST = qr/(?>$IDN_U_LABEL(?:$IDN_DOT$IDN_U_LABEL)*)/ux;
188             # our $IDN_HOST = qr/(?>$IDN_U_LABEL(?:$IDN_DOT$IDN_U_LABEL)* $IDN_DOT?)/ux;
189              
190             # Unicode-friendly “tail” for path/query in inline links (non-<…> case).
191             # RFC 3986 pchar allows pct-enc; for Markdown it is usually safer to “stop at obvious enders”.
192 27         321 our $UNICODE_PATHQ = qr{ [^<>"'\s\)]* }ux;
193              
194             # RFC3986/IDN superset host: ASCII host-or-IPv4 (previous one based on RFC2396), or IDN host, or IPv6 literal
195 27         27358 my $HTTP_RFC3986_HOST = qr/
196             (?:
197             # (1) previous ASCII host-or-IPv4 branch based on RFC2396:
198             $HTTP_RFC2396_HOST
199             |
200             # (2) New: Unicode IDN hostname
201             $IDN_HOST
202             |
203             # (3) New: IPv6 literal in brackets
204             $IP_LITERAL
205             )
206             /x;
207              
208             # Full HTTP(S) URI using RFC2396 path/query (strict ASCII), suitable for angle-bracket autolinks.
209 27         32361 our $HTTP_RFC3986_URI = qr{
210             (?:
211             (?:http|https)://
212             $HTTP_RFC3986_HOST
213             (?:: $port )?
214             (?: /
215             (?:
216             $path_segments
217             (?: \? $query )?
218             )
219             )?
220             )
221             }x;
222              
223 27         29043 our $HTTP_R3986_URI_INLINE = qr{
224             (?:
225             (?:http|https)://
226             $HTTP_RFC3986_HOST
227             (?:: $port )?
228             (?: / $UNICODE_PATHQ )?
229             (?: \? $UNICODE_PATHQ )?
230             )
231             }x;
232              
233 27         88593 our $REGEXP =
234             {
235             # NOTE: Bold
236             # https://regex101.com/r/Jp2Kos/3
237             bold => qr/
238             (?
239             (?
240             (?\*{2}|\_{2}) # Emphasis type: * or _
241             (?= # followed by non-space
242             (?:
243             (?:[_*`\$\\]) # avoid punctuation except those
244             |
245             (?![[:punct:]])
246             )
247             \S)
248             (?.+?[*_]*) # enclosed text
249             (?<=\S) # making sure preceding stuff was a non-space
250             \g{bold_type} # Balanced closing tag
251             )
252             /x,
253              
254             # NOTE: blockquote
255             # Code borrowed from original Markdown author: John Gruber
256             # https://regex101.com/r/TdKq0K/1
257             bquote => qr/
258             (? # Wrap whole match in $1
259             (?>
260             ^[ \t]*>[ \t]? # '>' at the start of a line
261             .+\n # rest of the first line
262             (?.+\n)* # subsequent consecutive lines
263             \n* # blanks
264             )+
265             )
266             /xm,
267              
268             # NOTE: code block
269             # ```
270             # Some code
271             # ```
272             # https://regex101.com/r/M6W99K/7
273             # 2025-10-21: Updated
274             # - Allows optional info string after the opening fence (ignored in base mode)
275             # - Closes with the same fence run length & char
276             # - Works with up to 3 leading spaces per CommonMark
277             code_block => qr/
278             (?:(?<=\n)|(?<=\A)) # Necessarily at the begining of a new line or start of string
279             (?
280             [ ]{0,3} # Possibly up to 3 leading spaces
281             (? # CAPTURE ONLY the fence run
282             \`{3,}
283             )
284             [ \t]* [^\n]* # optional info string (ignored in base)
285             \n
286             (?.*?) # enclosed content
287             \n
288             [ ]{0,3} # up to 3 leading spaces on the closer line
289             (?
290             \g{code_start} # close with the SAME fence run
291             (?!`) # not followed by a backtick
292             [ \t]* # optional trailing spaces
293             \n # final newline (keep strict like your original)
294             )
295             /xms,
296              
297             # NOTE: code line
298             # Marked by left hand indentation
299             # https://regex101.com/r/toEboU/3
300             code_line => qr/
301             (?:(?<=^\n)|(?<=\A)) # Starting at beginning of string or with 2 new lines
302             (?
303             (?:
304             (? # Lines must start with a tab or a tab-width of spaces
305             [ ]{4}
306             |
307             \t
308             )
309             (?.*\n+) # with some content, possibly nothing followed by a new line
310             )+
311             )
312             (?
313             (?=^[ ]{0,4}\S) # Lookahead for non-space at line-start
314             |
315             \Z # or end of doc
316             )
317             /xm,
318              
319             # NOTE: code span
320             # \x{0060} is ` in unicode; see perl -le 'printf "\\x%x", ord( "`" )'
321             # Updated on 2025-10-19 to change the 'code_start', and 'code_content'
322             # from (?.+?) to (?:[^`]|`(?!\g{code_start}))*?
323             # Updated on 2025-10-21 to guard from capturing fenced blocks and indented codes
324             code_span => qr{
325             (?: $MD_SKIP_FENCED | $MD_SKIP_INDENTED )
326             |
327             (?
328             (?
329             (?!^[ ]{0,3}`{3,}[^\n]*\n) # not a fence opener (needs /m)
330             (?`+) # opening, capture N backticks
331             (? # allow single ` inside when using more ticks
332             (?:[^`]|`(?!\g{code_start}))*?
333             )
334             (?
335             \g{code_start} # balanced closing ticks
336             )
337             }xms,
338              
339             # NOTE: emphasis
340             # https://regex101.com/r/eDb6RN/5
341             em => qr/
342             (?
343             (?
344             (?\*|\_) # Emphasis type: * or _
345             (?=\S) # followed by non-space
346             (?.+?) # enclosed text
347             (?<=\S) # making sure preceding stuff was a non-space
348             (?
349             \g{em_type} # Balanced closing tag
350             )
351             /x,
352              
353             # NOTE: headers
354             # Headers: #, ##, ###, ####, #####, ###### become h1..6
355             # atx-style headers:
356             # # Header 1
357             # ## Header 2
358             # ## Header 2 with closing hashes ##
359             # ...
360             # ###### Header 6
361             # https://regex101.com/r/9uQwBk/6
362             # 2025-10-19: Added safeguards $MD_SKIP_FENCED and $MD_SKIP_INDENTED
363             # other change avoids “#######” lines with no content and the occasional 7+ “#” oddity.
364             header => qr/
365             (?: $MD_SKIP_FENCED | $MD_SKIP_INDENTED )
366             |
367             (?
368             ^
369             (?
370             (?\#{1,6}) # 1..6 # only
371             [ \t]* # Possibly followed by some spaces or tabs
372             (?![ \t]*$) # must have some content
373             (?.*?) # Possibly followed by some spaces or tabs or some dashes (don't need to match the opening ones)
374             [ \t\#]* # optional closing hashes or spaces
375             \n
376             )
377             /mx,
378              
379             # NOTE: setext-style headers
380             # Setext-style headers:
381             # Header 1
382             # ========
383             #
384             # Header 2
385             # --------
386             #
387             # This is to be on a single line of its own
388             # https://regex101.com/r/sQLEqz/4
389             # 2025-10-19: Added safeguards $MD_SKIP_FENCED and $MD_SKIP_INDENTED
390             header_line => qr/
391             (?: $MD_SKIP_FENCED | $MD_SKIP_INDENTED ) |
392             (?
393             ^
394             (?.+?) # Header content
395             [ \t]* # Possibly followed by spaces or tabs
396             \n # With then a new line
397             (?
398             (?={3,}|-{3,}) # underline, 3+ to avoid list dashes noise
399             [ \t]* # Possibly followed by spaces or tabs
400             \n # Terminated by a new line
401             )
402             /mx,
403              
404             # NOTE: html
405             # https://regex101.com/r/SH8ki3/4
406             # 2025-10-18: Added safeguards to exclude html embedded within code blocks
407             # 2025-10-24: Added the fence guard $MD_SKIP_FENCED
408             html => qr/
409             #---------------------------------------------------------
410             # 0) Indented-code guard
411             #---------------------------------------------------------
412             $MD_SKIP_INDENTED
413             |
414             #---------------------------------------------------------
415             # 1) Ignore fenced code blocks (```lang … ``` or ~~~ … ~~~)
416             # We match them first, then (*SKIP)(*F) so the engine
417             # jumps past the whole block and resumes searching after it.
418             #---------------------------------------------------------
419             $MD_SKIP_FENCED
420             |
421             #---------------------------------------------------------
422             # 2) The actual HTML matcher
423             #---------------------------------------------------------
424             (?:(?<=\n)|(?<=\A)) # Necessarily at the begining of a new line or start of string
425             (?[ ]{0,3})
426             (?
427             (?:
428             (?
429             <(?\S+)
430             [^\>]*
431             >\n*
432             )
433             (?.+?)
434             \n*
435             (?
436             (?() # If leading spaces were found
437             (?:
438             (?<=\n)\g{leading_space} # Either there is a symmetry in leading space for open and closing tag
439             |
440             (?:(?<=\S)[[:blank:]\h]*) # or the closing tag is on the same line with preceding data
441             )
442             |
443             (?=<[[:blank:]\h\v]*\/[[:blank:]\h\v]*\g{tag_name}[[:blank:]\h\v]*>) # No leading space, so we don't expect anything before the closing tag other than what has already been caught in the 'content'
444             )
445             <[[:blank:]\h\v]*\/[[:blank:]\h\v]*\g{tag_name}[[:blank:]\h\v]*>
446             )
447             [[:blank:]\h]*\n
448             )
449             |
450             (?:
451            
452             )
453             |
454             (?:
455             <
456             [[:blank:]\h\v]*
457             (?[a-zA-Z0-9][\w\-]+)
458             (?(?&tag_attr))*
459             [[:blank:]\h\v]*
460             \/?
461             [[:blank:]\h\v]*
462             >
463             )
464             )
465             (?(DEFINE)
466             (?
467             (?:
468             [[:blank:]\h]*
469             [\w\-]+
470             [[:blank:]\h]*
471             =
472             [^\"\'[:blank:]\h]+
473             [[:blank:]\h]*
474             )
475             |
476             (?:
477             [[:blank:]\h]*
478             [\w\-]+
479             [[:blank:]\h]*
480             =
481             [[:blank:]\h]*
482             (?["'])
483             (.*?)
484             \g{quote}
485             [[:blank:]\h]*
486             )
487             )
488             )
489             /xsm,
490              
491             # NOTE: image
492             # Basically same as link, except there is an exclamation mark (!) just before:
493             # Ex: ![alt text](url "optional title")
494             # https://regex101.com/r/z0yH2F/10
495             # 2025-10-19: Added safeguard $MD_SKIP_INLINE_CODE
496             img => qr/
497             $MD_SKIP_INLINE_CODE
498             |
499             (?
500             (?
501             \!\[(?.+?)\] # image alternative text, i.e. the text used when the image does not
502             (?:
503             (?:
504             [ ]? # possibly followed by some spaces
505             (?:\n[ ]*)? # and a new line with some space
506             \[(?.*?)\] # with the link id in brackets, but may be empty
507             )
508             |
509             (?:
510             (?:
511             \(
512             [ \t]*
513             <(? # link url within <>; or
514             .+?
515             )>
516             [ \t]* # possibly followed by some spaces or tabs
517             (?:
518             (?['"]) # Title is surrounded ether by double or single quotes
519             (?.*?) # actual title, but could be empty as in ""
520             \g{img_title_container} # make the sure enclosing mark balance
521             )?
522             [ \t]*
523             \)
524             )
525             |
526             (?:
527             \(
528             [ \t]*
529             (? # link url within <>; or
530             (?:((?!["']).)*+|.*)
531             )
532             [ \t]*
533             (?:
534             (?['"]) # Title is surrounded ether by double or single quotes
535             (?.*?) # actual title, but could be empty as in ""
536             \g{img_title_container} # make the sure enclosing mark balance
537             )?
538             [ \t]*
539             \)
540             )
541             )
542             )
543             )
544             /xm,
545              
546             # ^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$
547             # NOTE: line
548             # Horizontal line
549             # https://daringfireball.net/projects/markdown/syntax#hr
550             # https://regex101.com/r/Vlew4X/2
551             # 20215-10-19: group the repeated unit atomically.
552             line => qr/
553             ^ # At start of line
554             $SPACE_RE{0,2} # with up to 3 spaces before
555             (?
556             (?
557             (?:
558             $SPACE_RE?
559             (?\*|\-|\_) # asterisk, hyphen or underscore
560             $SPACE_RE? # possibly followed by spaces
561             ){3,} # 3 or more occurences
562             )
563             [ \t]*
564             $ # end of line or end of string
565             /mx,
566              
567             # NOTE: line break
568             # https://regex101.com/r/6VG46H/1
569             line_break => qr/
570             (?
571             [ ]{2,}\n
572             )
573             /mx,
574              
575             # NOTE: Link
576             # https://daringfireball.net/projects/markdown/syntax#link
577             # https://regex101.com/r/sGsOIv/10
578             # Links' id can be multiline, so we need the /s modifier
579             # 2025-10-19 Added safeguard $MD_SKIP_INLINE_CODE
580             link => qr/
581             $MD_SKIP_INLINE_CODE
582             |
583             (? # Check it was not escaped with a \
584             (?:(?:\\|\!)\[(*SKIP)(*FAIL)|\[) # Cannot be preceded by an anti-slash nor an exclamation mark (image)
585             (?
586             (?!\^) # Make sure, this is not confused with a footnote
587             (?>\\[\[\]]|[^\[\]])*+ # Link text
588             )
589             (?
590             \]
591             (?:
592             (?:
593             [ ]? # possibly followed by some spaces
594             (?:\n[ ]*)? # and a new line with some space
595             (?
596             (?!\^) # Make sure, this is not confused with a footnote
597             (?.*?)
598             (?
599             )
600             |
601             (?:
602             (?
603             [ \t]*
604             (?:
605             <(?.*?)> # link url within <>; or
606             |
607             (? # link url without <>
608             $HTTP_R3986_URI_INLINE
609             )
610             |
611             (?.*?)
612             )
613             [ \t]* # possibly followed by some spaces or tabs
614             (?:
615             (?['"]) # Title is surrounded ether by double or single quotes
616             (?.*?) # actual title, but could be empty as in ""
617             \g{link_title_container} # make sure the enclosing mark balance
618             )?
619             [ \t]*
620             (?
621             (?![[:blank:]\h]+["'][^"']+["'])
622             )
623             )
624             )
625             /xms,
626              
627             # NOTE: link auto
628             # https://regex101.com/r/bAUu1E/4/
629             # 2025-10-19: Added safeguard $MD_SKIP_INLINE_CODE
630             # Added labels 'link_idn_http', 'link_idn_https', 'link_v6_http', and 'link_v6_https'
631             link_auto => qr{
632             $MD_SKIP_INLINE_CODE
633             |
634             (?
635             (?
636             <
637             (?
638             (?$RE{URI}{HTTP}) # http
639             |
640             (?$RE{URI}{HTTP}{-scheme => 'https'}) # https
641             |
642             (?$RE{URI}{FTP}) # ftp
643             |
644             (?$URI::tel::TEL_URI) # tel
645             |
646             (?$RE{URI}{file}) # file
647             |
648             (?$RE{URI}{news}) # news
649             |
650             (?(?:mailto\:)?$RE{Email}{Address}) # email address: mailto:john@example.com or simply john@example.com
651             |
652             (? http:// $IDN_HOST (?::(?:$port))?(?:/(?:(?:$path_segments)(?:[?](?:$query))?))? ) # IDN http hosts
653             |
654             (? https:// $IDN_HOST (?::(?:$port))?(?:/(?:(?:$path_segments)(?:[?](?:$query))?))? ) # IDN https hosts
655             |
656             (? http:// $IP_LITERAL (?::(?:$port))?(?:/(?:(?:$path_segments)(?:[?](?:$query))?))? ) # IPv6 http hosts
657             |
658             (? https:// $IP_LITERAL (?::(?:$port))?(?:/(?:(?:$path_segments)(?:[?](?:$query))?))? ) # IPv6 https hosts
659             )
660             >
661             )
662             }x,
663              
664             # NOTE: link definition
665             # Definition
666             # https://daringfireball.net/projects/markdown/syntax#link
667             # https://regex101.com/r/edg2F7/3
668             link_def => qr/
669             ^[ ]{0,3} # Leading space up to 3
670             (?
671             (?
672             (?!\^) # Make sure, this is not confused with a footnote
673             (?.+?)
674             (?
675             [ \t]* # Possibly with some space before colon
676             \:
677             [ \t]*\n?[ \t]* # Possibly with some space after the colon, possibly with a new line in between?
678             (?:
679             <(?[^\>]+)> # link within <>
680             |
681             (?\S+) # or link without <>
682             )
683             (?:
684             (?:
685             [ \t]+ # Either some space or tabs
686             |
687             [ \t]*\n[ \t]* # or a new line surrounded by 0 or more spaces or tabs
688             )
689             (?:
690             (?:
691             (?['"]) # Title is surrounded ether by double or single quotes
692             (?.+?)
693             \g{link_title_container} # make the sure enclosing mark balance
694             )
695             | # or
696             \((?[^\)]+)\) # by parenthesis
697             )
698             )?
699             [ \t]* # Possibly ending with some trailing spaces or tab
700             )
701             (?:\n+|\Z) # terminated by a new line or end of file
702             /xm,
703              
704             # NOTE: link reference
705             # Link with reference to link definition id
706             # https://daringfireball.net/projects/markdown/syntax#link
707             # https://regex101.com/r/QmyfnH/1/
708             # The /s switch is required for link name spawning multiple lines: [Some\nlink][]
709             # 2025-10-19: Added safeguard $MD_SKIP_INLINE_CODE
710             link_ref => qr/
711             $MD_SKIP_INLINE_CODE
712             |
713             (?
714             (?.+?)(?
715             [ ]? # possibly followed by some spaces
716             (?:\n[ ]*)? # and a new line with some space
717             (?.*?)(?
718             )
719             /xms,
720              
721             # NOTE: list type ordered
722             # regular expression for list, ordered or unordered
723             list_type_ordered => $LIST_TYPE_ORDERED,
724              
725             # NOTE: list type unordered
726             list_type_unordered => $LIST_TYPE_UNORDERED,
727              
728             # NOTE: list all
729             # Taken from Markdown original author, John Gruber's original regular expression
730             list => $LIST_ALL,
731              
732             # NOTE: list first level
733             # https://regex101.com/r/RfhRVg/5
734             list_first_level => qr/
735             (?:(?<=^\n)|(?<=\A))
736             $LIST_ALL
737             /mx,
738              
739             # NOTE: list nth level
740             list_nth_level => qr/
741             ^
742             $LIST_ALL
743             /mx,
744              
745             # NOTE: list item
746             # Minor deviation from John Gruber's original regular expression
747             # Changed [ \t]+ to [ \t]* and .+? to .*? so that it catches empty list item, like:
748             # *
749             # * Something
750             # https://regex101.com/r/bulBCP/1/
751             list_item => qr/
752             (?
753             (?\n)? # leading line
754             (?^[ \t]*) # leading whitespace
755             (? # list marker
756             (?:
757             (?:
758             (?(?&list_unordered_star))
759             |
760             (?(?&list_unordered_minus))
761             |
762             (?(?&list_unordered_plus))
763             )
764             |
765             (?(?&list_ordered))
766             )
767             )
768             [ \t]*
769             (?(?s:.*?) # list item text
770             (\n{1,2}))
771             (?= \n*
772             (
773             \z
774             |
775             \g{li_lead_space}
776             (?
777             (?:
778             (?:
779             (?(?&list_unordered_star))
780             |
781             (?(?&list_unordered_minus))
782             |
783             (?(?&list_unordered_plus))
784             )
785             |
786             (?(?&list_ordered))
787             )
788             )
789             [ \t]*
790             )
791             )
792             )
793             (?(DEFINE) # Definition block for recursive pattern matching
794             $LIST_TYPE_UNORDERED_STAR
795             $LIST_TYPE_UNORDERED_MINUS
796             $LIST_TYPE_UNORDERED_PLUS
797             $LIST_TYPE_ORDERED
798             )
799             /xm,
800              
801             # NOTE: paragraph
802             # https://regexr.com/5929n
803             # https://regex101.com/r/0B3gR4/5
804             # 2025-10-19: updated to add safeguard, and improved 'para_content' to avoid swallowing definitions/footnotes/HTML starts.
805             paragraph => qr/
806             (?: $MD_SKIP_FENCED | $MD_SKIP_INDENTED )
807             |
808             (?:(?<=^\n)|(?<=\A)) # Needs 1 or more new lines or start of string
809             (?
810             (?[ ]{0,3}) # Possibly some leading spaces, but less than a tab worth
811             (?
812             (?:
813             (?! # some line content not starting with those exceptions
814             [[:blank:]\h]{0,3}
815             (?:
816             \*{1}[ \t]+ # a list
817             |
818             (?:\*(?:[ ]?\*){2,}) # a horizontal line
819             |
820             (?:\-*(?:[ ]?\-){2,}) # a horizontal line
821             |
822             (?:\_*(?:[ ]?\_){2,}) # a horizontal line
823             |
824             [>+-=\#] # bq, heading, setext line, etc.
825             |
826             \d+\. # ordered list
827             |
828             \`{3,} # code block (fenced)
829             |
830             \~{3,} # code block extended
831             |
832             \[ [^\]\n]+ \] [ \t]*: # link def [id]:
833             |
834             \[\^ [^\]\n]+ \] [ \t]*: # footnote def [^id]:
835             |
836             <[A-Za-z] # HTML block start
837             )
838             )
839             .+
840             (?!\n(?:[=-]+))
841             (?:\n|$)
842             )+
843             )
844             )
845             /mx
846             };
847              
848             # NOTE: extended regular expressions
849             # Extended regular expression
850 27         408137 our $REGEXP_EXT =
851             {
852             # NOTE: abbreviation
853             # Ex: *[HTML]: Hyper Text Markup Language
854             # This is similar, but different from definitions
855             # https://regex101.com/r/ztM2Pw/2/
856             ex_abbr => qr/
857             (?
858             (?
859             (?.+?)(?
860             [[:blank:]\h]*
861             \:
862             [[:blank:]\h]+
863             (?.*?)
864             (?:\n|\z)
865             )
866             /x,
867              
868             # NOTE: checkbox
869             # https://regex101.com/r/ezMwsv/1/
870             ex_checkbox => qr/
871             (?
872             [ ]?
873             \[(?X|[[:blank:]\h])\]
874             [ ]+
875             (?=\S+)
876             )
877             /xi,
878              
879             # NOTE: code block extended
880             # This is same as the regular code block, except this allows for a code class or a code definition with class, id, etc.
881             # https://regex101.com/r/Y9lPAz/10
882             # 2025-10-21: Updated
883             ex_code_block => qr/
884             (?:(?<=\n)|(?<=\A)) # Necessarily at the begining of a new line or start of string
885             (?
886             [ ]{0,3} # Possibly up to 3 leading spaces
887             (? # CAPTURE ONLY the fence run
888             (?[`]{3,}) # 3 code marks (backticks) or more
889             |
890             (?[~]{3,}) # or 3 code marks (tilde) or more
891             )
892             [ \t]*
893             (?:
894             (?: # .class[.class] {attrs}? OR .class only
895             (?(?&_code_class))
896             (?:
897             [ \t]*
898             \{ [[:blank:]\h]* (?(?&_code_attr)) [[:blank:]\h]* \}
899             )?
900             )
901             | # OR {attrs} only
902             \{ [[:blank:]\h]* (?(?&_code_attr)) [[:blank:]\h]* \}
903             )?
904             \n+
905             (?.*?) # enclosed content
906             \n+
907             [ ]{0,3} # Possibly up to 3 leading spaces on the closing line
908             \g{code_start} # balanced closing block marks (backticks or tildes)
909             [ \t]* # possibly followed by some space
910             (?:\n|\Z) # contrary to the vanilla version, we allow for no double line-break
911             )
912             (?(DEFINE)
913             (?<_code_class> [\w\-.]+)
914             (?<_code_attr> [^}]+)
915             )
916             /xms,
917              
918             # NOTE: footnote extended
919             # https://regex101.com/r/WuB1FR/2/
920             ex_footnote => qr/
921             (?
922             ^[ ]{0,3}
923             \[\^(?.+?)\][ ]?: # footnote id
924             [ ]*
925             \n? # maybe *one* newline
926             (? # footnote text (no blank lines allowed)
927             (?:
928             .+ # actual text
929             |
930             \n # newlines but
931             (?!\[.+?\][ ]?:\s) # negative lookahead for footnote or link definition marker.
932             (?!\n+[ ]{0,3}\S) # ensure line is not blank and followed
933             # by non-indented content
934             )*
935             )
936             )
937             /xm,
938              
939             # NOTE: footnote reference extended
940             # https://regex101.com/r/3eO7rJ/1/
941             ex_footnote_ref => qr/
942             (? # 3 possible patterns
943             (?:
944             \[\^(?.*?)\] # extended patterns with possibly null id
945             [[:blank:]\h]* # possibly some spaces
946             \((?.+?)\) # and some text in parenthesis
947             )
948             |
949             (?:
950             \[\^(?.+?)\] # regular footnote with a mandatory id
951             (?![[:blank:]\h]*\((?:.+?)\)) # but not followed by enclosing parenthesis
952             )
953             |
954             (?:
955             \^\[(?.+?)\] # inline footnote with auto-generated id à la pandoc
956             )
957             )
958             /xms,
959              
960             # NOTE: header extended
961             # atx-style headers:
962             # # Header 1
963             # ## Header 2
964             # ## Header 2 with closing hashes ##
965             # ...
966             # ###### Header 6
967             # ## Le Site ## {.main .shine #the-site lang=fr}
968             # Same as regular header + parameters insides curly braces in between
969             # https://regex101.com/r/GyzbR2/3
970             # 2025-10-19: Added safeguard
971             # Limited level to 6
972             # Must have some text in 'header_content'
973             ex_header => qr/
974             (?: $MD_SKIP_FENCED | $MD_SKIP_INDENTED )
975             |
976             (?
977             ^
978             (?
979             (?\#{1,6}) # one or more #
980             [ \t]* # Possibly followed by some spaces or tabs
981             (?![ \t]*\{) # Do not allow only attributes as “content”
982             (?.+?) # Header content enclosed
983             [ \t\#]* # Possibly followed by some spaces or tabs or some dashes (don't need to match the opening ones)
984             (?
985             \{
986             [[:blank:]\h]* # Possibly with some spaces
987             (?[^\}]*) # and attributes instead braces
988             \}
989             \n # Terminated by a new line
990             )
991             /xm,
992              
993             # NOTE: setext-style headers
994             # Setext-style headers:
995             # Header 1 {.main .shine #the-site lang=fr}
996             # ========
997             #
998             # Header 2 {.main .shine #the-site lang=fr}
999             # --------
1000             #
1001             # This is to be on a single line of its own
1002             # https://regex101.com/r/berfAR/4
1003             # 2025-10-19: Added safeguard
1004             # Added a limit to the number of header marker: up to 3
1005             ex_header_line => qr/
1006             (?: $MD_SKIP_FENCED | $MD_SKIP_INDENTED )
1007             |
1008             (?
1009             ^
1010             (?.+) # Header content
1011             [ \t]*
1012             (?
1013             \{
1014             [[:blank:]\h]* # Possibly with some spaces
1015             (?.+?) # and attributes instead braces
1016             [[:blank:]\h]*
1017             \}
1018             [ \t]* # Possibly followed by spaces or tabs
1019             \n # With then a new line
1020             (?
1021             (?={3,}|-{3,}) # Multiple = or -, up to 3
1022             [ \t]* # Possibly followed by spaces or tabs
1023             \n # Terminated by a new line
1024             )
1025             /mx,
1026              
1027             # NOTE: html extended
1028             # https://regex101.com/r/M6KCjp/3
1029             # 2025-10-24: Added indent and fence guard
1030             ex_html_markdown => qr/
1031             #---------------------------------------------------------
1032             # 0) Indented-code guard
1033             #---------------------------------------------------------
1034             $MD_SKIP_INDENTED
1035             |
1036             #---------------------------------------------------------
1037             # 1) Fenced-code guard (```...``` or ~~~...~~~)
1038             #---------------------------------------------------------
1039             $MD_SKIP_FENCED
1040             |
1041             #---------------------------------------------------------
1042             # 2) The actual extended HTML-with-markdown matcher
1043             #---------------------------------------------------------
1044             (?
1045             (?
1046             (?:\n|\A)
1047             (?
1048             (?[[:blank:]\h]*)
1049             <(?\S+)
1050             (?>
1051             [[:blank:]\h\v]+[\w\_]+(?:[[:blank:]\h]*\=[[:blank:]\h]*["'][^"']*['"])?
1052             )*
1053             [[:blank:]\h\v]+markdown[[:blank:]\h]*\=[[:blank:]\h]*(?["']?)1\g{quote}
1054             [^\>]*
1055             >\n*
1056             )
1057             (?.+?)
1058             \n*
1059             (?
1060             (?() # If leading spaces were found
1061             (?:
1062             (?<=\n)\g{leading_space} # Either there is a symmetry in leading space for open and closing tag
1063             |
1064             (?:(?<=\S)[[:blank:]\h]*) # or the closing tag is on the same line with preceding data
1065             )
1066             |
1067             (?=<\/\g{tag_name}>) # No leading space, so we don't expect anything before the closing tag other than what has already been caught in the 'content'
1068             )
1069             <\/\g{tag_name}>
1070             )
1071             [[:blank:]\h]*\n
1072             )
1073             |
1074             (?
1075             (?<=\S)
1076             (?
1077             (?[[:blank:]\h]*)
1078             <(?\S+)
1079             (?>
1080             [[:blank:]\h\v]+[\w\_]+(?:[[:blank:]\h]*\=[[:blank:]\h]*["'][^"']*['"])?
1081             )*
1082             [[:blank:]\h\v]+markdown[[:blank:]\h]*\=[[:blank:]\h]*(?["'])?1\g{quote}
1083             [^\>]*
1084             >
1085             )
1086             (?.+?)
1087             (?
1088             [[:blank:]\h]*
1089             <\/\g{tag_name}>
1090             )
1091             (?=\S|[[:blank:]\h\v]*)
1092             )
1093             )
1094             /xms,
1095              
1096             # NOTE: image extended
1097             # https://regex101.com/r/xetHV1/4
1098             # 2025-10-19: Added safeguard $MD_SKIP_INLINE_CODE
1099             ex_img => qr/
1100             $MD_SKIP_INLINE_CODE
1101             |
1102             (?
1103             (?
1104             \!\[(?.+?)\] # image alternative text, i.e. the text used when the image does not
1105             (?:
1106             (?:
1107             [ ]? # possibly followed by some spaces
1108             (?:\n[ ]*)? # and a new line with some space
1109             \[(?.*?)\] # with the link id in brackets, but may be empty
1110             )
1111             |
1112             (?:
1113             (?:
1114             \(
1115             [ \t]*
1116             <(? # link url within <>; or
1117             .+?
1118             )>
1119             [ \t]* # possibly followed by some spaces or tabs
1120             (?:
1121             (?['"]) # Title is surrounded ether by double or single quotes
1122             (?.*?) # actual title, but could be empty as in ""
1123             \g{img_title_container} # make the sure enclosing mark balance
1124             )?
1125             [ \t]*
1126             \)
1127             )
1128             |
1129             (?:
1130             \(
1131             [ \t]*
1132             (? # link url within <>; or
1133             (?:((?!["']).)*+|.*)
1134             )
1135             [ \t]*
1136             (?:
1137             (?['"]) # Title is surrounded ether by double or single quotes
1138             (?.*?) # actual title, but could be empty as in ""
1139             \g{img_title_container} # make the sure enclosing mark balance
1140             )?
1141             [ \t]*
1142             \)
1143             )
1144             )
1145             )
1146             [ \t]*
1147             (?
1148             \{
1149             [[:blank:]\h]*
1150             (?.+?)
1151             [[:blank:]\h]*
1152             \}
1153             )
1154             /x,
1155              
1156             # NOTE: insertion extended
1157             # https://regex101.com/r/IZw4YU/1/
1158             # 2025-10-19: Added safeguard $MD_SKIP_INLINE_CODE
1159             ex_insertion => qr/
1160             $MD_SKIP_INLINE_CODE
1161             |
1162             (? # insertion can be multilines
1163             (?
1164             (?!\n) # but not followed by a new line to avoid any previous + getting caught
1165             (? # the content which excludes any + unless they are escaped
1166             (?>\\[\+]|[^\+])*+
1167             )
1168             (?
1169             )
1170             /xm,
1171              
1172             # NOTE: katex dollar
1173             # https://regex101.com/r/43OuNT/1/
1174             # 2025-10-19: Added safeguard $MD_SKIP_INLINE_CODE
1175             ex_katex_dollar2 => qr/
1176             $MD_SKIP_INLINE_CODE
1177             |
1178             (?
1179             (?\${2})
1180             (?!\$)
1181             \n?
1182             (?.+?)\n?
1183             (?
1184             (?\g{katex_open})
1185             (?!\$)
1186             \n?
1187             /mxs,
1188             ex_katex_dollar1 => qr/
1189             $MD_SKIP_INLINE_CODE
1190             |
1191             (?
1192             (?\${1})
1193             (?!\$)
1194             \n?
1195             (?.+?)\n?
1196             (?
1197             (?\g{katex_open})
1198             (?!\$)
1199             \n?
1200             /mxs,
1201             # NOTE: katex bracket
1202             ex_katex_bracket => qr/
1203             $MD_SKIP_INLINE_CODE
1204             |
1205             (?
1206             (?\\\[)
1207             \n?
1208             (?.+?)\n?
1209             (?
1210             (?
1211             \\\]
1212             )\n?
1213             /mxs,
1214             # NOTE: katex parens
1215             ex_katex_parens => qr/
1216             $MD_SKIP_INLINE_CODE
1217             |
1218             (?
1219             (?\\\()
1220             \n?
1221             (?.+?)\n?
1222             (?
1223             (?
1224             \\\)
1225             )\n?
1226             /mxs,
1227              
1228             # NOTE: line break extended
1229             # https://regex101.com/r/6VG46H/1
1230             ex_line_break => qr/
1231             (?
1232             (?:[ ]{2,}\n|\n)
1233             )
1234             /mx,
1235              
1236             # NOTE: link extended
1237             # [Hyperlinked text](http://www.example.com)
1238             # [Hyperlinked text](http://www.example.com "Example Site")
1239             # https://regex101.com/r/7mLssJ/7
1240             # 2025-10-19: Added safeguard $MD_SKIP_INLINE_CODE
1241             ex_link => qr/
1242             $MD_SKIP_INLINE_CODE
1243             |
1244             (?
1245             (?:(?:\\|\!)\[(*SKIP)(*FAIL)|\[) # Cannot be preceded by an anti-slash nor an exclamation mark (image)
1246             (?
1247             (?!\^) # Make sure, this is not confused with a footnote
1248             (?>\\[\[\]]|[^\[\]])*+ # Link text
1249             )
1250             (?
1251             (?:
1252             (?:
1253             [ ]?
1254             (?:\n[ ]*)?
1255             (?
1256             (?!\^) # Make sure, this is not confused with a footnote
1257             (?.*?)
1258             (?
1259             )
1260             |
1261             (?:
1262             (?
1263             [ \t]*
1264             (?:
1265             <(?.*?)>
1266             |
1267             (? # link url without <>
1268             $HTTP_R3986_URI_INLINE
1269             )
1270             |
1271             (?.*?)
1272             )
1273             [ \t]*
1274             (?:
1275             (?['"])
1276             (?.*?)
1277             \g{link_title_container}
1278             )?
1279             [ \t]*
1280             (?
1281             (?![[:blank:]\h]+["'][^"']+["'])
1282             )
1283             )
1284             [ \t]*
1285             (?
1286             \{
1287             [[:blank:]\h]*
1288             (?.+?)
1289             [[:blank:]\h]*
1290             \}
1291             )
1292             /x,
1293              
1294             # NOTE: link definition extended
1295             # https://regex101.com/r/hVfXCe/3
1296             ex_link_def => qr/
1297             ^[ ]{0,3} # Leading space up to 3
1298             (?
1299             (?
1300             (?!\^) # Make sure, this is not confused with a footnote
1301             (?.+?)
1302             (?
1303             [ \t]* # Possibly with some space before colon
1304             \:
1305             [ \t]*\n?[ \t]* # Possibly with some space after the colon, possibly with a new line in between?
1306             (?:
1307             <(?[^\>]+)> # link within <>
1308             |
1309             (?\S+) # or link without <>
1310             )
1311             (?:
1312             (?:
1313             [ \t]+ # Either some space or tabs
1314             |
1315             [ \t]*\n[ \t]* # or a new line surrounded by 0 or more spaces or tabs
1316             )
1317             (?:
1318             (?:
1319             (?['"]) # Title is surrounded ether by double or single quotes
1320             (?.+?)
1321             \g{link_title_container} # make the sure enclosing mark balance
1322             )
1323             | # or
1324             \((?[^\)]+)\) # by parenthesis
1325             )
1326             )?
1327             [ \t]*
1328             (?
1329             \{
1330             [[:blank:]\h]*
1331             (?.+?)
1332             [[:blank:]\h]*
1333             \}
1334             [ \t]* # Possibly ending with some trailing spaces or tab
1335             )
1336             (?:\n+|\Z) # terminated by a new line or end of file
1337             /xm,
1338              
1339             # NOTE: markdown attributes
1340             # Ex: {#id1} or {.cl} or {#id.cl.class}
1341             md_attributes1 => qr/(?[^\}]*)\}/,
1342              
1343             # NOTE: subscript extended
1344             # https://regex101.com/r/gF6wVe/2
1345             # 2025-10-19: Added safeguard $MD_SKIP_INLINE_CODE
1346             ex_subscript => qr/
1347             $MD_SKIP_INLINE_CODE
1348             |
1349             (?
1350             (?:
1351             (?
1352             (?
1353             (?>\\[\~[:blank:]\h]|[^\~[:blank:]\h\v])*+
1354             )
1355             (?
1356             )
1357             |
1358             (?: # or the Microsoft way. Beurk
1359             \
1360             (?
1361             ((?!\v).)+
1362             )
1363             \<\/sub\>
1364             )
1365             )
1366             /x,
1367              
1368             # NOTE: superscript extended
1369             # https://regex101.com/r/yAcNcX/1/
1370             # 2025-10-19: Added safeguard $MD_SKIP_INLINE_CODE
1371             ex_superscript => qr/
1372             $MD_SKIP_INLINE_CODE
1373             |
1374             (?
1375             (?:
1376             (?
1377             (?
1378             (?>\\[\^[:blank:]\h]|[^\^[:blank:]\h\v])*+
1379             )
1380             (?
1381             )
1382             |
1383             (?: # or the Microsoft way. Beurk
1384             \
1385             (?
1386             ((?!\v).)+
1387             )
1388             \<\/sup\>
1389             )
1390             )
1391             /x,
1392              
1393             # NOTE: strikethrough extended
1394             # https://regex101.com/r/4Z3h4F/1/
1395             # 2025-10-19: Added safeguard $MD_SKIP_INLINE_CODE
1396             ex_strikethrough => qr/
1397             $MD_SKIP_INLINE_CODE
1398             |
1399             (? # strikethrough can be multilines
1400             (?
1401             (?!\n) # but not followed by a new line to avoid any previous tildes getting caught
1402             (? # the content which excludes any tilde unless they are escaped
1403             (?>\\[\~]|[^\~])*+
1404             )
1405             (?
1406             )
1407             /x,
1408              
1409             # NOTE: table extended
1410             # https://regex101.com/r/01XCqB/13
1411             # 2025-10-19: Added safeguard
1412             ex_table => qr/
1413             (?: $MD_SKIP_FENCED | $MD_SKIP_INDENTED )
1414             |
1415             (?:(?<=^\n)|(?<=\A))
1416             (?
1417             (?(?&t_caption))? # maybe some caption at the top?
1418             (?
1419             (? # Table header
1420             (?(?&t_th_sep))? # Possible top separator line
1421             (? # First header row
1422             [^\n]+\n
1423             )
1424             (?(?&t_th_sep)) # a separator
1425             (? # and possibly a second row of header
1426             [^\n]+\n
1427             (?(?&t_th_sep))
1428             )?
1429             )
1430             )
1431             (? # Multiple table rows
1432             (?(?&t_row))+
1433             )
1434             (?(?&t_th_sep))? # Possibly ended by a separator line
1435             (?(?&t_caption))? # and maybe with some caption at the bottom
1436             (?=\n|\Z)
1437             )
1438             (?(DEFINE)
1439             (?
1440             [\+\-\|](?:[\: ]*\-+[\: ]*[\+\-\|]?)+\n
1441             )
1442             (?
1443             [ ]{0,3}
1444             (?!(?&t_th_sep)|(?&t_caption))
1445             (?:
1446             (?[\|\:]+)?
1447             (?[^\|\:\n]+)
1448             (?:
1449             (?:[\|\:]{0,2}[ ]*(?=\n))
1450             |
1451             [\|\:]{1,2}
1452             )
1453             )+
1454             (?:
1455             (?:\n(?=(?&t_th_sep)))
1456             |
1457             (?:\n(?=(?&t_caption)))
1458             |
1459             \n
1460             )
1461             )
1462             (?
1463             [ ]{0,3}
1464             \[[^\]]+\]
1465             [ ]*
1466             \n
1467             )
1468             )
1469             /xms,
1470             },
1471             };
1472              
1473             pattern name => [qw( Markdown -extended=1 ) ],
1474             create => sub
1475             {
1476             my( $self, $flags ) = @_;
1477             my %re = %$REGEXP;
1478             ## Override vanilla regular expressions by the extended ones
1479             if( $flags->{'-extended'} )
1480             {
1481             my @k = keys( %$REGEXP_EXT );
1482             @re{ @k } = @$REGEXP_EXT{ @k };
1483             }
1484             my $pat = join( '|' => values( %re ) );
1485             return( "(?k:$pat)" );
1486             };
1487              
1488             pattern name => [qw( Markdown Bold ) ],
1489             create => $REGEXP->{bold};
1490              
1491             pattern name => [qw( Markdown Blockquote ) ],
1492             create => $REGEXP->{bquote};
1493              
1494             pattern name => [qw( Markdown CodeBlock ) ],
1495             create => $REGEXP->{code_block};
1496              
1497             pattern name => [qw( Markdown CodeLine ) ],
1498             create => $REGEXP->{code_line };
1499              
1500             pattern name => [qw( Markdown CodeSpan ) ],
1501             create => $REGEXP->{code_span};
1502              
1503             pattern name => [qw( Markdown Em ) ],
1504             create => $REGEXP->{em};
1505              
1506             pattern name => [qw( Markdown Header ) ],
1507             create => $REGEXP->{header};
1508              
1509             pattern name => [qw( Markdown HeaderLine ) ],
1510             create => $REGEXP->{header_line};
1511              
1512             pattern name => [qw( Markdown Html ) ],
1513             create => $REGEXP->{html};
1514              
1515             pattern name => [qw( Markdown Image ) ],
1516             create => $REGEXP->{img};
1517              
1518             pattern name => [qw( Markdown Line ) ],
1519             create => $REGEXP->{line};
1520              
1521             pattern name => [qw( Markdown LineBreak ) ],
1522             create => $REGEXP->{line_break};
1523              
1524             pattern name => [qw( Markdown Link ) ],
1525             create => $REGEXP->{link};
1526              
1527             pattern name => [qw( Markdown LinkAuto ) ],
1528             create => $REGEXP->{link_auto};
1529              
1530             pattern name => [qw( Markdown LinkDefinition ) ],
1531             create => $REGEXP->{link_def};
1532              
1533             pattern name => [qw( Markdown LinkRef ) ],
1534             create => $REGEXP->{link_ref};
1535              
1536             pattern name => [qw( Markdown List ) ],
1537             create => $REGEXP->{list};
1538              
1539             pattern name => [qw( Markdown ListFirstLevel ) ],
1540             create => $REGEXP->{list_first_level};
1541              
1542             pattern name => [qw( Markdown ListNthLevel ) ],
1543             create => $REGEXP->{list_nth_level};
1544              
1545             pattern name => [qw( Markdown ListItem ) ],
1546             create => $REGEXP->{list_item};
1547              
1548             pattern name => [qw( Markdown Paragraph ) ],
1549             create => $REGEXP->{paragraph};
1550              
1551             pattern name => [qw( Markdown ExtAbbr ) ],
1552             create => $REGEXP_EXT->{ex_abbr};
1553              
1554             pattern name => [qw( Markdown ExtAttributes ) ],
1555             create => $REGEXP_EXT->{md_attributes1};
1556              
1557             pattern name => [qw( Markdown ExtCheckbox ) ],
1558             create => $REGEXP_EXT->{ex_checkbox};
1559              
1560             pattern name => [qw( Markdown ExtCodeBlock ) ],
1561             create => $REGEXP_EXT->{ex_code_block};
1562              
1563             pattern name => [qw( Markdown ExtFootnote ) ],
1564             create => $REGEXP_EXT->{ex_footnote};
1565              
1566             pattern name => [qw( Markdown ExtFootnoteReference ) ],
1567             create => $REGEXP_EXT->{ex_footnote_ref};
1568              
1569             pattern name => [qw( Markdown ExtHeader ) ],
1570             create => $REGEXP_EXT->{ex_header};
1571              
1572             pattern name => [qw( Markdown ExtHeaderLine ) ],
1573             create => $REGEXP_EXT->{ex_header_line};
1574              
1575             pattern name => [qw( Markdown ExtHtmlMarkdown ) ],
1576             create => $REGEXP_EXT->{ex_html_markdown};
1577              
1578             pattern name => [qw( Markdown ExtImage )],
1579             create => $REGEXP_EXT->{ex_img};
1580              
1581             pattern name => [qw( Markdown ExtInsertion )],
1582             create => $REGEXP_EXT->{ex_insertion};
1583              
1584             # pattern name => [qw( Markdown ExtKatex ), -delimiter=],
1585             # create => $REGEXP_EXT->{ex_katex};
1586             pattern name => [qw( Markdown ExtKatex ), "-delimiter=\$\$,\$\$,\$,\$,\\\[,\\\],\\\(,\\\)"],
1587             create => sub
1588             {
1589             my $delim = [split(/[[:blank:]\h]*\,[[:blank:]\h]*/, $_[1]->{'-delimiter'} )];
1590             my $map =
1591             {
1592             '$$$$' => 'ex_katex_dollar2',
1593             '$$' => 'ex_katex_dollar1',
1594             '\[\]' => 'ex_katex_bracket',
1595             '\(\)' => 'ex_katex_parens',
1596             };
1597             my $res = [];
1598             for( my $i = 0; $i < scalar( @$delim ); $i += 2 )
1599             {
1600             my $k = join( '', @$delim[ $i..$i+1 ] );
1601             push( @$res, $REGEXP_EXT->{ $map->{ $k } } ) if( exists( $map->{ $k } ) );
1602             }
1603             my $re;
1604             if( scalar( @$res ) )
1605             {
1606             my $re_spec = "(?\n" . join( "\n|\n", @$res ) . "\n)";
1607             $re = qr/$re_spec/mxs;
1608             }
1609             $re;
1610             };
1611              
1612             pattern name => [qw( Markdown ExtLineBreak ) ],
1613             create => $REGEXP_EXT->{ex_line_break};
1614              
1615             pattern name => [qw( Markdown ExtLink ) ],
1616             create => $REGEXP_EXT->{ex_link};
1617              
1618             pattern name => [qw( Markdown ExtLinkDefinition ) ],
1619             create => $REGEXP_EXT->{ex_link_def};
1620              
1621             pattern name => [qw( Markdown ExtStrikeThrough ) ],
1622             create => $REGEXP_EXT->{ex_strikethrough};
1623              
1624             pattern name => [qw( Markdown ExtSubscript ) ],
1625             create => $REGEXP_EXT->{ex_subscript};
1626              
1627             pattern name => [qw( Markdown ExtSuperscript ) ],
1628             create => $REGEXP_EXT->{ex_superscript};
1629              
1630             pattern name => [qw( Markdown ExtTable ) ],
1631             create => $REGEXP_EXT->{ex_table};
1632              
1633             1;
1634             # NOTE: POD
1635             __END__