=encoding utf8
=head1 NAME
pod/markdown - Render parsed POD documents as Markdown.
=head1 SYNOPSIS
from pod/parser import parse_pod;
from pod/markdown import PodMarkdown;
let doc := parse_pod("=head1 NAME\n\nExample\n\n=cut\n");
say( ( new PodMarkdown() ).render(doc) );
=head1 DESCRIPTION
This pure-Zuzu module renders C<pod/parser> C<PodDocument> objects to
Markdown. It renders headings, paragraphs, verbatim blocks, lists,
items, and Markdown-targeted C<=for> and C<=begin> blocks.
=head1 EXPORTED CLASSES
=over
=item C<PodMarkdown>
Renderer class with C<render> and C<render_node> methods.
=back
=head1 COPYRIGHT AND LICENCE
B<< pod/markdown >> is copyright Toby Inkster.
It is free software; you may redistribute it and/or modify it under
the terms of either the Artistic License 1.0 or the GNU General Public
License version 2.
=cut
from pod/parser import PodDocument, PodNode;
from std/string import index, join, replace, split, substr, trim;
function _repeat ( String text, Number count ) {
let out := "";
let i := 0;
while ( i < count ) {
out _= text;
i++;
}
return out;
}
function _nonempty_push ( Array out, text ) {
if ( text != null and trim(text) ne "" ) {
out.push(text);
}
}
function _indent_block ( String text, String prefix ) {
let out := [];
for ( let line in split( text, "\n" ) ) {
out.push(prefix _ line);
}
return join( "\n", out );
}
function _split_link ( String inner ) {
let pipe := index( inner, "|" );
if ( pipe < 0 ) {
return [ trim(inner), trim(inner) ];
}
return [
trim( substr( inner, 0, pipe ) ),
trim( substr( inner, pipe + 1 ) ),
];
}
function _slug ( String text ) {
let out := "";
let dash := false;
let source := lc(text);
let i := 0;
while ( i < length source ) {
let ch := substr( source, i, 1 );
if ( ch ~ /^[a-z0-9]$/ ) {
out _= ch;
dash := false;
}
else if ( out ne "" and not dash ) {
out _= "-";
dash := true;
}
i++;
}
return substr( out, 0, length out - 1 ) if dash;
return out;
}
function _module_search_url ( String module ) {
return "https://zuzulang.org/modules?q="
_ replace( module, "/", "%2F", "g" )
_ "&direct=1";
}
function _link_target ( String raw ) {
let target := trim(raw);
return target if target ~ /^[A-Za-z][A-Za-z0-9+.-]*:/;
if ( length target > 0 and substr( target, 0, 1 ) eq "#" ) {
return target;
}
if ( length target > 0 and substr( target, 0, 1 ) eq "/" ) {
return "#" _ _slug( substr( target, 1 ) );
}
if ( index( target, "/" ) >= 0 ) {
return _module_search_url(target);
}
return "#" _ _slug(target);
}
function _link_label ( String label ) {
let text := trim(label);
if ( length text > 0 and substr( text, 0, 1 ) eq "/" ) {
return substr( text, 1 );
}
return text;
}
function _markdown_link ( String inner ) {
let parts := _split_link(inner);
let label := _link_label(parts[0]);
let target := parts[1];
label := target if label eq "";
return "[" _ label _ "](" _ _link_target(target) _ ")";
}
function _format_inner ( String marker, String inner, Boolean trim_inner ) {
let text := trim_inner ? trim(inner) : inner;
if ( marker eq "C" ) {
return "`" _ trim(text) _ "`";
}
if ( marker eq "B" ) {
return "**" _ text _ "**";
}
if ( marker eq "I" ) {
return "*" _ text _ "*";
}
if ( marker eq "L" ) {
return _markdown_link(text);
}
return text;
}
function _inline ( text ) {
let source := "" _ text;
let out := "";
let i := 0;
while ( i < length source ) {
let marker := substr( source, i, 1 );
if (
( marker eq "C" or marker eq "B" or marker eq "I" or marker eq "L" )
and i + 1 < length source
and substr( source, i + 1, 1 ) eq "<"
) {
let delimiter := 1;
while (
i + 1 + delimiter < length source
and substr( source, i + 1 + delimiter, 1 ) eq "<"
) {
delimiter++;
}
let start := i + 1 + delimiter;
let close := index( source, _repeat( ">", delimiter ), start );
if ( close >= 0 ) {
out _= _format_inner(
marker,
substr( source, start, close - start ),
delimiter > 1,
);
i := close + delimiter;
next;
}
}
out _= substr( source, i, 1 );
i++;
}
return out;
}
class PodMarkdown {
let Boolean include_for_markdown := true;
method render ( PodDocument document ) {
return join( "\n\n", self._render_children( document, 0 ) );
}
method render_node ( PodNode node ) {
return self._render_node( node, 0 );
}
method _render_children ( PodNode parent, Number depth ) {
let out := [];
for ( let child in parent.children() ) {
_nonempty_push( out, self._render_node( child, depth ) );
}
return out;
}
method _render_node ( PodNode node, Number depth ) {
let kind := node.type();
if ( kind eq "document" ) {
return join( "\n\n", self._render_children( node, depth ) );
}
if ( kind eq "encoding" or kind eq "pod" or kind eq "command" ) {
return "";
}
if ( kind eq "heading" ) {
let level := node.level();
level := 1 if level < 1;
level := 6 if level > 6;
return _repeat( "#", level ) _ " " _ _inline( node.text() );
}
if ( kind eq "paragraph" ) {
return _inline( node.text() );
}
if ( kind eq "verbatim" ) {
return _indent_block( node.text(), _repeat( " ", depth + 1 ) );
}
if ( kind eq "list" ) {
return join( "\n", self._render_list_items( node, depth ) );
}
if ( kind eq "item" ) {
return self._render_item( node, depth );
}
if ( kind eq "for" ) {
return "" if not include_for_markdown;
return node.target() eq "markdown" ? node.text() : "";
}
if ( kind eq "block" ) {
return "" if node.target() ne "markdown";
return join( "\n\n", self._render_children( node, depth ) );
}
return _inline( node.text() );
}
method _render_list_items ( PodNode list, Number depth ) {
let out := [];
for ( let child in list.children() ) {
if ( child.type() eq "item" ) {
_nonempty_push( out, self._render_item( child, depth ) );
}
else {
_nonempty_push( out, self._render_node( child, depth + 1 ) );
}
}
return out;
}
method _render_item ( PodNode item, Number depth ) {
let prefix := _repeat( " ", depth ) _ "- ";
let parts := [];
let body := _inline( item.text() );
body := " " if body eq "";
parts.push(prefix _ body);
for ( let child in item.children() ) {
let rendered := self._render_node( child, depth + 1 );
if ( trim(rendered) ne "" ) {
parts.push(
child.type() eq "verbatim"
? rendered
: _indent_block( rendered, _repeat( " ", depth + 1 ) ),
);
}
}
return join( "\n\n", parts );
}
}
modules/pod/markdown.zzm
pod-parser-0.0.1 source code
Package
- Name
- pod-parser
- Version
- 0.0.1
- Uploaded
- 2026-05-28 11:45:33
- Dependencies
-
-
std/getopt>= 0 -
std/io>= 0 -
std/string>= 0 -
std/tui>= 0
-
- Metadata
- zuzu-distribution.json
- Archive
- Download .tar.gz