NAME

Locale::Unicode::RBNF::Substitution - Substitutions of a rule-based number format rule

VERSION

v0.1.0

DESCRIPTION

Internal module. This is a port of ICU4C NFSubstitution and its subclasses SameValueSubstitution, MultiplierSubstitution, ModulusSubstitution, IntegralPartSubstitution, FractionalPartSubstitution, AbsoluteValueSubstitution and NumeratorSubstitution (nfsubs.cpp, ICU 79.1).

CONVENTIONS

The methods follow the ICU code they are ported from, and share these arguments:

  • $st

    A scalar reference that receives an error status, the equivalent of ICU UErrorCode. It holds a message starting with the ICU error code name, such as U_PARSE_ERROR: .... Most methods do nothing if it is already set.

  • $out

    A scalar reference to the string being built by formatting.

  • $pos

    The position in $out where the text produced is inserted. Rules are formatted by inserting their text into the output, then the text of their substitutions at their own offsets.

  • $rc

    The recursion count, checked against $Locale::Unicode::RBNF::RuleSet::RECURSION_LIMIT (64), as ICU does, to stop runaway rule descriptions.

  • $pp

    A parse position, as an hash reference with the keys index and error, the equivalent of ICU ParsePosition.

  • $mask

    The bit mask of the non-numerical rules already executed during a parse, used by ICU to prevent infinite recursion through them.

CLASSES

A substitution is the part of a rule text between two tokens, such as <<, >%%frac> or =#,##0=, which formats a value derived from the number, either with a rule set or with a Locale::Unicode::RBNF::DecimalFormat. The token character and the type of the rule decide the class used, as ICU NFSubstitution::makeSubstitution() does:

All classes inherit from Locale::Unicode::RBNF::Substitution, and override some of its methods, documented below for each class.

CONSTANTS

The rule types needed to choose the class of a substitution, duplicated from Locale::Unicode::RBNF::Rule to avoid a circular use: NEGATIVE_NUMBER (-1), IMPROPER_FRACTION (-2), PROPER_FRACTION (-3) and DEFAULT_RULE (-4).

The variable $DBL_MAX holds the largest double, and $MAX_INT64_IN_DOUBLE the largest integer a double holds exactly, 2^53 - 1.

CONSTRUCTORS

make

my $sub = Locale::Unicode::RBNF::Substitution->make( $pos, $rule, $predecessor, $rule_set, $formatter, $token, \$st );

This is ICU NFSubstitution::makeSubstitution(): it creates the substitution of the class matching the first character of the token and the type of the rule $rule, as described in "CLASSES", at the offset $pos of the rule text. $rule_set is the rule set owning the rule, $predecessor the preceding rule, used by >>>, and $formatter the Locale::Unicode::RBNF object, whose default rule set a numerator substitution uses.

It returns the substitution, or an empty list if the token is empty or an error occurred, in which case $st is set.

new

my $sub = Locale::Unicode::RBNF::Substitution::SameValue->new( $pos, $rule_set, $token, \$st );

This is the constructor of ICU NFSubstitution, shared by all classes. The token must begin and end with the same character. What is between them selects how the value is formatted: nothing, or > for >>>, means the rule set owning the rule, a name starting with % another rule set of the formatter, and a pattern starting with # or 0 a Locale::Unicode::RBNF::DecimalFormat.

Rule sets reference one another through substitutions, so the rule set is kept as a weak reference.

METHODS

calc_upper_bound

my $bound = $sub->calc_upper_bound( $old_upper_bound );

Returns the upper bound of the values the substitution can match during a parse. The base implementation returns the bound given.

compose_rule_value

my $value = $sub->compose_rule_value( $new, $old );

Combines the value parsed by the substitution, $new, with the value of the rule, $old. The base implementation returns $new.

do_parse

my( $success, $value ) = $sub->do_parse( $text, $pp, $base_value, $upper_bound, $lenient, $mask, $rc );

This is ICU NFSubstitution::doParse(): it parses the text with the rule set, or the decimal format, of the substitution, then composes the result with the base value given, with "compose_rule_value". It returns a list of 2 elements: whether something was matched, and the value.

This method is provided by Locale::Unicode::RBNF::PP, and used by the pure Perl parsing engine. The classes Modulus, FractionalPart and Numerator have their own version.

do_substitution_double

