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 )