#!/usr/bin/env raku
grammar TinyMD {
token TOP { \s* <block>* \s* }
token block { <hr> || <header> || <codeblock> || <quote> || <ul> || <ol> || <para> }
token hr { \h* <[-*_]> ** 3..* \h* \n+ }
token header { $<lvl>=('#' ** 1..6) \h+ $<text>=\N+ \n* }
token codeblock { [ [ ' ' ** 4 | \t ] $<text>=\N+ \n* ]+ }
token quote { [ \h* '>' \h* $<text>=\N+ \n* ]+ }
token ul { [ \h* <[-*+]> \h+ $<text>=\N+ \n* ]+ }
token ol { [ \h* \d+ '.' \h+ $<text>=\N+ \n* ]+ }
token block_stop { \h* [ <[-*_#]> | \d+ '.' | '>' | ' ' | \t ] }
token para { [ <!before <block_stop> > $<text>=\N+ \n* ]+ }
}
class MD-HTML {
method TOP($/) { make $<block>.map(*.made).join("\n\n") }
method block($/) {
make ($<hr> || $<header> || $<codeblock> || $<quote> || $<ul> || $<ol> || $<para>).made;
}
method hr($/) { make "<hr>" }
method header($/) { make "<h{$<lvl>.chars}>" ~ inline($<text>.Str) ~ "</h{$<lvl>.chars}>" }
method codeblock($/) { make "<pre><code>\n" ~ $<text>.join("\n") ~ "\n</code></pre>" }
method quote($/) { make "<blockquote>\n" ~ $<text>.map({" <p>"~inline($_)~"</p>"}).join("\n") ~ "\n</blockquote>" }
method para($/) { make "<p>\n " ~ inline($<text>.join("\n")) ~ "\n</p>" }
method ul($/) { make "<ul>\n" ~ $<text>.map({" <li>" ~ inline($_) ~ "</li>"}).join("\n") ~ "\n</ul>" }
method ol($/) { make "<ol>\n" ~ $<text>.map({" <li>" ~ inline($_) ~ "</li>"}).join("\n") ~ "\n</ol>" }
sub inline($str) {
$str.subst(/ \! \[ (.*?) \] \( (.*?) \) /, -> $/ { qq|<img src="$1" alt="$0">| }, :g) # Images
.subst(/ \[ (.*?) \] \( (.*?) \) /, -> $/ { qq|<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 .
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;