NAME
Locale::Unicode::RBNF::PP - Pure-Perl fallback for the Locale::Unicode::RBNF XS layer
VERSION
v0.1.0
DESCRIPTION
This module is loaded automatically by Locale::Unicode::RBNF when the XS shared object cannot be loaded, such as when the distribution was installed without a C compiler, or when the environment variable PERL_LOCALE_UNICODE_RBNF_PP is set to a true value.
It holds the pure Perl implementation of the parsing code path, a port of ICU4C 79.1, which the XS layer otherwise provides in C. All functions defined here are injected directly into the namespace of the class they belong to, so that callers see them transparently as methods.
Loading it while the XS layer is in use only installs those methods, and does not replace the C parsing engine; the test suite relies on this to compare both engines.
You should not normally load or call this module directly.
FUNCTIONS
Each function is a port of an ICU method, injected into a class as shown below. They are documented here as they are defined, with $self being an object of that class; see "CONVENTIONS" in Locale::Unicode::RBNF::Rule for the arguments they share.
decimal_format_parse
Installed as "parse" in Locale::Unicode::RBNF::DecimalFormat.
my $value = $decimal_format->parse( $text, $pp );
Parses a number at $pp->{index}. On success, the index is moved past the number; otherwise, it is left unchanged and the error index is set. Returns the value.
fractional_part_do_parse
Installed as do_parse in "Locale::Unicode::RBNF::Substitution::FractionalPart" in Locale::Unicode::RBNF::Substitution.
my( $success, $value ) = $sub->do_parse( $text, $pp, $base_value, $upper_bound, $lenient, $mask, $rc );
This is ICU FractionalPartSubstitution::doParse(): in "by digits" mode, the text is parsed one digit at a time with the owning rule set, using an upper bound of 10, until the text no longer matches.
modulus_do_parse
Installed as do_parse in "Locale::Unicode::RBNF::Substitution::Modulus" in Locale::Unicode::RBNF::Substitution.
my( $success, $value ) = $sub->do_parse( $text, $pp, $base_value, $upper_bound, $lenient, $mask, $rc );
This is ICU ModulusSubstitution::doParse(): a >>> substitution uses the doParse() of the preceding rule, and does some of the work of NFRuleSet::parse() itself.
numerator_do_parse
Installed as do_parse in "Locale::Unicode::RBNF::Substitution::Numerator" in Locale::Unicode::RBNF::Substitution.
my( $success, $value ) = $sub->do_parse( $text, $pp, $base_value, $upper_bound, $lenient, $mask, $rc );
This is ICU NumeratorSubstitution::doParse(). Lenient parsing is turned off here, as ICU does, since it seriously disrupts the algorithm.
plural_format_parse_type
Installed as "parse_type" in Locale::Unicode::RBNF::PluralFormat.
my( $begin, $end ) = $plural_format->parse_type( $source, $rule, $starting_at );
This is ICU PluralFormat::parseType(): it finds the longest sub-message occurring in $source at or after $starting_at, and returns its beginning and its end, or ( -1, -1 ) if none is found.
rbnf_parse_text
Installed as _parse_text in Locale::Unicode::RBNF when the pure Perl engine is in use.
my( $result, $index, $error_index ) = $formatter->_parse_text( $text );
This is ICU RuleBasedNumberFormat::parse(): every public and parseable rule set is tried, and the one matching the most characters wins. It returns the value, the number of characters matched, and the error index, as the C engine does; "parse" in Locale::Unicode::RBNF derives its own return values from them.
rule_all_ignorable
Installed as _all_ignorable in Locale::Unicode::RBNF::Rule.
my $bool = $rule->_all_ignorable( $string );
This is ICU NFRule::allIgnorable(): it returns true if the string is empty. Collation is not available, so no other string is ignorable.
rule_do_parse
Installed as "do_parse" in Locale::Unicode::RBNF::Rule.
my( $success, $value ) = $rule->do_parse( $text, $pp, $is_fraction_rule, $upper_bound, $mask, $rc );
This is ICU NFRule::doParse(): it strips the text before the first substitution, matches the substitutions up to their delimiters, and returns whether something was matched, and the value parsed. $pp->{index} is set to the number of characters matched.
rule_find_text
Installed as _find_text in Locale::Unicode::RBNF::Rule.
my( $position, $length ) = $rule->_find_text( $string, $key, $starting_at );
This is ICU NFRule::findText(): it returns the position and the length of the key in the string, at or after $starting_at, or ( -1, 0 ) if it is not found. If the rule text has a plural sub-message, the key is matched with "parse_type" in Locale::Unicode::RBNF::PluralFormat, together with the rule text around the sub-message. Otherwise, the key is searched as is, then, if the formatter is lenient, with "rule_find_text_lenient".
rule_find_text_lenient
Installed as _find_text_lenient in Locale::Unicode::RBNF::Rule.
my( $position, $length ) = $rule->_find_text_lenient( $string, $key, $starting_at );
This is ICU NFRule::findTextLenient(): it tries "rule_prefix_length_at" at each successive position. The result only depends on the arguments and on the formatter settings, which do not change during a parse, so it is memoised for the duration of a parse.
rule_find_text_lenient_uncached
Installed as _find_text_lenient_uncached in Locale::Unicode::RBNF::Rule.
The same as "rule_find_text_lenient", without the memoisation.
rule_match_to_delimiter
Installed as _match_to_delimiter in Locale::Unicode::RBNF::Rule.
my $value = $rule->_match_to_delimiter( $text, $start_pos, $base_value, $delimiter, $pp, $sub, $mask, $rc, $upper_bound );
This is ICU NFRule::matchToDelimiter(): it searches the text for the delimiter, and uses the substitution $sub to match what precedes it. Without a delimiter, the substitution matches as much text as it can. It returns the value, and sets $pp.
rule_prefix_length
Installed as _prefix_length in Locale::Unicode::RBNF::Rule.
my $length = $rule->_prefix_length( $string, $prefix );
This is ICU NFRule::prefixLength(): it returns how many characters at the start of the string match the prefix, or 0 if they do not. In lenient mode, the comparison is made after NFC normalisation and simple case folding, Turkic for Turkish and Azerbaijani, and ignores separators, as ICU 79 does.
rule_prefix_length_at
Installed as _prefix_length_at in Locale::Unicode::RBNF::Rule.
my $length = $rule->_prefix_length_at( $string, $offset, $prefix );
The same as "rule_prefix_length", for the text of the string starting at $offset, which avoids copying the text at every step.
rule_set_parse
Installed as "parse" in Locale::Unicode::RBNF::RuleSet.
my( $success, $value ) = $rule_set->parse( $text, $pp, $upper_bound, $mask, $rc );
This is ICU NFRuleSet::parse(): it matches the text against each rule, and keeps the result of the rule that matched the most characters.
rule_strip_prefix
Installed as _strip_prefix in Locale::Unicode::RBNF::Rule.
$rule->_strip_prefix( \$text, $prefix, $pp );
This is ICU NFRule::stripPrefix(): if the text starts with the prefix, as matched by "rule_prefix_length", the prefix is removed from the text, and $pp->{index} is advanced by its length.
substitution_do_parse
Installed as "do_parse" in Locale::Unicode::RBNF::Substitution.
my( $success, $value ) = $sub->do_parse( $text, $pp, $base_value, $upper_bound, $lenient, $mask, $rc );
This is ICU NFSubstitution::doParse(): it parses with the rule set or the decimal format, then composes the partial result with the value passed in.
SEE ALSO
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.