public
Description: Markdown processing with Nu. Use with any Nu or Objective-C project. Created by Grayson Hansard.
Homepage: http://programming.nu/posts/2007/10/10/markdown-in-nu
Clone URL: git://github.com/timburks/numarkdown.git
Modified substitutions to use eachInReverse:

Making substitions from right-to-left is safer.
Some enumerations were changed already as part of
fixing the MarkdownTest regressions.  This change
set is the result of reviewing the source and
changing other uses of [NSArray each:] that appeared
vulnerable.
timburks (author)
Tue Nov 06 20:59:31 -0800 2007
commit  758dab4ac390bce8a0d526cf79a8097876e36f82
tree    bdd3147c7f3cc5a7840fbf74b2dd7a9b8e23f91b
parent  501b033fa28f0e5243829dd2dc452437fc1c9b8c
...
148
149
150
151
 
152
153
154
155
156
 
157
158
159
...
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
303
304
305
...
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
 
 
 
 
 
 
 
 
 
 
 
 
 
332
333
334
...
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
364
365
366
...
378
379
380
381
382
383
384
385
386
387
388
389
390
391
 
 
 
 
 
 
 
 
 
 
 
 
392
393
394
...
430
431
432
433
 
434
435
436
 
 
437
438
 
439
440
441
...
450
451
452
453
454
455
456
 
 
 
 
457
458
459
...
469
470
471
472
 
473
474
475
...
478
479
480
481
 
482
483
484
...
489
490
491
492
 
493
494
495
...
498
499
500
501
 
502
503
 
504
505
506
...
509
510
511
512
513
 
514
515
516
...
531
532
533
534
 
535
536
537
538
539
 
540
541
542
...
548
549
550
551
 
552
553
554
...
556
557
558
559
 
560
561
562
 
 
563
564
565
...
148
149
150
 
151
152
153
154
155
 
156
157
158
159
...
278
279
280
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
...
318
319
320
 
 
 
 
 
 
 
 
 
 
 
 
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
...
348
349
350
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
...
381
382
383
 
 
 
 
 
 
 
 
 
 
 
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
...
434
435
436
 
437
438
 
 
439
440
441
 
442
443
444
445
...
454
455
456
 
 
 
 
457
458
459
460
461
462
463
...
473
474
475
 
476
477
478
479
...
482
483
484
 
485
486
487
488
...
493
494
495
 
496
497
498
499
...
502
503
504
 
505
506
 
507
508
509
510
...
513
514
515
 
 
516
517
518
519
...
534
535
536
 
537
538
539
540
541
 
542
543
544
545
...
551
552
553
 
554
555
556
557
...
559
560
561
 
562
563
 
 
564
565
566
567
568
0
@@ -148,12 +148,12 @@
0
       [")]
0
       [ \t]*
0
     )?  # title is optional
0
- (?:\n+|\Z)END -"mx") findAllInString:str) each:
0
+ (?:\n+|\Z)END -"mx") findAllInString:str) eachInReverse:
0
       (do (m)
0
           ($g_urls setObject:(markdown_EncodeAmpsAndAngles (m groupAtIndex:2)) forKey:(m groupAtIndex:1))
0
           (if (!= (m groupAtIndex:3) nil)
0
               ($g_titles setObject:(m groupAtIndex:3) forKey:(m groupAtIndex:1)))
0
- (str replaceOccurrencesOfString:(m group) withString:-"")))
0
+ (str replaceCharactersInRange:(m range) withString:"")))
0
      str)
0
 
0
 (function markdown_EncodeCode (str)
0
@@ -278,28 +278,29 @@
0
      (.*?)    # id = $3
0
      \]
0
 
