icu_plurals/provider/rules/reference/
ast.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//! [`AST`](self) provides a set of Syntax Tree Nodes used to store
6//! the output of [`parse`] method that is used in [`test_condition`] method
7//! to evaluate whether a given [`PluralCategory`] should be used.
8//!
9//! <div class="stab unstable">
10//! 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
11//! including in SemVer minor releases. In particular, the `DataProvider` implementations are only
12//! guaranteed to match with this version's `*_unstable` providers. Use with caution.
13//! </div>
14//!
15//! # Examples
16//!
17//! ```
18//! use icu::plurals::provider::rules::reference::ast::*;
19//! use icu::plurals::provider::rules::reference::parse_condition;
20//!
21//! let input = "i = 1";
22//!
23//! let ast = parse_condition(input.as_bytes()).expect("Parsing failed.");
24//!
25//! assert_eq!(
26//!     ast,
27//!     Condition(vec![AndCondition(vec![Relation {
28//!         expression: Expression {
29//!             operand: Operand::I,
30//!             modulus: None,
31//!         },
32//!         operator: Operator::Eq,
33//!         range_list: RangeList(vec![RangeListItem::Value(Value(1))])
34//!     }])])
35//! );
36//! ```
37//!
38//! [`PluralCategory`]: crate::PluralCategory
39//! [`parse`]: super::parse()
40//! [`test_condition`]: super::test_condition()
41use alloc::string::String;
42use alloc::vec::Vec;
43use core::ops::RangeInclusive;
44
45/// A complete AST representation of a plural rule.
46/// Comprises a vector of [`AndConditions`] and optionally a set of [`Samples`].
47///
48/// <div class="stab unstable">
49/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
50/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
51/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
52/// </div>
53///
54/// # Examples
55///
56/// ```
57/// use icu::plurals::provider::rules::reference::ast::*;
58/// use icu::plurals::provider::rules::reference::{parse, parse_condition};
59///
60/// let condition =
61///     parse_condition(b"i = 5 or v = 2").expect("Parsing failed.");
62///
63/// let samples = Samples {
64///     integer: Some(SampleList {
65///         sample_ranges: vec![SampleRange {
66///             lower_val: DecimalValue("2".to_string()),
67///             upper_val: None,
68///         }],
69///         ellipsis: true,
70///     }),
71///     decimal: Some(SampleList {
72///         sample_ranges: vec![SampleRange {
73///             lower_val: DecimalValue("2.5".to_string()),
74///             upper_val: None,
75///         }],
76///         ellipsis: false,
77///     }),
78/// };
79///
80/// let rule = Rule {
81///     condition,
82///     samples: Some(samples),
83/// };
84///
85/// assert_eq!(
86///     rule,
87///     parse("i = 5 or v = 2 @integer 2, … @decimal 2.5".as_bytes())
88///         .expect("Parsing failed")
89/// )
90/// ```
91///
92/// [`AndConditions`]: AndCondition
93#[derive(Debug, Clone, PartialEq)]
94#[allow(clippy::exhaustive_structs)] // this type is stable
95pub struct Rule {
96    /// The set of conditions that each must be satisfied for the entire `Rule` to be satisfied
97    pub condition: Condition,
98    /// The set of sample numerical values matching each plural category that has a rule, or `None` if not present.
99    pub samples: Option<Samples>,
100}
101
102/// A complete AST representation of a plural rule's condition. Comprises a vector of [`AndConditions`].
103///
104/// <div class="stab unstable">
105/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
106/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
107/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
108/// </div>
109///
110/// # Examples
111///
112/// ```
113/// use icu::plurals::provider::rules::reference::ast::*;
114/// use icu::plurals::provider::rules::reference::parse_condition;
115///
116/// let condition = Condition(vec![
117///     AndCondition(vec![Relation {
118///         expression: Expression {
119///             operand: Operand::I,
120///             modulus: None,
121///         },
122///         operator: Operator::Eq,
123///         range_list: RangeList(vec![RangeListItem::Value(Value(5))]),
124///     }]),
125///     AndCondition(vec![Relation {
126///         expression: Expression {
127///             operand: Operand::V,
128///             modulus: None,
129///         },
130///         operator: Operator::Eq,
131///         range_list: RangeList(vec![RangeListItem::Value(Value(2))]),
132///     }]),
133/// ]);
134///
135/// assert_eq!(
136///     condition,
137///     parse_condition(b"i = 5 or v = 2").expect("Parsing failed")
138/// )
139/// ```
140///
141/// [`AndConditions`]: AndCondition
142#[derive(Debug, Clone, PartialEq)]
143#[allow(clippy::exhaustive_structs)] // this type is stable
144pub struct Condition(pub Vec<AndCondition>);
145
146/// An incomplete AST representation of a plural rule. Comprises a vector of [`Relations`].
147///
148/// <div class="stab unstable">
149/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
150/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
151/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
152/// </div>
153///
154/// # Examples
155///
156/// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the
157/// AST using the [`parse()`](crate::provider::rules::reference::parser::parse()) function.
158///
159/// ```text
160/// "i = 3 and v = 0"
161/// ```
162///
163/// Can be represented by the AST:
164///
165/// ```
166/// use icu::plurals::provider::rules::reference::ast::*;
167///
168/// AndCondition(vec![
169///     Relation {
170///         expression: Expression {
171///             operand: Operand::I,
172///             modulus: None,
173///         },
174///         operator: Operator::Eq,
175///         range_list: RangeList(vec![RangeListItem::Value(Value(5))]),
176///     },
177///     Relation {
178///         expression: Expression {
179///             operand: Operand::V,
180///             modulus: None,
181///         },
182///         operator: Operator::NotEq,
183///         range_list: RangeList(vec![RangeListItem::Value(Value(2))]),
184///     },
185/// ]);
186/// ```
187///
188/// [`Relations`]: Relation
189#[derive(Debug, Clone, PartialEq)]
190#[allow(clippy::exhaustive_structs)] // this type is stable
191pub struct AndCondition(pub Vec<Relation>);
192
193/// An incomplete AST representation of a plural rule. Comprises an [`Expression`], an [`Operator`], and a [`RangeList`].
194///
195/// <div class="stab unstable">
196/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
197/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
198/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
199/// </div>
200///
201/// # Examples
202///
203/// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the
204/// AST using the [`parse()`](crate::provider::rules::reference::parser::parse()) function.
205///
206/// ```text
207/// "i = 3"
208/// ```
209///
210/// Can be represented by the AST:
211///
212/// ```
213/// use icu::plurals::provider::rules::reference::ast::*;
214///
215/// Relation {
216///     expression: Expression {
217///         operand: Operand::I,
218///         modulus: None,
219///     },
220///     operator: Operator::Eq,
221///     range_list: RangeList(vec![RangeListItem::Value(Value(3))]),
222/// };
223/// ```
224#[derive(Debug, Clone, PartialEq)]
225#[allow(clippy::exhaustive_structs)] // this type is stable
226pub struct Relation {
227    /// The plural operand variable that optionally includes an application of modulo arithmetic.
228    pub expression: Expression,
229    /// The operator (equals, not equals) indicating whether the resolved expression value
230    /// at runtime should match the set of possible values in `range_list`. Note: `Operator::Eq`
231    /// effectively means "is contained within the set of".
232    pub operator: Operator,
233    /// A sequence of `RangeListItem`, each of which represents a scalar number or a numerical range,
234    /// that creates the interval set within which `expression`'s resolved value should exist.
235    pub range_list: RangeList,
236}
237
238/// An enum of [`Relation`] operators for plural rules.
239///
240/// Each Operator enumeration belongs to the corresponding symbolic operators:
241///
242/// | Enum Operator | Symbolic Operator | Meaning                                        |
243/// | --------------| ----------------- |------------------------------------------------|
244/// | `Eq`          | "="               | is contained within the following interval set |
245/// | `NotEq`       | "!="              | complement of `Eq` ("is _not_ contained..."")  |
246///
247/// <div class="stab unstable">
248/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
249/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
250/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
251/// </div>
252#[derive(Debug, Clone, Copy, PartialEq)]
253#[allow(clippy::exhaustive_enums)] // this type is stable
254pub enum Operator {
255    /// In a plural rule [`Relation`], represents that the plural operand [`Expression`]'s value at
256    /// should be contained within the [`RangeList`] interval set.
257    Eq,
258    /// The opposite of `Eq` -- that the plural operand [`Expression`]'s value at
259    /// _should not be contained_ within the [`RangeList`] interval set.
260    NotEq,
261}
262
263/// An incomplete AST representation of a plural rule. Comprises an [`Operand`] and an optional modulo.
264///
265/// <div class="stab unstable">
266/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
267/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
268/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
269/// </div>
270///
271/// # Examples
272///
273/// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the
274/// AST using the [`parse()`](crate::provider::rules::reference::parser::parse()) function.
275///
276/// ```text
277/// "i % 100"
278/// ```
279///
280/// Can be represented by the AST:
281///
282/// ```
283/// use icu::plurals::provider::rules::reference::ast::*;
284///
285/// Expression {
286///     operand: Operand::I,
287///     modulus: Some(Value(100)),
288/// };
289/// ```
290#[derive(Debug, Clone, PartialEq)]
291#[allow(clippy::exhaustive_structs)] // this type is stable
292pub struct Expression {
293    /// The plural operand under test in this expression.
294    pub operand: Operand,
295    /// An optional modulo arithmetic base value when modulo arithmetic should be applied to the
296    /// value of `operand`, otherwise `None`.
297    pub modulus: Option<Value>,
298}
299
300/// An incomplete AST representation of a plural rule. Comprises a [`char`].
301///
302/// <div class="stab unstable">
303/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
304/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
305/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
306/// </div>
307///
308/// # Examples
309///
310/// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the
311/// AST using the [`parse()`](crate::provider::rules::reference::parser::parse()) function.
312///
313/// ```text
314/// "i"
315/// ```
316///
317/// Can be represented by the AST:
318///
319/// ```
320/// use icu::plurals::provider::rules::reference::ast::Operand;
321///
322/// Operand::I;
323/// ```
324#[derive(Debug, Clone, Copy, PartialEq)]
325#[allow(clippy::exhaustive_enums)] // this type is stable
326pub enum Operand {
327    /// Absolute value of input
328    N,
329    /// An integer value of input with the fraction part truncated off
330    I,
331    /// Number of visible fraction digits with trailing zeros
332    V,
333    /// Number of visible fraction digits without trailing zeros
334    W,
335    /// Visible fraction digits with trailing zeros
336    F,
337    /// Visible fraction digits without trailing zeros
338    T,
339    /// Compact decimal exponent value:
340    ///   exponent of the power of 10 used in compact decimal formatting
341    C,
342    /// Currently, synonym for ā€˜c’. however, may be redefined in the future
343    E,
344}
345
346/// An incomplete AST representation of a plural rule. Comprises a vector of [`RangeListItems`].
347///
348/// <div class="stab unstable">
349/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
350/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
351/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
352/// </div>
353///
354/// # Examples
355///
356/// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the
357/// AST using the [`parse()`](crate::provider::rules::reference::parser::parse()) function.
358///
359/// ```text
360/// "5, 7, 9"
361/// ```
362///
363/// Can be represented by the AST:
364///
365/// ```
366/// use icu::plurals::provider::rules::reference::ast::*;
367///
368/// RangeList(vec![
369///     RangeListItem::Value(Value(5)),
370///     RangeListItem::Value(Value(7)),
371///     RangeListItem::Value(Value(9)),
372/// ]);
373/// ```
374///
375/// [`RangeListItems`]: RangeListItem
376#[derive(Debug, Clone, PartialEq)]
377#[allow(clippy::exhaustive_structs)] // this type is stable
378pub struct RangeList(pub Vec<RangeListItem>);
379
380/// An enum of items that appear in a [`RangeList`]: `Range` or a `Value`.
381///
382/// See [`RangeInclusive`] and [`Value`] for additional details.
383/// A range comprises two values: an inclusive lower and upper limit.
384///
385/// <div class="stab unstable">
386/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
387/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
388/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
389/// </div>
390///
391/// # Examples
392///
393/// ```text
394/// 5
395/// 11..15
396/// ```
397///
398/// Can be represented by the AST:
399///
400/// ```
401/// use icu::plurals::provider::rules::reference::ast::*;
402///
403/// let _ = RangeListItem::Value(Value(5));
404/// let _ = RangeListItem::Range(Value(11)..=Value(15));
405/// ```
406#[derive(Debug, Clone, PartialEq)]
407#[allow(clippy::exhaustive_enums)] // this type is stable
408pub enum RangeListItem {
409    /// An interval of numerical values (inclusive of both interval boundaries).
410    Range(RangeInclusive<Value>),
411    /// A single scalar numerical value.
412    Value(Value),
413}
414
415/// An incomplete AST representation of a plural rule, representing one integer.
416///
417/// <div class="stab unstable">
418/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
419/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
420/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
421/// </div>
422///
423/// # Examples
424///
425/// All AST nodes can be built explicitly, as seen in the example. However, due to its complexity, it is preferred to build the
426/// AST using the [`parse()`](crate::provider::rules::reference::parser::parse()) function.
427///
428/// ```text
429/// "99"
430/// ```
431///
432/// Can be represented by the AST:
433///
434/// ```
435/// use icu::plurals::provider::rules::reference::ast::*;
436///
437/// RangeListItem::Value(Value(99));
438/// ```
439#[derive(Debug, Clone, PartialEq, PartialOrd)]
440#[allow(clippy::exhaustive_structs)] // this type is stable
441pub struct Value(pub u64);
442
443/// A sample of example values that match the given rule.
444///
445/// <div class="stab unstable">
446/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
447/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
448/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
449/// </div>
450///
451/// # Examples
452///
453/// ```text
454/// @integer 2, … @decimal 2.5
455/// ```
456///
457/// ```
458/// use icu::plurals::provider::rules::reference::ast::*;
459/// Samples {
460///     integer: Some(SampleList {
461///         sample_ranges: vec![SampleRange {
462///             lower_val: DecimalValue("2".to_string()),
463///             upper_val: None,
464///         }],
465///         ellipsis: true,
466///     }),
467///     decimal: Some(SampleList {
468///         sample_ranges: vec![SampleRange {
469///             lower_val: DecimalValue("2.5".to_string()),
470///             upper_val: None,
471///         }],
472///         ellipsis: false,
473///     }),
474/// };
475/// ```
476#[derive(Debug, Clone, PartialEq)]
477#[allow(clippy::exhaustive_structs)] // this type is stable
478pub struct Samples {
479    /// The list of integer samples provided (denoted
480    /// [in LDML by `@integer`](http://unicode.org/reports/tr35/tr35-numbers.html#Samples)).
481    pub integer: Option<SampleList>,
482    /// The list of samples with decimal fractions provided (denoted
483    /// [in LDML by `@decimal`](http://unicode.org/reports/tr35/tr35-numbers.html#Samples)).
484    pub decimal: Option<SampleList>,
485}
486
487/// A list of values used in samples.
488///
489/// <div class="stab unstable">
490/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
491/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
492/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
493/// </div>
494///
495/// # Examples
496///
497/// ```text
498/// 0.0~1.5, …
499/// ```
500///
501/// ```
502/// use icu::plurals::provider::rules::reference::ast::*;
503/// SampleList {
504///     sample_ranges: vec![SampleRange {
505///         lower_val: DecimalValue("0.0".to_string()),
506///         upper_val: Some(DecimalValue("1.5".to_string())),
507///     }],
508///     ellipsis: true,
509/// };
510/// ```
511#[derive(Debug, Clone, PartialEq)]
512#[allow(clippy::exhaustive_structs)] // this type is stable
513pub struct SampleList {
514    /// A collection of intervals in which all of the contained values (inclusive of the
515    /// interval boundaries) satisfy the associated rule.
516    pub sample_ranges: Vec<SampleRange>,
517    /// Indicates the presence of U+2026 HORIZONTAL ELLIPSIS at the end of sample string, which
518    /// represents whether an infinite set of values satisfies the rule or not.
519    pub ellipsis: bool,
520}
521
522/// A value range used in samples.
523///
524/// <div class="stab unstable">
525/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
526/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
527/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
528/// </div>
529///
530/// # Examples
531///
532/// ```text
533/// 0.0~1.5
534/// ```
535///
536/// ```
537/// use icu::plurals::provider::rules::reference::ast::*;
538/// SampleRange {
539///     lower_val: DecimalValue("0.0".to_string()),
540///     upper_val: Some(DecimalValue("1.5".to_string())),
541/// };
542/// ```
543#[derive(Debug, Clone, PartialEq)]
544#[allow(clippy::exhaustive_structs)] // this type is stable
545pub struct SampleRange {
546    /// When `upper_val` is `None`, this field represents a single sample value that satisfies
547    /// the associated plural rule. When `upper_val` is `Some`, this field represents the lower
548    /// bound of an interval (and is included in the interval) whose values all satisfy the rule.
549    pub lower_val: DecimalValue,
550    /// When this `SampleRange` represents an interval of values, this field represents the upper
551    /// bound of the interval (and is included in the interval). Otherwise, this field is `None`.
552    pub upper_val: Option<DecimalValue>,
553}
554
555/// A decimal value used in samples.
556///
557/// <div class="stab unstable">
558/// 🚧 This code is considered unstable; it may change at any time, in breaking or non-breaking ways,
559/// including in SemVer minor releases. In particular, the `DataProvider` implementations are only
560/// guaranteed to match with this version's `*_unstable` providers. Use with caution.
561/// </div>
562///
563/// # Examples
564///
565/// ```text
566/// 1.00
567/// ```
568///
569/// ```
570/// use icu::plurals::provider::rules::reference::ast::*;
571/// DecimalValue("1.00".to_string());
572/// ```
573#[derive(Debug, Clone, PartialEq)]
574#[allow(clippy::exhaustive_structs)] // this type is stable
575pub struct DecimalValue(pub String);