<commit_before>package Mojo::Template;

use strict;
use warnings;

use base 'Mojo::Base';

use Carp 'croak';
use Encode qw/decode encode/;
use IO::File;
use Mojo::ByteStream;
use Mojo::Exception;

use constant CHUNK_SIZE => $ENV{MOJO_CHUNK_SIZE} || 262144;

__PACKAGE__->attr([qw/auto_escape compiled namespace/]);
__PACKAGE__->attr([qw/append code prepend/] => '');
__PACKAGE__->attr(capture_end               => 'end');
__PACKAGE__->attr(capture_start             => 'begin');
__PACKAGE__->attr(comment_mark              => '#');
__PACKAGE__->attr(encoding                  => 'UTF-8');
__PACKAGE__->attr(escape_mark               => '=');
__PACKAGE__->attr(expression_mark           => '=');
__PACKAGE__->attr(line_start                => '%');
__PACKAGE__->attr(tag_start                 => '<%');
__PACKAGE__->attr(tag_end                   => '%>');
__PACKAGE__->attr(template                  => '');
__PACKAGE__->attr(tree => sub { [] });
__PACKAGE__->attr(trim_mark => '=');

# Helpers
my $HELPERS = <<'EOF';
use Mojo::ByteStream 'b';
no strict 'refs'; no warnings 'redefine';
sub block;
*block = sub { shift->(@_) };
sub escape;
*escape = sub {
    ref $_[0] && ref $_[0] eq 'Mojo::ByteStream'
      ? "$_[0]"
      : b($_[0])->xml_escape->to_string;
};
use strict; use warnings;
EOF
$HELPERS =~ s/\n//g;

sub build {
    my $self = shift;

    # Compile
    my @lines;
    my $cpst;
    for my $line (@{$self->tree}) {

        # New line
        push @lines, '';
        for (my $j = 0; $j < @{$line}; $j += 2) {
            my $type  = $line->[$j];
            my $value = $line->[$j + 1];

            # Need to fix line ending
            $value ||= '';
            my $newline = chomp $value;

            # Capture end
            if ($type eq 'cpen') {

                # End block
                $lines[-1] .= 'return $_M }';

                # No following code
                my $next = $line->[$j + 3];
                $lines[-1] .= ';' if !defined $next || $next =~ /^\s*$/;
            }

            # Text
            if ($type eq 'text') {

                # Quote and fix line ending
                $value = quotemeta($value);
                $value .= '\n' if $newline;

                $lines[-1] .= "\$_M .= \"" . $value . "\";";
            }

            # Code
            if ($type eq 'code') { $lines[-1] .= "$value" }

            # Expression
            if ($type eq 'expr' || $type eq 'escp') {

                # Escaped
                my $a = $self->auto_escape;
                if (($type eq 'escp' && !$a) || ($type eq 'expr' && $a)) {
                    $lines[-1] .= "\$_M .= escape";
                    $lines[-1] .= " +$value" if length $value;
                }

                # Raw
                else { $lines[-1] .= "\$_M .= $value" }

                # Append semicolon
                $lines[-1] .= ';' unless $cpst;
            }

            # Capture started
            if ($cpst) {
                $lines[-1] .= $cpst;
                $cpst = undef;
            }

            # Capture start
            if ($type eq 'cpst') {

                # Start block
                $cpst = " sub { my \$_M = ''; ";
            }
        }
    }

    # Wrap
    my $prepend   = $self->prepend;
    my $append    = $self->append;
    my $namespace = $self->namespace || ref $self;
    $lines[0] ||= '';
    $lines[0] =
      "package $namespace; $HELPERS sub { my \$_M = ''; $prepend; do {"
      . $lines[0];
    $lines[-1] .= qq/$append; \$_M; } };/;

    $self->code(join "\n", @lines);
    return $self;
}

sub compile {
    my $self = shift;

    # Shortcut
    my $code = $self->code;
    return unless $code;

    # Compile
    my $compiled = eval $code;

    # Exception
    return Mojo::Exception->new($@, $self->template)->verbose(1) if $@;

    $self->compiled($compiled);
    return;
}

sub interpret {
    my $self = shift;

    # Compile
    unless ($self->compiled) {
        my $e = $self->compile;

        # Exception
        return $e if ref $e;
    }
    my $compiled = $self->compiled;

    # Shortcut
    return unless $compiled;

    # Interpret
    my $output = eval { $compiled->(@_) };
    $output = Mojo::Exception->new($@, $self->template)->verbose(1) if $@;

    return $output;
}

