use alloc::vec::Vec;
use crate::allocation::{AllocationContext, RequestedCapacity, try_push, try_reserve_total_exact};
use crate::error::{ParseError, ParseErrorKind, ParseLimitError, ParseRepresentationError};
use crate::inspect::{OnceRuleCount as PublicOnceRuleCount, RuleCount, RulePosition};
use crate::limits::RuleLimit;
use crate::rule::{OnceRuleCount, ParsedRule, Rule, RuleAvailability, RuleRepeatSyntax};
#[derive(Debug, PartialEq, Eq, Default)]
pub(crate) struct RuleSet {
rules: Vec<Rule>,
}
#[derive(Debug, PartialEq, Eq, Default)]
pub(crate) struct RuleSetBuilder {
rules: Vec<Rule>,
next_once_rule_count: OnceRuleCount,
}
struct PendingRuleInsertion {
rule: Rule,
next_once_rule_count: OnceRuleCount,
}
struct RuleInsertionPermit {
attempted_rule_count: RuleCount,
position: RulePosition,
}
impl RuleInsertionPermit {
fn new(
current_len: usize,
limit: RuleLimit,
line_number: crate::source::SourceLineNumber,
) -> Result<Self, ParseError> {
let attempted_rule_count = current_len.checked_add(1).ok_or_else(|| {
ParseError::at_line(
line_number,
ParseErrorKind::Representation(ParseRepresentationError::RuleCount),
)
})?;
let attempted_rule_count = RuleCount::new(attempted_rule_count);
if !limit.accepts(attempted_rule_count) {
return Err(ParseError::at_line(
line_number,
ParseErrorKind::Limit(ParseLimitError::rules(limit, attempted_rule_count)),
));
}
let position = RulePosition::from_zero_based(current_len).ok_or_else(|| {
ParseError::at_line(
line_number,
ParseErrorKind::Representation(ParseRepresentationError::RulePosition),
)
})?;
Ok(Self {
attempted_rule_count,
position,
})
}
const fn requested_capacity(&self) -> RequestedCapacity {
RequestedCapacity::from_rule_count(self.attempted_rule_count)
}
const fn position(&self) -> RulePosition {
self.position
}
}
impl PendingRuleInsertion {
fn from_parsed(
position: RulePosition,
parsed: ParsedRule,
next_once_rule_count: OnceRuleCount,
) -> Result<Self, ParseError> {
let line_number = parsed.line_number();
let (availability, next_once_rule_count) = match parsed.repeat_syntax() {
RuleRepeatSyntax::Once => {
let (slot, next_once_rule_count) =
next_once_rule_count.assign_next_slot().ok_or_else(|| {
ParseError::at_line(
line_number,
ParseErrorKind::Representation(ParseRepresentationError::OnceRuleCount),
)
})?;
(RuleAvailability::Once(slot), next_once_rule_count)
}
RuleRepeatSyntax::Always => (RuleAvailability::Always, next_once_rule_count),
};
Ok(Self {
rule: Rule::from_parsed(position, parsed, availability),
next_once_rule_count,
})
}
const fn line_number(&self) -> crate::source::SourceLineNumber {
self.rule.line_number()
}
}
impl RuleSetBuilder {
pub(crate) fn new() -> Self {
Self::default()
}
pub(crate) fn push_parsed_rule(
&mut self,
parsed: ParsedRule,
limit: RuleLimit,
) -> Result<(), ParseError> {
let line_number = parsed.line_number();
let insertion = RuleInsertionPermit::new(self.rules.len(), limit, line_number)?;
let pending = PendingRuleInsertion::from_parsed(
insertion.position(),
parsed,
self.next_once_rule_count,
)?;
let pending_line_number = pending.line_number();
try_reserve_total_exact(
&mut self.rules,
insertion.requested_capacity(),
AllocationContext::ProgramRuleTable,
)
.map_err(|error| {
ParseError::at_line(pending_line_number, ParseErrorKind::Allocation(error))
})?;
try_push(
&mut self.rules,
pending.rule,
AllocationContext::ProgramRuleTable,
)
.map_err(|error| {
ParseError::at_line(pending_line_number, ParseErrorKind::Allocation(error))
})?;
self.next_once_rule_count = pending.next_once_rule_count;
Ok(())
}
pub(crate) fn finish(self) -> RuleSet {
RuleSet { rules: self.rules }
}
}
impl RuleSet {
pub(crate) fn rule_count(&self) -> RuleCount {
RuleCount::new(self.rules.len())
}
pub(crate) fn once_rule_count(&self) -> PublicOnceRuleCount {
PublicOnceRuleCount::new(
self.rules
.iter()
.filter(|rule| rule.availability().is_once())
.count(),
)
}
pub(crate) fn as_slice(&self) -> &[Rule] {
&self.rules
}
}