ielr 0.1.1

Table generation backend for LR parser generators
Documentation
/*
 * Copyright 2022 Arnaud Golfouse
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

use crate::input::PrecedenceLevel;

/// Precedence annotations in a [`super::LRItem`].
///
/// Each item that wish to appear to the left or right of the item containing the
/// annotations must meet the threshold for each concerned field.
#[derive(Clone, Debug, Default, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub(crate) struct PrecedenceAnnotations {
    /// Inherited precedence level on the left of the current item.
    ///
    /// This will forbid any item on the *left* of this one, whose left precedence
    /// levels do not meet the threshold for this field.
    pub(crate) forbidden_left: Vec<Option<PrecedenceLevel>>,
    /// Precedence level on the right of the current item.
    ///
    /// This will forbid any item on the *right* of this one, whose right precedence
    /// levels do not meet the threshold for this field.
    pub(crate) forbidden_right: Vec<Option<PrecedenceLevel>>,
}

impl PrecedenceAnnotations {
    pub(super) const fn new() -> Self {
        Self {
            forbidden_left: Vec::new(),
            forbidden_right: Vec::new(),
        }
    }

    /// Returns `true` if it does not contain any annotations.
    pub(crate) fn is_empty(&self) -> bool {
        self.forbidden_left.is_empty() && self.forbidden_right.is_empty()
    }

    /// One should be left, the other right.
    ///
    /// They are compatible if for every non-`None` `level` in `current_precendences`,
    /// the `next_level` at the same index in `next_precendences` is `None`, or equal or
    /// greater than `level`.
    pub(crate) fn compatible_with(
        current_precendences: &[Option<PrecedenceLevel>],
        next_precendences: &[Option<PrecedenceLevel>],
    ) -> bool {
        for (&level, &next_level) in current_precendences.iter().zip(next_precendences) {
            match (level, next_level) {
                (Some(level), Some(next_level)) if level > next_level => return false,
                _ => {}
            }
        }
        true
    }

    /// Merge `annotations` into `self_annotations`, increasing the precedence levels.
    ///
    /// Returns `true` if `self_annotations` was changed.
    pub(super) fn merge_increasing(
        self_annotations: &mut Vec<Option<PrecedenceLevel>>,
        annotations: &[Option<PrecedenceLevel>],
    ) -> bool {
        if self_annotations.len() < annotations.len() {
            self_annotations.resize(annotations.len(), None);
        }
        let mut modified = false;
        for (self_level, level) in self_annotations.iter_mut().zip(annotations) {
            let level = match *level {
                Some(l) => l,
                None => continue,
            };
            match self_level {
                Some(l) if *l < level => {
                    modified = true;
                    *l = level
                }
                None => {
                    modified = true;
                    *self_level = Some(level)
                }
                _ => {}
            }
        }
        modified
    }
}