##
# doc {
# my_elem {
# # children go here
# };
# };
##
##
# my_elem {
# text "some text";
# my_attr_ "value";
# };
##
##
our $__frag; # points to fragment under active construction
sub doc(&) {
my ($content_fn) = @_;
local $__frag = [undef,undef,undef];
$content_fn->();
$__frag->[2][0];
}
sub _elem {
my ($elem_name, $content_fn) = @_;
# an element is represented by the triple [name, attrs, children]
my $elem = [$elem_name, undef, undef];
do { local $__frag = $elem; $content_fn->() };
push @{$__frag->[2]}, $elem;
}
sub _attr {
my ($attr_name, $val) = @_;
push @{$__frag->[1]}, [$attr_name, $val];
}
sub text($) {
push @{$__frag->[2]}, @_;
}
##
##
sub define_vocabulary {
my ($elems, $attrs) = @_;
eval "sub $_(&) { _elem('$_',\@_) }" for @$elems;
eval "sub ${_}_(\$) { _attr('$_',\@_) }" for @$attrs;
}
##
##
BEGIN {
define_vocabulary(
[qw( html head title body h1 h2 h3 p img br )],
[qw( src href class style )]
);
}
##
##
my $my_doc = doc {
html {
head { title { text "Title" } };
body {
p { class_ "warning"; text "paragraph" }
};
}
};
use Data::Dumper;
$Data::Dumper::Indent = $Data::Dumper::Terse = 1;
print Dumper $my_doc;
# [
# 'html',
# undef,
# [
# [
# 'head',
# undef,
# [
# [
# 'title',
# undef,
# [
# 'Title'
# ]
# ]
# ]
# ],
# [
# 'body',
# undef,
# [
# [
# 'p',
# [
# [
# 'class',
# 'warning'
# ]
# ],
# [
# 'paragraph'
# ]
# ]
# ]
# ]
# ]
# ]
##
##
use XML::Writer;
sub render_via_xml_writer {
my $doc = shift;
my $writer = XML::Writer->new(@_); # extra args go to ->new()
my $render_fn;
$render_fn = sub {
my $frag = shift;
my ($elem, $attrs, $children) = @$frag;
$writer->startTag( $elem, map {@$_} @$attrs );
for (@$children) {
ref() ? $render_fn->($_) : $writer->characters($_);
}
$writer->endTag($elem);
};
$render_fn->($doc);
$writer->end();
}
##
##
render_via_xml_writer( $my_doc, DATA_MODE => 1, UNSAFE => 1 );
#
#
# Title
#
#
# paragraph
#
#
##
##
sub render_doc(&) {
my $docfn = shift;
render_via_xml_writer(
doc( \&$docfn ),
DATA_MODE => 1,
UNSAFE => 1
);
}
##
##
render_doc {
html {
head {
title { text "My grand document!" }
};
body {
h1 { text "Heading" };
p {
class_ "first"; # attribute class="first"
text "This is the first paragraph!";
style_ "font: bold"; # another attr
};
# it's just Perl, so we can mix in other code
for (2..5) {
p { text "Plus paragraph number $_." }
}
};
};
};
#
#
# My grand document!
#
#
# Heading
# This is the first paragraph!
# Plus paragraph number 2.
# Plus paragraph number 3.
# Plus paragraph number 4.
# Plus paragraph number 5.
#
#