The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
package Text::LangTag;

our %script_var = (     # use lowercase
    'fonipa' => 'latn',
    'fonupa' => 'latn',
    '1901' => 'latn',
    '1996' => 'latn',
    'monoton' => 'grek',
    'polyton' => 'grek',
    );

sub parse
{
    my ($class, $str) = @_;
    my (@list) = split('-', lc($str));
    my ($i, $res);

    if (length($list[0]) != 4 && !defined $script_var{$list[0]})
    {
        $res->{'lang'} = shift(@list);
        while (length($list[0]) == 3)
        { $res->{'extlang'}{shift(@list)} = 1; }
    }
    if (length($list[0]) == 4)
    { $res->{'script'} = shift(@list); }
    if (length($list[0]) == 2)
    { $res->{'region'} = shift(@list); }
    while (length($list[0]) > 4 && length($list[0]) < 9 || ($list[0] =~ m/^[0-9].{3}$/o))
    { $res->{'variant'}{shift(@list)} = 1; }
    while (length($list[0]) == 1)   # too loose, but hey
    { $res->{'extension'}{shift(@list) . '-' . shift(@list)} = 1; }
    return bless $res, $class;
}

sub to_string
{
    my ($tag) = @_;
    my (@res);

    push (@res, $tag->{'lang'}) if ($tag->{'lang'});
    push (@res, sort keys %{$tag->{'extlang'}}) if ($tag->{'extlang'});
    if ($tag->{'script'})
    {
        my ($str) = $tag->{'script'};
        $str =~ s/^(.)/uc($1)/oe;
        push (@res, $str);
    }
    push (@res, uc($tag->{'region'})) if ($tag->{'region'});
    push (@res, sort keys %{$tag->{'variant'}}) if ($tag->{'variant'});
    push (@res, sort keys %{$tag->{'extension'}}) if ($tag->{'extension'});
    return join('-', @res);
}

sub suppress
{
    my ($base, $script, $suppress) = @_;

    if ($script)
    {
        $base->{'script'} = $script->{'script'} if ($script->{'script'});
        $base->{'region'} = $script->{'region'} unless (defined $base->{'region'} || !defined $script->{'region'});
        foreach (grep {defined $script_var{$_}} keys %{$base->{'variant'}})
        { delete $base->{'variant'}{$_}; }
        foreach (keys %{$script->{'variant'}})
        {
            $base->{'variant'}{$_} = 1;
            $suppress->{'script'} = $script_var{$_};        # imply script suppression
        }
    }
    if ($suppress)
    {
        foreach (grep {defined $script_var{$_}} keys %{$suppress->{'variant'}})
        { $suppress->{'script'} = $script_var{$_}; }
        return if ($base->{'script'} && $base->{'script'} ne $suppress->{'script'});
        delete $base->{'script'};
        foreach (keys %{$suppress->{'variant'}})
        { delete $base->{'variant'}{$_}; }
        delete $base->{'region'} if (defined $base->{'region'} && $base->{'region'} eq $suppress->{'region'});
    }
}

sub just_script
{
    my ($base) = @_;
    my ($res);

    $res->{'script'} = $base->{'script'};
    foreach (grep {defined $script_var{$_}} keys %{$base->{'variant'}})
    { $res->{'variant'}{$_} = 1; }
    $res->{'region'} = $base->{'region'};
    return bless $res, ref $base;
}

sub no_script
{
    my ($base) = @_;
    my ($res) = bless {%$base}, ref $base;

    delete $res->{'script'};
    delete $res->{'region'};
    foreach (grep {defined $script_var{$_}} keys %{$base->{'variant'}})
    { delete $res->{'variant'}{$_}; }
    return $res;
}

1;