icu_plurals/provider/rules/
mod.rs

1// This file is part of ICU4X. For terms of use, please see the file
2// called LICENSE at the top level of the ICU4X source tree
3// (online at: https://github.com/unicode-org/icu4x/blob/main/LICENSE ).
4
5//! 🚧 \[Unstable\] APIs and Data Structures for Plural Rules
6//!
7//! A single Plural Rule is an expression which tests the value of [`PluralOperands`]
8//! against a condition. If the condition is truthful, then the [`PluralCategory`]
9//! to which the Rule is assigned should be used.
10//!
11//! <div class="stab unstable">
12//! 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
13//! including in SemVer minor releases. In particular, the `DataProvider` implementations are only
14//! guaranteed to match with this version's `*_unstable` providers. Use with caution.
15//! </div>
16//!
17//! # Examples
18//!
19//! In this example we're going to examine the AST, parsing and resolving of a
20//! set of English Cardinal Plural Rules.
21//!
22//! A CLDR JSON source contains the following entry:
23//!
24//! ```json
25//! {
26//!   "one": "i = 1 and v = 0 @integer 1",
27//!   "other": " @integer 0, 2~16, 100, 1000, 10000, 100000, 1000000, … @decimal 0.0~1.5, 10.0, 100.0, 1000.0, 10000.0, 100000.0, 1000000.0, …"
28//! }
29//! ```
30//!
31//! When the user provides a number for which the [`PluralCategory`] is to be selected,
32//! the system will examine a rule for each category in order, and stop on the first
33//! category which matches.
34//!
35//! In our example, the user provided an input value `1`.
36//! That value expanded into [`PluralOperands`] might look something like this, in its
37//! internal representation of plural operand values, or something logically equivalent:
38//!
39//! ```text
40//! PluralOperands {
41//!     i: 1,
42//!     v: 0,
43//!     w: 0,
44//!     f: 0,
45//!     t: 0,
46//!     c: 0,
47//! };
48//! ```
49//!
50//! Now, the system will parse the first rule, assigned to [`PluralCategory::One`], and
51//! test if it matches.
52//!
53//! The value of the rule is:
54//!
55//! ```text
56//! i = 1 and v = 0 @integer 1
57//! ```
58//!
59//! The [`Rule`] contains a [`Condition`] `i = 1 and v = 0` and a [`Sample`] `@integer 1`.
60//!
61//! When parsed, the resulting [`AST`] will look like this:
62//!
63//! ```
64//! use icu::plurals::provider::rules::reference::ast::*;
65//! use icu::plurals::provider::rules::reference::parse_condition;
66//!
67//! let input = "i = 1 and v = 0 @integer 1";
68//!
69//! let ast = parse_condition(input.as_bytes()).expect("Parsing failed.");
70//! assert_eq!(
71//!     ast,
72//!     Condition(vec![AndCondition(vec![
73//!         Relation {
74//!             expression: Expression {
75//!                 operand: Operand::I,
76//!                 modulus: None,
77//!             },
78//!             operator: Operator::Eq,
79//!             range_list: RangeList(vec![RangeListItem::Value(Value(1))])
80//!         },
81//!         Relation {
82//!             expression: Expression {
83//!                 operand: Operand::V,
84//!                 modulus: None,
85//!             },
86//!             operator: Operator::Eq,
87//!             range_list: RangeList(vec![RangeListItem::Value(Value(0))])
88//!         },
89//!     ]),])
90//! );
91//! ```
92//!
93//! Finally, we can pass this [`AST`] (in fact, just the [`Condition`] node),
94//! to a resolver alongside the [`PluralOperands`] to test if the Rule
95//! matches:
96//!
97//! ```
98//! use icu::plurals::provider::rules::reference::parse_condition;
99//! use icu::plurals::provider::rules::reference::test_condition;
100//! use icu::plurals::PluralOperands;
101//!
102//! let input = "i = 1 and v = 0 @integer 1";
103//!
104//! let operands = PluralOperands::from(1_u32);
105//!
106//! let ast = parse_condition(input.as_bytes()).expect("Parsing failed.");
107//!
108//! assert!(test_condition(&ast, &operands));
109//! ```
110//!
111//! Since the rule for [`PluralCategory::One`] matches, we will return this category.
112//! Otherwise, we'd test the next rule, in this case [`PluralCategory::Other`], which has an
113//! empty [`Condition`], meaning that it'll match all operands.
114//!
115//! # Summary
116//!
117//! For [`PluralRuleType::Cardinal`] in English, we can restate the rule's logic as:
118//!
119//! When the `PluralOperands::i` is `1` and `PluralOperands::v` is `0` (or equivalent thereof), [`PluralCategory::One`]
120//! should be used, otherwise [`PluralCategory::Other`] should be used.
121//!
122//! For other locales, there are different/more [`PluralCategories`] defined in the `PluralRules` (see [`PluralRules::categories`]),
123//! and possibly more complicated [`Rules`] therein.
124//!
125//! # Difference between Category and Number
126//!
127//! While in English [`PluralCategory::One`] overlaps with an integer value `1`,
128//! in other languages, the category may be used for other numbers as well.
129//!
130//! For example, in Russian [`PluralCategory::One`] matches numbers such as `11`, `21`, `121` etc.
131//!
132//! # Runtime vs. Resolver Rules
133//!
134//! `ICU4X` provides two sets of rules AST and APIs to manage it:
135//!  * `reference` is the canonical implementation of the specification intended for
136//!    tooling and testing to use.
137//!    This module provides APIs for parsing, editing and serialization of the rules.
138//!  * `runtime` is a non-public, non-mutable runtime version optimized for
139//!    performance and low memory overhead. This version provides
140//!    runtime resolver used by the `PluralRules` itself.
141//!
142//! [`PluralCategory`]: super::PluralCategory
143//! [`PluralCategories`]: super::PluralCategory
144//! [`PluralCategory::One`]: super::PluralCategory::One
145//! [`PluralCategory::Other`]: super::PluralCategory::Other
146//! [`PluralOperands`]: super::PluralOperands
147//! [`PluralRules::categories`]: super::PluralRules::categories
148//! [`PluralRuleType::Cardinal`]: crate::PluralRuleType::Cardinal
149//! [`Rule`]: super::rules::reference::ast::Rule
150//! [`Rules`]: super::rules::reference::ast::Rule
151//! [`Condition`]: super::rules::reference::ast::Condition
152//! [`Sample`]: super::rules::reference::ast::Samples
153//! [`AST`]: super::rules::reference::ast
154
155pub mod runtime;
156
157// The reference module is used internally, but is only needed externally in datagen mode
158pub mod reference;