The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Localizer::Scanner::Xslate;
use 5.008005;
use strict;
use warnings;

our $VERSION = "0.05";

sub DEBUG () { 0 }

use Localizer::Dictionary;

use Class::Accessor::Lite 0.05 (
    ro => [qw(parser)],
);

sub new {
    my $class = shift;
    my %args = @_==1 ? %{$_[0]} : @_;

    my $self = bless { }, $class;
    my $syntax = $args{syntax} || 'TTerse';
    $self->{parser} = $self->_build_parser($syntax);

    return $self;
}

our $RESULT;
our $FILENAME;

sub _build_parser {
    my ($self, $syntax) = @_;

    eval "use Text::Xslate::Syntax::${syntax};"; ## no critic
    die $@ if $@;

    "Text::Xslate::Syntax::${syntax}"->new(),
}

sub scan {
    my($self, $result, $filename, $data) = @_;
    my $ast = $self->parser->parse($data);
    local $FILENAME = $filename;
    local $RESULT = $result;
    $self->_walker($ast);
    return $result;
}

sub scan_file {
    my ($self, $result, $filename) = @_;
    open my $fh, '<:encoding(utf-8)', $filename
        or die "Cannot open file '$filename' for reading: $!";
    my $data = do { local $/; <$fh> };
    return $self->scan($result, $filename, $data);
}

my $sp = '';
sub _walker {
    my($self, $ast) = @_;
    $ast = [ $ast ] if $ast && ref($ast) eq 'Text::Xslate::Symbol';
    return unless $ast && ref($ast) eq 'ARRAY';

    for my $sym (@{ $ast }) {

        if ($sym->arity eq 'call' && $sym->value eq '(') {
            my $first = $sym->first;
            if ($first && ref($first) eq 'Text::Xslate::Symbol') {
                if ($first->arity eq 'variable' && $first->value eq 'l') {
                    my $second = $sym->second;
                    if ($second && ref($second) eq 'ARRAY' && $second->[0] && ref($second->[0]) eq 'Text::Xslate::Symbol') {
                        my $value = $second->[0];
                        if ($value->arity eq 'literal') {
                            $RESULT->add_entry_position($value->value, $FILENAME, $value->line);
                        }
                    }
                }
            }
        }

        unless (DEBUG) {
            $self->_walker($sym->first);
            $self->_walker($sym->second);
            $self->_walker($sym->third);
        } else {
            warn "$sp id: " . $sym->id;
            warn "$sp line: " . $sym->line;
            warn "$sp ldp: ". $sym->lbp;
            warn "$sp udp: ". $sym->ubp;
            warn "$sp type: ". $sym->type;

            warn "$sp arity: ". $sym->arity;
            warn "$sp assignment: ". $sym->assignment;
            warn "$sp value: ". $sym->value;

            warn "$sp first: " . $sym->first;
            $sp .= ' ';
            $self->_walker($sym->first);
            $sp =~ s/^.//;

            warn "$sp second: " . $sym->second;
            $sp .= ' ';
            $self->_walker($sym->second);
            $sp =~ s/^.//;

            warn "$sp third: " . $sym->third;
            $sp .= ' ';
            $self->_walker($sym->third);
            $sp =~ s/^.//;

            warn $sp . '----------';
        }
    }
}

1;
__END__

=for stopwords xslate foobar

=encoding utf-8

=head1 NAME

Localizer::Scanner::Xslate - Scanner for L<Text::Xslate> style file

=head1 SYNOPSIS

    use Localizer::Dictionary;
    use Localizer::Scanner::Xslate;

    my $result  = Localizer::Dictionary->new();
    my $scanner = Localizer::Scanner::Xslate->new(
        syntax => 'TTerse',
    );
    $scanner->scan_file($result, 'path/to/xslate.html');

=head1 DESCRIPTION

Localizer::Scanner::Xslate is localization tag scanner for Xslate templates.

This module finds C<< [% l("foo") %] >> style tags from xslate template files.

=head1 METHODS

=over 4

=item * Localizer::Scanner::Xslate(%args | \%args)

Constructor. It makes scanner instance.

e.g.

    my $ext = Localizer::Scanner::Xslate->new(
        syntax => 'Kolon', # => will use Text::Xslate::Syntax::Kolon
    );

=over 8

=item syntax: String

Specify syntax of L<Text::Xslate>. Default, this module uses L<Text::Xslate::Syntax::TTerse>.

=back

=item * $scanner->scan_file($result, $filename)

Scan file which is written by xslate.
C<$result> is the instance of L<Localizer::Dictionary> to store results.
C<$filename> is file name of the target to scan.

For example, if target file is follows;

    [% IF xxx == l('term') %]
    [% END %]

    [% l('hello') %]

Scanner uses C<l('foobar')> as C<msgid> (in this case, 'foobar' will be C<msgid>).

C<$result> will be like a following;

    {
        'term' => {
            'position' => [ [ 'path/to/xslate.html', 1 ] ]
        },
        'hello' => {
            'position' => [ [ 'path/to/xslate.html', 4 ] ]
        }
    }

=item * $scanner->scan($result, $filename, $data)

This method is almost the same as C<scan_file()>.
This method does not load file contents, it uses C<$data> as file contents instead.

=back

=head1 SEE ALSO

This module is based on L<Locale::Maketext::Extract::Plugin::Xslate>.

=head1 LICENSE

Copyright (C) Kazuhiro Osawa

This library is free software; you can redistribute it and/or modify
it under the same terms as Perl itself.

=head1 AUTHOR

Kazuhiro Osawa, Tokuhiro Matsuno E<lt>tokuhirom@gmail.comE<gt>

=cut