# I am so smart! I am so smart! S-M-R-T! I mean S-M-A-R-T...
sub parse {
    my ($self, $tmpl) = @_;
    $self->template($tmpl);

    # Clean start
    delete $self->{tree};

    # Tags
    my $line_start    = quotemeta $self->line_start;
    my $tag_start     = quotemeta $self->tag_start;
    my $tag_end       = quotemeta $self->tag_end;
    my $cmnt          = quotemeta $self->comment_mark;
    my $escp          = quotemeta $self->escape_mark;
    my $expr          = quotemeta $self->expression_mark;
    my $trim          = quotemeta $self->trim_mark;
    my $capture_start = quotemeta $self->capture_start;
    my $capture_end   = quotemeta $self->capture_end;

    # DEPRECATED in Comet!
    # Use "block" and "end" instead of "{" and "}"
    my $mixed_re = qr/
        (
        $tag_start$expr$escp                 # Escaped expression
        |
        $tag_start$expr                      # Expression<commit_msg>fixed typo<commit_after>package Mojo::Template;

use strict;
use warnings;

use base 'Mojo::Base';

use Carp 'croak';
use Encode qw/decode encode/;
use IO::File;
use Mojo::ByteStream;
use Mojo::Exception;

use constant CHUNK_SIZE => $ENV{MOJO_CHUNK_SIZE} || 262144;

__PACKAGE__->attr([qw/auto_escape compiled namespace/]);
__PACKAGE__->attr([qw/append code prepend/] => '');
__PACKAGE__->attr(capture_end               => 'end');
__PACKAGE__->attr(capture_start             => 'begin');
__PACKAGE__->attr(comment_mark              => '#');
__PACKAGE__->attr(encoding                  => 'UTF-8');
__PACKAGE__->attr(escape_mark               => '=');
__PACKAGE__->attr(expression_mark           => '=');
__PACKAGE__->attr(line_start                => '%');
__PACKAGE__->attr(tag_start                 => '<%');
__PACKAGE__->attr(tag_end                   => '%>');
__PACKAGE__->attr(template                  => '');
__PACKAGE__->attr(tree => sub { [] });
__PACKAGE__->attr(trim_mark => '=');

# Helpers
my $HELPERS = <<'EOF';
use Mojo::ByteStream 'b';
no strict 'refs'; no warnings 'redefine';
sub block;
*block = sub { shift->(@_) };
sub escape;
*escape = sub {
    ref $_[0] && ref $_[0] eq 'Mojo::ByteStream'
      ? "$_[0]"
      : b($_[0])->xml_escape->to_string;
};
use strict; use warnings;
EOF
$HELPERS =~ s/\n//g;

sub build {
    my $self = shift;

    # Compile
    my @lines;
    my $cpst;
    for my $line (@{$self->tree}) {

        # New line
        push @lines, '';
        for (my $j = 0; $j < @{$line}; $j += 2) {
            my $type  = $line->[$j];
            my $value = $line->[$j + 1];

            # Need to fix line ending
            $value ||= '';
            my $newline = chomp $value;

            # Capture end
            if ($type eq 'cpen') {

                # End block
                $lines[-1] .= 'return $_M }';

                # No following code
                my $next = $line->[$j + 3];
                $lines[-1] .= ';' if !defined $next || $next =~ /^\s*$/;
            }

            # Text
            if ($type eq 'text') {

                # Quote and fix line ending
                $value = quotemeta($value);
                $value .= '\n' if $newline;

                $lines[-1] .= "\$_M .= \"" . $value . "\";";
            }

            # Code
            if ($type eq 'code') { $lines[-1] .= "$value" }

            # Expression
            if ($type eq 'expr' || $type eq 'escp') {

                # Escaped
                my $a = $self->auto_escape;
                if (($type eq 'escp' && !$a) || ($type eq 'expr' && $a)) {
                    $lines[-1] .= "\$_M .= escape";
                    $lines[-1] .= " +$value" if length $value;
                }

                # Raw
                else { $lines[-1] .= "\$_M .= $value" }

                # Append semicolon
                $lines[-1] .= ';' unless $cpst;
            }

            # Capture started
            if ($cpst) {
                $lines[-1] .= $cpst;
                $cpst = undef;
            }

            # Capture start
            if ($type eq 'cpst') {

                # Start block
                $cpst = " sub { my \$_M = ''; ";
            }
        }
    }

    # Wrap
    my $prepend   = $self->prepend;
    my $append    = $self->append;
    my $namespace = $self->namespace || ref $self;
    $lines[0] ||= '';
    $lines[0] =
      "package $namespace; $HELPERS sub { my \$_M = ''; $prepend; do {"
      . $lines[0];
    $lines[-1] .= qq/$append; \$_M; } };/;

    $self->code(join "\n", @lines);
    return $self;
}

sub compile {
    my $self = shift;

    # Shortcut
    my $code = $self->code;
    return unless $code;

    # Compile
    my $compiled = eval $code;

    # Exception
    return Mojo::Exception->new($@, $self->template)->verbose(1) if $@;

    $self->compiled($compiled);
    return;
}

sub interpret {
    my $self = shift;

    # Compile
    unless ($self->compiled) {
        my $e = $self->compile;

        # Exception
        return $e if ref $e;
    }
    my $compiled = $self->compiled;

    # Shortcut
    return unless $compiled;

    # Interpret
    my $output = eval { $compiled->(@_) };
    $output = Mojo::Exception->new($@, $self->template)->verbose(1) if $@;

    return $output;
}

# I am so smart! I am so smart! S-M-R-T! I mean S-M-A-R-T...
sub parse {
    my ($self, $tmpl) = @_;
    $self->template($tmpl);

    # Clean start
    delete $self->{tree};

    # Tags
    my $line_start    = quotemeta $self->line_start;
    my $tag_start     = quotemeta $self->tag_start;
    my $tag_end       = quotemeta $self->tag_end;
    my $cmnt          = quotemeta $self->comment_mark;
    my $escp          = quotemeta $self->escape_mark;
    my $expr          = quotemeta $self->expression_mark;
    my $trim          = quotemeta $self->trim_mark;
    my $capture_start = quotemeta $self->capture_start;
    my $capture_end   = quotemeta $self->capture_end;

    # DEPRECATED in Comet!
    # Use "begin" and "end" instead of "{" and "}"
    my $mixed_re = qr/
        (
        $tag_start$expr$escp                 # Escaped expression
        |
        $tag_start$expr                      # Expression
{'fraction_non_alphanumeric': 0.17226445962852469, 'fraction_numerical': 0.00577422769704552, 'mean_word_length': 1.6537282941777325, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 131, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 2}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3058050', 'n_tokens_mistral': 3541, 'n_tokens_neox': 3374, 'n_words': 810}