CodeGen-Cpppp
view release on metacpan or search on metacpan
lib/CodeGen/Cpppp/Template.pm view on Meta::CPAN
our $_next_pkg= 1;
sub _create_derived_package($class, $cpppp_ver, $parse_data) {
my $pkg= 'CodeGen::Cpppp::Template::_'.$_next_pkg++;
no strict 'refs';
@{"${pkg}::ISA"}= ( $class );
${"${pkg}::cpppp_version"}= $cpppp_ver;
$pkg->_init_parse_data($parse_data);
}
sub _setup_derived_package($class, $pkg, $cpppp_ver) {
strict->import;
warnings->import;
utf8->import;
experimental->import(qw( lexical_subs signatures postderef ));
no strict 'refs';
@{"${pkg}::ISA"}= ( $class ) unless @{"${pkg}::ISA"};
${"${pkg}::cpppp_version"}= $cpppp_ver;
}
sub _init_parse_data($class, $parse_data) {
no strict 'refs';
${"${class}::_parse_data"}= $parse_data;
# Create accessors for all of the attributes declared in the template.
for (keys $parse_data->{template_parameter}->%*) {
my $name= $_;
*{"${class}::$name"}= sub { $_[0]{$name} };
}
# Expose all of the functions declared in the template
for (keys $parse_data->{template_method}->%*) {
my $name= $_;
*{"${class}::$name"}= sub {
my $m= shift->{template_method}{$name}
or croak "Template execution did not define method '$name'";
goto $m;
};
}
$class;
}
sub cpppp_version($class) {
no strict 'refs';
${"${class}::cpppp_version"} // __PACKAGE__->VERSION
}
sub _gen_perl_scope_functions($class, $cpppp_ver) {
return (
'# line '. (__LINE__+1) . ' "' . __FILE__ . '"',
'my sub param { unshift @_, $self; goto $self->can("_init_param") }',
'my sub define { unshift @_, $self; goto $self->can("define_template_macro") }',
'my sub section { unshift @_, $self; goto $self->can("current_output_section") }',
'my sub template { unshift @_, $self->context; goto $self->context->can("new_template") }',
'my $trim_comma= CodeGen::Cpppp::AntiCharacter->new(qr/,/, qr/\s*/);',
'my $trim_ws= CodeGen::Cpppp::AntiCharacter->new(qr/\s*/);',
);
}
sub _gen_BUILD_method($class, $cpppp_ver, $perl, $src_filename, $src_lineno) {
return
"sub ${class}::BUILD(\$self, \$constructor_parameters=undef) {",
" Scalar::Util::weaken(\$self);",
# Inject all the lexical functions that need to be in scope
$class->_gen_perl_scope_functions($cpppp_ver),
qq{# line $src_lineno "$src_filename"},
$perl,
"}",
}
sub _build_BUILD_method($class, $version, $perl, $src_filename, $src_lineno) {
{
no strict 'refs';
croak "${class}::BUILD is already defined" if defined &{$class.'::BUILD'};
}
croak "Compile failed for ${class}::BUILD() : $@"
unless eval join "\n",
$class->_gen_BUILD_method($version, $perl, $src_lineno, $src_filename),
'1';
}
sub context { $_[0]{context} }
sub output { $_[0]->flush->{output} }
sub current_output_section($self, $new=undef) {
if (defined $new) {
$self->output->has_section($new)
or croak "No defined output section '$new'";
$self->_finish_render;
$self->{current_output_section}= $new;
}
$self->{current_output_section};
}
sub autocolumn { $_[0]{autocolumn} = $_[1]||0 if @_ > 1; $_[0]{autocolumn} }
sub autocomma { $_[0]{autocomma} = $_[1]||0 if @_ > 1; $_[0]{autocomma} }
sub autoindent { $_[0]{autoindent} = $_[1]||0 if @_ > 1; $_[0]{autoindent} }
sub autostatementline { $_[0]{autostatementline}= $_[1]||0 if @_ > 1; $_[0]{autostatementline} }
sub indent { $_[0]{indent} = $_[1] if @_ > 1; $_[0]{indent} }
sub emit_POD { $_[0]{emit_POD} = $_[1]||0 if @_ > 1; $_[0]{emit_POD} }
sub _parse_data($class) {
$class = ref $class if ref $class;
no strict 'refs';
return ${"${class}::_parse_data"};
}
sub new($class, @args) {
no strict 'refs';
my %attrs= @args == 1 && ref $args[0]? $args[0]->%*
: !(@args&1)? @args
: croak "Expected even-length list or hashref";
my $parse= $class->_parse_data;
# Make sure each attr is the correct type of ref, for the params.
for (keys %attrs) {
if (my $p= $parse->{template_parameter}{$_}) {
if ($p eq '@') { ref $attrs{$_} eq 'ARRAY' or croak("Expected ARRAY for parameter $_"); }
elsif ($p eq '%') { ref $attrs{$_} eq 'HASH' or croak("Expected HASH for parameter $_"); }
}
else {
croak("Unknown parameter '$_' to template $parse->{filename}")
unless $class->can($_);
}
}
my $self= bless {
autocomma => 1,
autostatementline => 1,
(map +($_ => $parse->{$_}||0), qw(
autoindent autocolumn convert_linecomment_to_c89
)),
indent => $parse->{indent},
output => CodeGen::Cpppp::Output->new,
current_output_section => 'private',
%attrs,
}, $class;
Scalar::Util::weaken($self->{context})
if $self->{context};
$self->BUILD(\%attrs);
$self->flush;
}
sub coerce_parameters($class, $params) {
my %ret;
my $parse= $class->_parse_data;
for my $k (keys $parse->{template_parameter}->%*) {
my $p= $parse->{template_parameter}{$k};
my $v= $params->{$p.$k} // $params->{$k};
next unless defined $v;
if ($p eq '@') {
$v= ref $v eq 'HASH'? [ keys %$v ] : [ $v ]
unless ref $v eq 'ARRAY';
} elsif ($p eq '%') {
# If it isn't a hash, treat it like a list that needs added to a set
$v= { map +($_ => 1), ref $v eq 'ARRAY'? @$v : ($v) }
unless ref $v eq 'HASH';
}
$ret{$k}= $v;
}
\%ret;
}
sub _init_param($self, $name, $ref, @initial_value) {
if (exists $self->{$name}) {
# Assign the value received from constructor to the variable in the template
ref $ref eq 'SCALAR'? ($$ref= $self->{$name})
: ref $ref eq 'ARRAY' ? (@$ref= @{$self->{$name} || []})
: ref $ref eq 'HASH' ? (%$ref= %{$self->{$name} || {}})
: croak "Unhandled ref type ".ref($ref);
} else {
ref $ref eq 'SCALAR'? ($$ref= $initial_value[0])
: ref $ref eq 'ARRAY' ? (@$ref= @initial_value)
: ref $ref eq 'HASH' ? (%$ref= @initial_value)
: croak "Unhandled ref type ".ref($ref);
}
# Now store the variable of the template directly into this hash
ref $ref eq 'SCALAR'? Hash::Util::hv_store(%$self, $name, $$ref)
: ($self->{$name}= $ref);
$ref;
}
sub flush($self) {
$self->_finish_render;
$self;
}
sub define_template_macro($self, $name, $code) {
$self->{template_macro}{$name}= $code;
}
sub define_template_method($self, $name, $code) {
$self->{template_method}{$name}= $code;
( run in 1.762 second using v1.01-cache-2.11-cpan-39bf76dae61 )