use alloc::vec::Vec;
use core::slice;
use crate::allocation::{AllocationContext, RequestedCapacity, try_push, try_reserve_total_exact};
use crate::error::{
ParseError, ParseErrorKind, ParseLimitError, ParseRepresentationError, RuleAttemptCursorError,
};
use crate::inspect::{OnceRuleCount as PublicOnceRuleCount, RuleCount, RulePosition};
use crate::limits::RuleLimit;
use crate::rule::{OnceRuleSlot, ParsedRule, Rule, RuleAvailability, RuleRepeatSyntax};
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct RuleSet {
rules: Vec<Rule>,
once_rule_count: PublicOnceRuleCount,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct RuleScan<'program> {
rules: &'program [Rule],
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct RuleSetBuilder {
rules: Vec<Rule>,
once_rule_count: PublicOnceRuleCount,
}
struct PendingRuleInsertion {
rule: Rule,
}
struct RuleInsertionPermit {
attempted_rule_count: RuleCount,
position: RulePosition,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub(crate) struct RuleIndex {
zero_based: usize,
position: RulePosition,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum RuleCursor {
Active(ActiveRuleCursor),
Exhausted,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct ActiveRuleCursor {
next_rule_index: RuleIndex,
final_rule_index: RuleIndex,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum RuleCursorAfterMiss {
Advanced(ActiveRuleCursor),
Stable,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
enum RuleCursorSelection {
Active(ActiveRuleCursor),
NoExecutableRules,
}
pub(crate) enum RuleAttemptTargetSelection<'program> {
Target(RuleAttemptTarget<'program>),
NoExecutableRules,
}
pub(crate) struct RuleAttemptTarget<'program> {
active_cursor: ActiveRuleCursor,
target: RuleTarget<'program>,
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct RuleTarget<'program> {
rule: &'program Rule,
}
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,
availability: RuleAvailability,
) -> Self {
Self {
rule: Rule::from_parsed(position, parsed, availability),
}
}
const fn line_number(&self) -> crate::source::SourceLineNumber {
self.rule.line_number()
}
}
impl RuleSetBuilder {
pub(crate) fn new() -> Self {
Self {
rules: Vec::new(),
once_rule_count: PublicOnceRuleCount::ZERO,
}
}
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 (availability, next_once_rule_count) =
self.assign_rule_availability(&parsed, line_number)?;
let pending = PendingRuleInsertion::from_parsed(insertion.position(), parsed, availability);
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.once_rule_count = next_once_rule_count;
Ok(())
}
pub(crate) fn finish(self) -> RuleSet {
RuleSet {
rules: self.rules,
once_rule_count: self.once_rule_count,
}
}
fn assign_rule_availability(
&self,
parsed: &ParsedRule,
line_number: crate::source::SourceLineNumber,
) -> Result<(RuleAvailability, PublicOnceRuleCount), ParseError> {
match parsed.repeat_syntax() {
RuleRepeatSyntax::Always => Ok((RuleAvailability::Always, self.once_rule_count)),
RuleRepeatSyntax::Once => {
let slot = OnceRuleSlot::from_count(self.once_rule_count);
let next_once_rule_count =
self.once_rule_count.checked_next().ok_or_else(|| {
ParseError::at_line(
line_number,
ParseErrorKind::Representation(ParseRepresentationError::RuleCount),
)
})?;
Ok((RuleAvailability::Once(slot), next_once_rule_count))
}
}
}
}
impl RuleSet {
pub(crate) fn rule_count(&self) -> RuleCount {
RuleCount::new(self.rules.len())
}
pub(crate) fn once_rule_count(&self) -> PublicOnceRuleCount {
self.once_rule_count
}
pub(crate) fn as_slice(&self) -> &[Rule] {
&self.rules
}
pub(crate) fn scan(&self) -> RuleScan<'_> {
RuleScan { rules: &self.rules }
}
pub(crate) fn rule_attempt_cursor(&self) -> RuleCursor {
let Some(final_rule_index) = RuleIndex::last_for(self.rule_count()) else {
return RuleCursor::Exhausted;
};
RuleCursor::Active(ActiveRuleCursor {
next_rule_index: RuleIndex::first(),
final_rule_index,
})
}
fn target_for_cursor(
&self,
active_cursor: ActiveRuleCursor,
) -> Result<RuleTarget<'_>, RuleAttemptCursorError> {
let rule = self
.rules
.get(active_cursor.current_index().get())
.ok_or_else(|| {
RuleAttemptCursorError::missing_rule(active_cursor.current_position())
})?;
Ok(RuleTarget { rule })
}
pub(crate) fn select_attempt_target(
&self,
cursor: &mut RuleCursor,
) -> Result<RuleAttemptTargetSelection<'_>, RuleAttemptCursorError> {
let active_cursor = match cursor.select_next() {
RuleCursorSelection::Active(active_cursor) => active_cursor,
RuleCursorSelection::NoExecutableRules => {
return Ok(RuleAttemptTargetSelection::NoExecutableRules);
}
};
Ok(RuleAttemptTargetSelection::Target(RuleAttemptTarget {
active_cursor,
target: self.target_for_cursor(active_cursor)?,
}))
}
}
impl RuleCursor {
fn select_next(&mut self) -> RuleCursorSelection {
match core::mem::replace(self, Self::Exhausted) {
Self::Active(active) => RuleCursorSelection::Active(active),
Self::Exhausted => RuleCursorSelection::NoExecutableRules,
}
}
}
impl<'program> RuleScan<'program> {
pub(crate) fn iter(self) -> slice::Iter<'program, Rule> {
self.rules.iter()
}
}
impl ActiveRuleCursor {
pub(crate) const fn current_index(self) -> RuleIndex {
self.next_rule_index
}
pub(crate) const fn current_position(self) -> RulePosition {
self.next_rule_index.position()
}
pub(crate) fn advance_after_miss(self) -> RuleCursorAfterMiss {
if self.next_rule_index >= self.final_rule_index {
return RuleCursorAfterMiss::Stable;
}
if let Some(next_rule_index) = self.next_rule_index.checked_next() {
RuleCursorAfterMiss::Advanced(Self {
next_rule_index,
final_rule_index: self.final_rule_index,
})
} else {
RuleCursorAfterMiss::Stable
}
}
pub(crate) const fn reset_to_first(self) -> Self {
Self {
next_rule_index: RuleIndex::first(),
final_rule_index: self.final_rule_index,
}
}
}
impl RuleIndex {
const fn first() -> Self {
Self {
zero_based: 0,
position: RulePosition::FIRST,
}
}
fn from_zero_based(zero_based: usize) -> Option<Self> {
let position = RulePosition::from_zero_based(zero_based)?;
Some(Self {
zero_based,
position,
})
}
fn last_for(rule_count: RuleCount) -> Option<Self> {
let zero_based = rule_count.get().checked_sub(1)?;
Self::from_zero_based(zero_based)
}
fn checked_next(self) -> Option<Self> {
let zero_based = self.zero_based.checked_add(1)?;
Self::from_zero_based(zero_based)
}
pub(crate) const fn get(self) -> usize {
self.zero_based
}
const fn position(self) -> RulePosition {
self.position
}
}
impl<'program> RuleTarget<'program> {
pub(crate) const fn rule(self) -> &'program Rule {
self.rule
}
}
impl<'program> RuleAttemptTarget<'program> {
pub(crate) const fn into_parts(self) -> (ActiveRuleCursor, RuleTarget<'program>) {
(self.active_cursor, self.target)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::error::RuleAttemptStepError;
use crate::test_support::{TestFailure, TestResult, ensure_eq};
#[test]
fn missing_cursor_rule_is_rule_attempt_step_error() -> TestResult {
let rule_set = RuleSet {
rules: Vec::new(),
once_rule_count: PublicOnceRuleCount::ZERO,
};
let cursor = ActiveRuleCursor {
next_rule_index: RuleIndex::first(),
final_rule_index: RuleIndex::first(),
};
let Err(RuleAttemptStepError::RuleCursor(error)) = rule_set
.target_for_cursor(cursor)
.map_err(RuleAttemptStepError::from)
else {
return Err(TestFailure::message("expected missing cursor rule error"));
};
ensure_eq!(error.rule(), RulePosition::FIRST)
}
}