0
- )END -"xsge") findAllInString:str) each:(do (m)
0
- (set whole_match (m groupAtIndex:1))
0
- (set alt_text (m groupAtIndex:2))
0
- (set link_id (m groupAtIndex:3))
0
- (set result nil)
0
- (if (== link_id -"")
0
- (set link_id alt_text))
0
- (set alt_text ((regex "\"") replaceWithString:-""" inString:alt_text))
0
- (if (!= ($g_urls objectForKey:link_id) nil)
0
- (set url (markdown_EncodeItalicsAndBolds ($g_urls objectForKey:link_id)))
0
- (set result "<img src=\"#{url}\" alt=\"#{alt_text}\"")
0
- (if (!= ($g_titles valueForKey:link_id) nil)
0
- (then (set title (markdown_EncodeQuotes (markdown_EncodeItalicsAndBolds ($g_titles objectForKey:link_id))))
0
- (set result (result stringByAppendingString:" title=\"#{title}\"")))
0
- ;; it seems like this else clause should be included
0
- ;; but it breaks the MarkdownTests regressions
0
- ;; which I think is a bug in Markdown.pl
0
- ;;(else (set result (result stringByAppendingString:" title=\"\""))
0
- )
0
- (set result (result stringByAppendingString:-" />"))
0
- (else (set result whole_match)))
0
- (str replaceOccurrencesOfString:whole_match withString:result)))
0
+ )END -"xsge") findAllInString:str) eachInReverse:
0
+ (do (m)
0
+ (set whole_match (m groupAtIndex:1))
0
+ (set alt_text (m groupAtIndex:2))
0
+ (set link_id (m groupAtIndex:3))
0
+ (set result nil)
0
+ (if (== link_id -"")
0
+ (set link_id alt_text))
0
+ (set alt_text ((regex "\"") replaceWithString:-"&quot;" inString:alt_text))
0
+ (if (!= ($g_urls objectForKey:link_id) nil)
0
+ (set url (markdown_EncodeItalicsAndBolds ($g_urls objectForKey:link_id)))
0
+ (set result "<img src=\"#{url}\" alt=\"#{alt_text}\"")
0
+ (if (!= ($g_titles valueForKey:link_id) nil)
0
+ (then (set title (markdown_EncodeQuotes (markdown_EncodeItalicsAndBolds ($g_titles objectForKey:link_id))))
0
+ (set result (result stringByAppendingString:" title=\"#{title}\"")))
0
+ ;; it seems like this else clause should be included
0
+ ;; but it breaks the MarkdownTests regressions
0
+ ;; which I think is a bug in Markdown.pl
0
+ ;;(else (set result (result stringByAppendingString:" title=\"\""))
0
+ )
0
+ (set result (result stringByAppendingString:-" />"))
0
+ (else (set result whole_match)))
0
+ (str replaceOccurrencesOfString:whole_match withString:result)))
0
      ; Next, handle inline images: ![alt text](url -"optional title")
0
      (((eregex <<-END
0
     (        # wrap whole match in $1
0
@@ -317,18 +318,19 @@
0
        [ \t]*
0
       )?      # title is optional
0
      \)
0
- )END -"xsge") findAllInString:str) each:(do (m)
0
- (set whole_match (m groupAtIndex:1))
0
- (set alt_text (markdown_EncodeQuotes (markdown_EncodeItalicsAndBolds (m groupAtIndex:2))))
0
- (set url (m groupAtIndex:3))
0
- (set result "<img src=\"#{url}\" alt=\"#{alt_text}\"")
0
- (if (!= (m groupAtIndex:6) nil)
0
- (then (set title (markdown_EncodeQuotes (markdown_EncodeItalicsAndBolds (m groupAtIndex:6))))
0
- (set result (result stringByAppendingString:" title=\"#{title}\"")))
0
- (else (set result (result stringByAppendingString:" title=\"\""))))
0
-
0
- (set result (result stringByAppendingString:-" />"))
0
- (str replaceOccurrencesOfString:whole_match withString:result)))
0
+ )END -"xsge") findAllInString:str) eachInReverse:
0
+ (do (m)
0
+ (set whole_match (m groupAtIndex:1))
0
+ (set alt_text (markdown_EncodeQuotes (markdown_EncodeItalicsAndBolds (m groupAtIndex:2))))
0
+ (set url (m groupAtIndex:3))
0
+ (set result "<img src=\"#{url}\" alt=\"#{alt_text}\"")
0
+ (if (!= (m groupAtIndex:6) nil)
0
+ (then (set title (markdown_EncodeQuotes (markdown_EncodeItalicsAndBolds (m groupAtIndex:6))))
0
+ (set result (result stringByAppendingString:" title=\"#{title}\"")))
0
+ (else (set result (result stringByAppendingString:" title=\"\""))))
0
+
0
+ (set result (result stringByAppendingString:-" />"))
0
+ (str replaceOccurrencesOfString:whole_match withString:result)))
0
      str)
0
 
0
 (function markdown_DoAnchors (str)
0
@@ -346,21 +348,22 @@
0
        \[
0
        (.*?)    # id = $3
0
        \]
0
- )END -"xsge") findAllInString:str) each:(do (m)
0
- (set result nil)
0
- (set whole_match (m groupAtIndex:1))
0
- (set link_text (m groupAtIndex:2))
0
- (set link_id (m groupAtIndex:3))
0
- (if (== link_id -"") (set link_id link_text))
0
- (if (!= ($g_urls valueForKey:link_id) nil)
0
- (set url (markdown_EncodeItalicsAndBolds ($g_urls valueForKey:link_id)))
0
- (set result "<a href=\"#{url}\"")
0
- (if (!= ($g_titles valueForKey:link_id) nil)
0
- (set title (markdown_EncodeQuotes (markdown_EncodeItalicsAndBolds ($g_titles valueForKey:link_id))))
0
- (set result (result stringByAppendingString:" title=\"#{title}\"")))
0
- (set result (result stringByAppendingString:-">#{link_text}</a>"))
0
- (else (set result whole_match)))
0
- (str replaceOccurrencesOfString:whole_match withString:result)))
0
+ )END -"xsge") findAllInString:str) eachInReverse:
0
+ (do (m)
0
+ (set result nil)
0
+ (set whole_match (m groupAtIndex:1))
0
+ (set link_text (m groupAtIndex:2))
0
+ (set link_id (m groupAtIndex:3))
0
+ (if (== link_id -"") (set link_id link_text))
0
+ (if (!= ($g_urls valueForKey:link_id) nil)
0
+ (set url (markdown_EncodeItalicsAndBolds ($g_urls valueForKey:link_id)))
0
+ (set result "<a href=\"#{url}\"")
0
+ (if (!= ($g_titles valueForKey:link_id) nil)
0
+ (set title (markdown_EncodeQuotes (markdown_EncodeItalicsAndBolds ($g_titles valueForKey:link_id))))
0
+ (set result (result stringByAppendingString:" title=\"#{title}\"")))
0
+ (set result (result stringByAppendingString:-">#{link_text}</a>"))
0
+ (else (set result whole_match)))
0
+ (str replaceOccurrencesOfString:whole_match withString:result)))
0
      
0
      ; Next, inline-style links: [link text](url -"optional title")
0
      (((eregex <<-END
0
@@ -378,17 +381,18 @@
0
        \5    # matching quote
0
       )?      # title is optional
0
      \)
0
- )END -"xsge") findAllInString:str) each:(do (m)
0
- (set whole_match (m groupAtIndex:1))
0
- (set link_text (m groupAtIndex:2))
0
- (set url (markdown_EncodeItalicsAndBolds (m groupAtIndex:3)))
0
- (set title (m groupAtIndex:6))
0
- (set result "<a href=\"#{url}\"")
0
- (if (!= title nil)
0
- (set title (markdown_EncodeQuotes (markdown_EncodeItalicsAndBolds (title))))
0
- (set result (result stringByAppendingString:" title=\"#{title}\"")))
0
- (set result (result stringByAppendingString:-">#{link_text}</a>"))
0
- (str replaceOccurrencesOfString:whole_match withString:result)))
0
+ )END -"xsge") findAllInString:str) eachInReverse:
0
+ (do (m)
0
+ (set whole_match (m groupAtIndex:1))
0
+ (set link_text (m groupAtIndex:2))
0
+ (set url (markdown_EncodeItalicsAndBolds (m groupAtIndex:3)))
0
+ (set title (m groupAtIndex:6))
0
+ (set result "<a href=\"#{url}\"")
0
+ (if (!= title nil)
0
+ (set title (markdown_EncodeQuotes (markdown_EncodeItalicsAndBolds (title))))
0
+ (set result (result stringByAppendingString:" title=\"#{title}\"")))
0
+ (set result (result stringByAppendingString:-">#{link_text}</a>"))
0
+ (str replaceOccurrencesOfString:whole_match withString:result)))
0
      str)
0
 
0
 (function markdown_UnescapeSpecialChars (str)
0
@@ -430,12 +434,12 @@
0
      ;
0
      ;   Header 2
0
      ;   --------
0
- (((eregex -"^(.+)[ \t]*\n=+[ \t]*\n+" -"mx") findAllInString:str) each:
0
+ (((eregex -"^(.+)[ \t]*\n=+[ \t]*\n+" -"mx") findAllInString:str) eachInReverse:
0
       (do (m) ; Note the multi-line hack below. -"\n\n" is not turned into new lines.
0
- (str replaceOccurrencesOfString:(m group) withString:"<h1>#{(markdown_RunSpanGamut (m groupAtIndex:1))}</h1>\n\n")))
0
- (((eregex -"^(.+)[ \t]*\n-+[ \t]*\n+" -"mx") findAllInString:str) each:
0
+ (str replaceCharactersInRange:(m range) withString:"<h1>#{(markdown_RunSpanGamut (m groupAtIndex:1))}</h1>\n\n")))
0
+ (((eregex -"^(.+)[ \t]*\n-+[ \t]*\n+" -"mx") findAllInString:str) eachInReverse:
0
       (do (m)
0
- (str replaceOccurrencesOfString:(m group) withString:"<h2>#{(markdown_RunSpanGamut (m groupAtIndex:1))}</h2>\n\n")))
0
+ (str replaceCharactersInRange:(m range) withString:"<h2>#{(markdown_RunSpanGamut (m groupAtIndex:1))}</h2>\n\n")))
0
      
0
      ; atx-style headers:
0
      ;  # Header 1
0
@@ -450,10 +454,10 @@
0
     (.+?)      # $2 = Header text
0
     [ \t]*
0
     \#*        # optional closing #'s (not counted)
0
- \n+END -"mx") findAllInString:str) each:(do (m)
0
- (str replaceOccurrencesOfString:(m group)
0
- withString:"<h#{((m groupAtIndex:1) length)}>#{(markdown_RunSpanGamut (m groupAtIndex:2))}</h#{((m groupAtIndex:1) length)}>\n\n"
0
- )))
0
+ \n+END -"mx") findAllInString:str) eachInReverse:
0
+ (do (m)
0
+ (str replaceCharactersInRange:(m range)
0
+ withString:"<h#{((m groupAtIndex:1) length)}>#{(markdown_RunSpanGamut (m groupAtIndex:2))}</h#{((m groupAtIndex:1) length)}>\n\n")))
0
      str)
0
 
0
 (function markdown_Outdent (item)
0
@@ -469,7 +473,7 @@
0
     (#{marker_any}) [ \t]+      # list marker = $3
0
     ((?s:.+?)            # list item text = $4
0
     (\n{1,2}))
0
- (?= \n* (\z | \2 (#{marker_any}) [ \t]+))END -"mx") findAllInString:list_str) each:
0
+ (?= \n* (\z | \2 (#{marker_any}) [ \t]+))END -"mx") findAllInString:list_str) eachInReverse:
0
       (do (m)
0
           (set item (m groupAtIndex:4))
0
           (set leading_line (m groupAtIndex:1))
0
@@ -478,7 +482,7 @@
0
           (if (or leading_line ((regex -"\n{2,}") findInString:item))
0
               (then (set item (markdown_RunBlockGamut (markdown_Outdent item))))
0
               (else (set item (markdown_RunSpanGamut ((markdown_DoLists (markdown_Outdent item)) chomp)))))
0
- (list_str replaceOccurrencesOfString:(m group) withString:"<li>#{item}</li>\n" options:0 range:(list 0 (list_str length)))))
0
+ (list_str replaceCharactersInRange:(m range) withString:"<li>#{item}</li>\n")))
0
      (set $g_list_level (- $g_list_level 1))
0
      list_str)
0
 
0
@@ -489,7 +493,7 @@
0
      (set whole_list -"(([ ]{0,3}(#{marker_any})[ \t]+)(?s:.+?)(\z|\n{2,}(?=\S)(?![ \t]*#{marker_any}[ \t]+)))")
0
      (set result str) ;; default
0
      (if (> $g_list_level 0)
0
- (then (((eregex -"^#{whole_list}" -"mx") findAllInString:str) each:
0
+ (then (((eregex -"^#{whole_list}" -"mx") findAllInString:str) eachInReverse:
0
                 (do (m)
0
                     (set m_list (m groupAtIndex:1))
0
                     (if ((regex marker_ul) findInString:(m groupAtIndex:3)) (then (set list_type -"ul")) (else (set list_type -"ol")))
0
@@ -498,9 +502,9 @@
0
                     (set m_list ((regex -"\n{2,}") replaceWithString:"\n\n\n" inString:m_list))
0
                     (set formattedList (markdown_ProcessListItems m_list marker_any))
0
                     (set formattedList "<#{list_type}>\n#{formattedList}</#{list_type}>\n")
0
- (result replaceOccurrencesOfString:(m group) withString:formattedList options:0 range:(list 0 (result length))))))
0
+ (result replaceCharactersInRange:(m range) withString:formattedList))))
0
          (else
0
- (((eregex -"(?:(?<=\n\n)|\A\n?)#{whole_list}" -"mx") findAllInString:str) each:
0
+ (((eregex -"(?:(?<=\n\n)|\A\n?)#{whole_list}" -"mx") findAllInString:str) eachInReverse:
0
                 (do (m)
0
                     (set m_list (m groupAtIndex:1))
0
                     (if ((regex marker_ul) findInString:(m groupAtIndex:3)) (then (set list_type -"ul")) (else (set list_type -"ol")))
0
@@ -509,8 +513,7 @@
0
                     (set m_list ((regex -"\n{2,}") replaceWithString:"\n\n\n" inString:m_list))
0
                     (set formattedList (markdown_ProcessListItems m_list marker_any))
0
                     (set formattedList "<#{list_type}>\n#{formattedList}</#{list_type}>\n")
0
- (result replaceOccurrencesOfString:(m group) withString:formattedList options:0 range:(list 0 (result length)))))
0
- ))
0
+ (result replaceCharactersInRange:(m range) withString:formattedList)))))
0
      result)
0
 
0
 (function markdown_Detab (str)
0
@@ -531,12 +534,12 @@
0
      )+
0
     )
0
     ((?=^[ ]{0,4}\S)|\Z)  # Lookahead for non-space at line-start, or end of doc
0
- END -"mx") findAllInString:str) each:
0
+ END -"mx") findAllInString:str) eachInReverse:
0
       (do (m)
0
           (set codeblock (m groupAtIndex:1))
0
           (set codeblock (markdown_Detab (markdown_EncodeCode (markdown_Outdent codeblock))))
0
           (set codeblock ((regex -"(\A\n+)|(\s+\z)") replaceWithString:-"" inString:codeblock))
0
- (str replaceOccurrencesOfString:(m group) withString:"\n<pre><code>#{codeblock}\n</code></pre>\n\n")))
0
+ (str replaceCharactersInRange:(m range) withString:"\n<pre><code>#{codeblock}\n</code></pre>\n\n")))
0
      str)
0
 
0
 (function markdown_DoBlockQuotes (str)
0
@@ -548,7 +551,7 @@
0
        (.+\n)*          # subsequent consecutive lines
0
        \n*            # blanks
0
       )+
0
- )/mx findAllInString:str) each:
0
+ )/mx findAllInString:str) eachInReverse:
0
       (do (m)
0
           (set bq (m groupAtIndex:1))
0
           (set bq (/^[ \t]*>[ \t]?/m replaceWithString:"" inString:bq)) ;; trim one level of quoting
0
@@ -556,10 +559,10 @@
0
           (set bq (markdown_RunBlockGamut bq))
0
           (set bq (/^/m replaceWithString:" " inString:bq))
0
           ; These leading spaces screw with <pre> content, so we need to fix that:
0
- (((eregex -"(\s*<pre>.+?</pre>)" -"egsx") findAllInString:bq) each:
0
+ (((eregex -"(\s*<pre>.+?</pre>)" -"egsx") findAllInString:bq) eachInReverse:
0
            (do (m2)
0
- (bq replaceOccurrencesOfString:(m2 group) withString:((eregex -"^ " -"mg") replaceWithString:-"" inString:(m2 groupAtIndex:1)))))
0
- (str replaceOccurrencesOfString:(m group) withString:"<blockquote>\n#{bq}\n</blockquote>\n\n")))
0
+ (bq replaceCharactersInRange:(m2 range) withString:((eregex -"^ " -"mg") replaceWithString:-"" inString:(m2 groupAtIndex:1)))))
0
+ (str replaceCharactersInRange:(m range) withString:"<blockquote>\n#{bq}\n</blockquote>\n\n")))
0
      str)
0
 
0
 (function markdown_FormParagraphs (str)

Comments

    No one has commented yet.