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);