Quick actions

cmd+k|ctrl+k

Navigation

Languages

Untitled

Snippet info

Language

Raku

Visibility

unlisted

Author

legacy-anonymous

Created

2026-06-05T05:29:51.800882Z

Updated

2026-06-05T05:29:51.800882Z

#!/usr/bin/env raku

grammar TinyMD {
    token TOP { \s* <block>* \s* }
    
    proto token block { * }

    # Horizontal Rule: 3 or more -, *, or _
    token block:sym<hr> { ^^ \h* <[-*_]> ** 3..* \h* \n+ }

    # Header: 1 to 6 # marks
    token block:sym<header> { ^^ $<lvl>=('#' ** 1..6) \h+ $<text>=\N+ \n* }

    # Code Block: Indented by 4 spaces or 1 tab
    token block:sym<codeblock> { [ ^^ [ ' ' ** 4 | \t ] $<text>=\N+ \n* ]+ }

    # Blockquote: Starts with >
    token block:sym<quote> { [ ^^ \h* '>' \h* $<text>=\N+ \n* ]+ }

    # Lists: Unordered (-, *, +) or Ordered (1.)
    token block:sym<list> { [ ^^ \h* $<marker>=[ <[-*+]> | \d+ '.' ] \h+ $<text>=\N+ \n* ]+ }

    # Paragraph: Any lines that does NOT start with the above block indicators
    token block:sym<para> { [ $<text>=\N+ \n* ]+ }
}

class MD-HTML {
    method TOP($/) { make $<block>.map(*.made).join("\n\n") }

    method block:sym<hr>($/)        { make "<hr>" }
    method block:sym<header>($/)    { make "<h{$<lvl>.chars}>" ~ inline($<text>) ~ "</h{$<lvl>.chars}>" }
    method block:sym<codeblock>($/) { make "<pre><code>\n" ~ $<text>.join("\n") ~ "\n</code></pre>" }
    method block:sym<quote>($/)     { make "<blockquote>\n" ~ $<text>.map({"  <p>"~inline($_)~"</p>"}).join("\n") ~ "\n</blockquote>" }
    method block:sym<para>($/)      { make "<p>\n  " ~ inline($<text>.join("\n")) ~ "\n</p>" }
    
    method block:sym<list>($/) {
        my $tag = $<marker>[0] ~~ /\d/ ?? "ol" !! "ul";
        make "<$tag>\n" ~ $<text>.map({"  <li>" ~ inline($_) ~ "</li>"}).join("\n") ~ "\n</$tag>"
    }

    sub inline($str) {
        $str.subst(/ \! \[ (.*?) \] \( (.*?) \) /,  -> $/   { "<img src=\"$1\" alt=\"$0\">" }, :g)   # Images
            .subst(/ \[ (.*?) \] \( (.*?) \) /,     -> $/   { "<a href=\"$1\">{$0}</a>" },     :g)   # Links
            .subst(/ \*\* (.*?) \*\* | __ (.*?) __ /, -> $/ { "<strong>{ $0 || $1 }</strong>" }, :g) # bold
            .subst(/ \* (.*?) \* | _ (.*?) _ /,       -> $/ { "<em>{ $0 || $1 }</em>" },     :g)     # italic
            .subst(/ \` (.*?) \` /,                   -> $/ { "<code>{$0}</code>" },       :g)       # code
    }
}

my $markdown = q:to/END/;
# Markdown Core Support

This is a paragraph with a [link](https://raku.org) and an ![image](logo.png).
It supports **bold**, __bold__, *italics*, _italics_, and `code`.

---

> This is a blockquote.
> It can span multiple lines!

1. First ordered item
2. Second ordered item

* Unordered item A
- Unordered item B

    print("This is a code block");
    say "Because it is indented by 4 spaces";
END

say TinyMD.parse($markdown, actions => MD-HTML.new).made;
INFO