$sub->do_substitution_double( $number, $out, $pos, $rc, $st );

Transforms the number with "transform_double", then formats the result into $out, at $pos plus the offset of the substitution: with the rule set, in integer space if the result is an integer, or with the decimal format. An infinite result is formatted with the infinity rule of the rule set.

do_substitution_int

$sub->do_substitution_int( $number, $out, $pos, $rc, $st );

The same as "do_substitution_double", for an integer, transformed with "transform_int". Beyond 2^53, a decimal format formats the integer itself, rather than a double, so that its precision is kept.

get_pos

Returns the offset of the substitution in the rule text.

get_rule_set

Returns the rule set used by the substitution, or undef if it uses a decimal format.

is_modulus

Returns true for a modulus substitution, and false otherwise.

set_decimal_format_symbols

$sub->set_decimal_format_symbols( $symbols, \$st );

Passes new decimal format symbols to the decimal format of the substitution, if it has one.

set_divisor

$sub->set_divisor( $radix, $exponent, \$st );

Does nothing, except for multiplier and modulus substitutions.

to_string

Returns the token of the substitution in the ICU syntax, such as <%spellout-cardinal< or =#,##0=, built with "token_char".

token_char

Returns the character delimiting the token of the substitution: =, < or >. Each class defines it.

transform_double

my $n = $sub->transform_double( $number );

Returns the value derived from a double that the substitution formats. The base implementation returns the number unchanged.

transform_int

my $n = $sub->transform_int( $number );

Returns the value derived from an integer that the substitution formats. The base implementation returns the number unchanged.

Locale::Unicode::RBNF::Substitution::SameValue

The == substitution: the value is passed through unchanged.

new

The same as the base "new", except that == alone, without a rule set or a decimal format, is an error.

calc_upper_bound

Returns the bound given.

compose_rule_value

Returns the value parsed, $new.

token_char

Returns =.

transform_double

Returns the number unchanged.

transform_int

Returns the number unchanged.

Locale::Unicode::RBNF::Substitution::Multiplier

The << substitution of a normal rule: the number is divided by the divisor of the rule, and the quotient is formatted.

new

my $sub = Locale::Unicode::RBNF::Substitution::Multiplier->new( $pos, $rule, $rule_set, $token, \$st );

The same as the base "new", with the owning rule, from which the divisor is taken, as an additional argument. A divisor of 0 is an error.

calc_upper_bound

Returns the divisor.

compose_rule_value

Returns the value parsed, $new, multiplied by the divisor.

set_divisor

$sub->set_divisor( $radix, $exponent, \$st );

Sets the divisor to the radix raised to the power of the exponent, as an int64_t. A divisor of 0 is an error.

token_char

Returns <.

transform_double

Returns the number divided by the divisor, floored. The quotient is not floored when it is formatted with a decimal format, the owning rule has no modulus substitution, and the rounding mode of the formatter is not floor, so that the decimal format can round it.

transform_int

Returns the number divided by the divisor, truncated towards zero.

Locale::Unicode::RBNF::Substitution::Modulus

The >> substitution of a normal rule: the remainder of the division of the number by the divisor of the rule is formatted. With >>>, the preceding rule is used directly, rather than the rule set.

new

my $sub = Locale::Unicode::RBNF::Substitution::Modulus->new( $pos, $rule, $predecessor, $rule_set, $token, \$st );

The same as the base "new", with the owning rule, from which the divisor is taken, and the preceding rule, used for >>>, as additional arguments. A divisor of 0 is an error.

calc_upper_bound

Returns the divisor.

compose_rule_value

Returns the value of the rule, $old, less its remainder by the divisor, plus the value parsed, $new.

do_parse

The same as the base "do_parse", except that, with >>>, the text is parsed with the preceding rule directly. This method is provided by Locale::Unicode::RBNF::PP.

do_substitution_double

The same as the base "do_substitution_double", except that, with >>>, the remainder is formatted with the preceding rule directly.

do_substitution_int

The same as the base "do_substitution_int", except that, with >>>, the remainder is formatted with the preceding rule directly.

is_modulus

Returns true.

set_divisor

$sub->set_divisor( $radix, $exponent, \$st );

Sets the divisor to the radix raised to the power of the exponent, as an int64_t. A divisor of 0 is an error.

to_string

