#!/usr/bin/env raku
use lib 'lib';
use Slang::TinyMD;
my $html = markdown {
super foo
};
say $html;
unit module TinyMD;
role TinyMD::Grammar {
token term:sym<markdown> {
'markdown' \h* '{' ~ '}' <markdown-body>
}
token markdown-body { \s* <block>* \s* }
proto token block { * }
token block:sym<hr> { ^^ \h* <[-*_]> ** 3..99 \h* \n+ }
token block:sym<header> { ^^ $<lvl>=('#' ** 1..6) \h+ $<text>=\N+ \n* }
token block:sym<fenced> { ^^ '```' $<lang>=\N* \n $<text>=.*? ^^ '```' \n* }
token block:sym<codeblock> { [ ^^ [ ' ' ** 4 | \t ] $<text>=\N+ \n* ]+ }
token block:sym<quote> { [ ^^ \h* '>' \h* $<text>=\N+ \n* ]+ }
token block:sym<ul> { [ ^^ \h* <[-*+]> \h+ $<text>=\N+ \n* ]+ }
token block:sym<ol> { [ ^^ \h* \d+ '.' \h+ $<text>=\N+ \n* ]+ }
# token block:sym<para> { [ ^^ <!before <block-stop> > $<text>=\N+ \n* ]+ }
token block:sym<para> { [ ^^ <!before <block-stop> > $<text>=[ <-[\n}]>+ ] \n* ]+ }
token block-stop { \h* [ '}' | <[-*_]>**3..99 | '#' | '>' | <[-*+]> \h | \d+ '.' \h | ' ' ** 4 | \t | '```' ] }
# token block-stop { \h* [ <[-*_]>**3..99 | '#' | '>' | <[-*+]> \h | \d+ '.' \h | ' ' ** 4 | \t | '```' ] }
}
role TinyMD::Actions {
method term:sym<markdown>($/) {
my $html = $<markdown-body>.made;
# Check if we are running under the modern Raku grammar (RakuAST)
if self.^name.starts-with('Raku::') {
use experimental :rakuast;
make RakuAST::StrLiteral.new($html);
}
else {
# Legacy QAST fallback for older Rakudo versions
use QAST:from<NQP>;
make QAST::SVal.new( :value($html) );
}
}
method markdown-body($/) { make $<block>.map(*.made).join("\n\n") }
method block:sym<hr>($/) { make "<hr>" }
method block:sym<header>($/) { make "<h{$<lvl>.chars}>" ~ $<text>.&inline ~ "</h{$<lvl>.chars}>" }
method block:sym<fenced>($/) { make "<pre><code" ~ ($<lang>.trim ?? " class=\"language-{$<lang>.trim}\"" !! "") ~ ">\n" ~ $<text>.&htmlscape ~ "</code></pre>" }
method block:sym<codeblock>($/) { make "<pre><code>\n" ~ $<text>.join("\n").&htmlscape ~ "\n</code></pre>" }
method block:sym<quote>($/) { make "<blockquote>\n" ~ $<text>.map({" <p>" ~ &.inline ~ "</p>"}).join("\n") ~ "\n</blockquote>" }
method block:sym<ul>($/) { make "<ul>\n" ~ $<text>.map({" <li>" ~ .&inline ~ "</li>"}).join("\n") ~ "\n</ul>" }
method block:sym<ol>($/) { make "<ol>\n" ~ $<text>.map({" <li>" ~ .&inline ~ "</li>"}).join("\n") ~ "\n</ol>" }
method block:sym<para>($/) { make "<p>\n " ~ $<text>.join("\n").&inline ~ "\n</p>" }
sub htmlscape($str) { $str.trans([ '<' , '>' , '&' ] => [ '<', '>', '&' ]) }
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(/ (\`+) \h* (.+?) \h* $0 /, -> $/ { "<code>" ~ $1.&htmlscape ~ "</code>" }, :g) # code
}
}
use Slangify TinyMD::Grammar, TinyMD::Actions;