Text-Xslate
view release on metacpan or search on metacpan
lib/Text/Xslate/Compiler.pm view on Meta::CPAN
if(not $list_op) {
push @code, $self->opcode('push');
}
return @code;
}
sub _cat_files {
my($self, $files) = @_;
my $engine = $self->engine || $self->_error("No Xslate engine which header/footer requires");
my $s = '';
foreach my $file(@{$files}) {
my $fullpath = $engine->find_file($file)->{fullpath};
$s .= $engine->slurp_template( $self->input_layer, $fullpath );
$self->requires($fullpath);
}
return $s;
}
our $_lv = -1;
sub compile_ast {
my($self, $ast) = @_;
return if not defined $ast;
local $_lv = $_lv + 1 if _DUMP_GEN;
my @code;
foreach my $node(ref($ast) eq 'ARRAY' ? @{$ast} : $ast) {
Scalar::Util::blessed($node) or Carp::confess("[BUG] Not a node object: " . p($node));
printf STDERR "%s"."generate %s (%s)\n", "." x $_lv, $node->arity, $node->id if _DUMP_GEN;
my $generator = $self->can('_generate_' . $node->arity)
|| Carp::confess("[BUG] Unexpected node: " . p($node));
push @code, $self->$generator($node);
}
return @code;
}
sub _process_cascade {
my($self, $cascade, $args, $main_code) = @_;
printf STDERR "# cascade %s %s", $self->file, $cascade->dump if _DUMP_CAS;
my $engine = $self->engine
|| $self->_error("Cannot cascade templates without Xslate engine", $cascade);
my($base_file, $base_code);
my $base = $cascade->first;
my @components = $cascade->second
? (map{ $self->_bare_to_file($_) } @{$cascade->second})
: ();
my $vars = $cascade->third;
if(defined $base) { # pure cascade
$base_file = $self->_bare_to_file($base);
$base_code = $engine->load_file($base_file);
$self->requires( $engine->find_file($base_file)->{fullpath} );
}
else { # overlay
$base_file = $args->{file}; # only for error messages
$base_code = $main_code;
if(defined $args->{fullpath}) {
$self->requires( $args->{fullpath} );
}
push @{$main_code}, $self->_flush_macro_table();
}
foreach my $cfile(@components) {
my $code = $engine->load_file($cfile);
my $fullpath = $engine->find_file($cfile)->{fullpath};
my $mtable = $self->macro_table;
my $macro;
foreach my $c(@{$code}) {
# $c = [name, arg, line, file, symbol ]
# retrieve macros from assembly code
if($c->[_OP_NAME] eq 'macro_begin' .. $c->[_OP_NAME] eq 'macro_end') {
if($c->[_OP_NAME] eq 'macro_begin') {
$macro = [];
$macro = {
name => $c->[_OP_ARG],
line => $c->[_OP_LINE],
file => $c->[_OP_FILE],
body => [],
};
push @{ $mtable->{$c->[_OP_ARG]} ||= [] }, $macro;
}
elsif($c->[_OP_NAME] eq 'macro_nargs') {
$macro->{nargs} = $c->[_OP_ARG];
}
elsif($c->[_OP_NAME] eq 'macro_outer') {
$macro->{outer} = $c->[_OP_ARG];
}
elsif($c->[_OP_NAME] eq 'macro_end') {
# noop
}
else {
push @{$macro->{body}}, $c;
}
}
elsif($c->[_OP_NAME] eq 'depend') {
$self->requires($c->[_OP_ARG]);
}
}
$self->requires($fullpath);
$self->_process_cascade_file($cfile, $base_code);
}
if(defined $base) { # pure cascade
$self->_process_cascade_file($base_file, $base_code);
if(defined $vars) {
unshift @{$base_code}, $self->_localize_vars($vars);
}
foreach my $c(@{$main_code}) {
if($c->[_OP_NAME] eq 'print_raw_s'
&& $c->[_OP_ARG] =~ m{ [^ \t\r\n] }xms) {
Carp::carp("Xslate: Useless use of text '$c->[1]'");
}
}
@{$main_code} = @{$base_code};
}
else { # overlay
return;
}
}
sub _process_cascade_file {
my($self, $file, $base_code) = @_;
printf STDERR "# cascade file %s\n", p($file) if _DUMP_CAS;
my $mtable = $self->macro_table;
for(my $i = 0; $i < @{$base_code}; $i++) {
my $c = $base_code->[$i];
if($c->[_OP_NAME] ne 'macro_begin') {
next;
}
# macro
my $name = $c->[_OP_ARG];
$name =~ s/\@.+$//;
printf STDERR "# macro %s\n", $name if _DUMP_CAS;
if(exists $mtable->{$name}) {
my $m = $mtable->{$name};
if(ref($m) ne 'HASH') {
$self->_error('[BUG] Unexpected macro structure: '
. p($m) );
}
$self->_error(
"Redefinition of macro/block $name in " . $file
. " (you must use block modifiers to override macros/blocks)",
$m->{line}
);
}
my $before = delete $mtable->{$name . '@before'};
my $around = delete $mtable->{$name . '@around'};
my $after = delete $mtable->{$name . '@after'};
if(defined $before) {
my $n = scalar @{$base_code};
foreach my $m(@{$before}) {
splice @{$base_code}, $i+1, 0, @{$m->{body}};
}
$i += scalar(@{$base_code}) - $n;
}
my $macro_start = $i+1;
$i++ while($base_code->[$i][_OP_NAME] ne 'macro_end'); # move to the end
if(defined $around) {
my @original = splice @{$base_code}, $macro_start, ($i - $macro_start);
$i = $macro_start;
my @body;
foreach my $m(@{$around}) {
push @body, @{$m->{body}};
}
for(my $j = 0; $j < @body; $j++) {
if($body[$j][_OP_NAME] eq 'super') {
splice @body, $j, 1, @original;
( run in 1.092 second using v1.01-cache-2.11-cpan-f56aa216473 )