Returns >>> if the preceding rule is used directly, and otherwise the same as the base "to_string".

token_char

Returns >.

transform_double

Returns the remainder of the division of the number by the divisor, as fmod() does.

transform_int

Returns the remainder of the division of the number by the divisor, with the sign of the number, as in C.

Locale::Unicode::RBNF::Substitution::IntegralPart

The << substitution of a fraction rule: the integral part of the number is formatted.

calc_upper_bound

Returns the largest double, since the integral part has no upper bound.

compose_rule_value

Returns the sum of the value parsed and of the value of the rule.

token_char

Returns <.

transform_double

Returns the number floored.

transform_int

Returns the number unchanged.

Locale::Unicode::RBNF::Substitution::FractionalPart

The >> substitution of a fraction rule: the fractional part of the number is formatted, either digit by digit with the rule set owning the rule, for >> and >>>, or with a fraction rule set, such as with >%%frac>.

new

The same as the base "new". With >>, >>>, or the rule set owning the rule, the digits are formatted one by one, separated with a space except for >>>. Otherwise, the rule set given becomes a fraction rule set, with "make_into_fraction_rule_set" in Locale::Unicode::RBNF::RuleSet.

calc_upper_bound

Returns 0, which is not used.

compose_rule_value

Returns the sum of the value parsed and of the value of the rule.

do_parse

When the digits are formatted one by one, the text is parsed one digit at a time with the rule set, with an upper bound of 10, until the text no longer matches. Otherwise, this is the same as the base "do_parse". This method is provided by Locale::Unicode::RBNF::PP.

do_substitution_double

When the digits are formatted one by one, each digit of the fractional part, including the zeros after the decimal point, is formatted as an integer with the rule set, from the shortest decimal representation of the number rounded to 20 fraction digits; if there is none, 0 is formatted, so that the result does not end with the word for the decimal point. Otherwise, this is the same as the base "do_substitution_double".

do_substitution_int

Does nothing, as a fractional part substitution has nothing to format for an integer.

token_char

Returns >.

transform_double

Returns the fractional part of the number.

transform_int

Returns 0.

Locale::Unicode::RBNF::Substitution::AbsoluteValue

The >> substitution of a negative-number rule: the absolute value of the number is formatted.

calc_upper_bound

Returns the largest double.

compose_rule_value

Returns the opposite of the value parsed.

token_char

Returns >.

transform_double

Returns the absolute value of the number.

transform_int

Returns the absolute value of the number.

Locale::Unicode::RBNF::Substitution::Numerator

The << substitution of a rule in a fraction rule set: the numerator of the fraction whose denominator is the base value of the rule is formatted, with the default rule set of the formatter. When the token ends with <<, such as in <%%digits<<, the leading zeros of the decimal expansion are formatted too.

new

my $sub = Locale::Unicode::RBNF::Substitution::Numerator->new( $pos, $denominator, $rule_set, $token, \$st );

The same as the base "new", with the denominator as an additional argument, and the default rule set of the formatter as the rule set. Without a rule set, this is an error.

calc_upper_bound

Returns the denominator.

compose_rule_value

Returns the value parsed divided by the value of the rule. As in C++, a division by 0 yields an infinity, or NaN for 0 divided by 0.

do_parse

The same as the base "do_parse", without lenient matching, which does not suit the algorithm, and, with leading zeros, counting the zeros parsed first. This method is provided by Locale::Unicode::RBNF::PP.

do_substitution_double

Formats the numerator, preceded by the leading zeros when they are requested, each zero preceded by a space.

do_substitution_int

Does nothing, as a numerator substitution has nothing to format for an integer.

token_char

Returns <.

transform_double

Returns the number multiplied by the denominator, rounded to the nearest integer.

transform_int

Returns the number multiplied by the denominator.

SEE ALSO

Locale::Unicode::RBNF, Locale::Unicode::RBNF::Rule, Locale::Unicode::RBNF::RuleSet

AUTHOR

Jacques Deguest <jack@deguest.jp>

COPYRIGHT & LICENSE

Copyright(c) 2026 DEGUEST Pte. Ltd.

Portions are derived from ICU, Copyright (C) 2016 and later Unicode, Inc. and others, under the Unicode License (https://www.unicode.org/copyright.html). Its text is in the LICENSE-UNICODE file of this distribution.

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