Text::XslateでCソースを吐くときにケツカンマを防止する方法

この記事はPerl Advent Calendar 2016の17日目の記事です。

YAPC::Hokkaido 2016 SAPPOROの前夜祭で言い忘れたのですが、
楽器アプリを作る工程の半分以上はPerlを書いていて、
基本的にパラメータテーブルはText::Xslateで出力しています。

ただ、個人的にケツカンマは好きじゃないので、
レンダリング前に処理する必要があって、これ面倒だなーって思ってたので、
metacpanのText::Xslateでドキュメントを漁ってたら、あっさり解決しました。

use v5.16;
use strict;
use warnings;

use Text::Xslate;

# 出力したいパラメータ
my @params = map {
    $_ - 8;
} 0..15;

my $template = join( '', <DATA> );

my $tx = Text::Xslate->new();
print $tx->render_string( $template, {
    src1 => format_as_int(\@params, 8),
    src2 => format_as_hex(\@params, 4),
} );

sub format_as_int {
    my ( $params, $n ) = @_;

    my @tmp = @{$params};
    my @ret = ();
    while ( @tmp ) {
        my @items = map {
            sprintf( '%3d', $_ );
        } splice(@tmp, 0, $n);
        push @ret, \@items;
    }

    return \@ret;
}

sub format_as_hex {
    my ( $params, $n ) = @_;

    my @tmp = @{$params};
    my @ret = ();    
    while ( @tmp ) {
        my @items = map {
            # 負の数を許容するややこしい書き方
            my $str = sprintf( '%04X', $_ );
            $str = substr($str, -4) if 4 < length($str);
            "0x${str}";
        } splice(@tmp, 0, $n);
        push @ret, \@items;
    }

    return \@ret;
}

__DATA__
static const int16_t foo[] = {
: for $src1 -> $item {
    <: $item.join(', ') :><: if not $~item.is_last { ',' } :>
: }
};

static const int16_t bar[] = {
: for $src2 -> $item {
    <: $item.join(', ') :><: if not $~item.is_last { ',' } :>
: }
};
$ perl aaa.pl
static const int16_t foo[] = {
     -8,  -7,  -6,  -5,  -4,  -3,  -2,  -1,
      0,   1,   2,   3,   4,   5,   6,   7
};

static const int16_t bar[] = {
    0xFFF8, 0xFFF9, 0xFFFA, 0xFFFB,
    0xFFFC, 0xFFFD, 0xFFFE, 0xFFFF,
    0x0000, 0x0001, 0x0002, 0x0003,
    0x0004, 0x0005, 0x0006, 0x0007
};

ポイントは、 <: if not $~item.is_last { ',' } :> ですね。
最後の要素じゃないときのみ、カンマを付加するようにしています。

それと、カンマ区切りで出力するのに使ってる、
<: $item.join(', ') :> も、すっごい便利。

おしまい。

Leave a Comment