use alloc::vec::Vec;
use core::fmt;
use crate::allocation::{AllocationContext, AllocationError, try_push, try_reserve_total_exact};
use crate::bytes::{Payload, PayloadByteCount};
use crate::source::SourceLineNumber;
use crate::syntax::SyntaxToken;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RuleCount {
value: usize,
}
impl RuleCount {
#[must_use]
pub(crate) const fn new(value: usize) -> Self {
Self { value }
}
#[must_use]
pub const fn get(self) -> usize {
self.value
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RuleNumber {
one_based: usize,
}
impl RuleNumber {
fn from_zero_based(zero_based: usize) -> Option<Self> {
let one_based = zero_based.checked_add(1)?;
Some(Self { one_based })
}
#[must_use]
pub const fn get(self) -> usize {
self.one_based
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RulePosition {
number: RuleNumber,
}
impl RulePosition {
pub(crate) fn from_zero_based(zero_based: usize) -> Option<Self> {
let number = RuleNumber::from_zero_based(zero_based)?;
Some(Self { number })
}
#[must_use]
pub const fn number(self) -> RuleNumber {
self.number
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuleRepeat {
Always,
Once,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct OnceRuleSlot {
zero_based: usize,
}
impl OnceRuleSlot {
const fn new(zero_based: usize) -> Self {
Self { zero_based }
}
pub(crate) const fn get(self) -> usize {
self.zero_based
}
}
#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct OnceRuleSlotCount {
value: usize,
}
impl OnceRuleSlotCount {
pub(crate) const fn get(self) -> usize {
self.value
}
pub(crate) const fn as_rule_count(self) -> RuleCount {
RuleCount::new(self.value)
}
fn allocate_next(self) -> Result<(OnceRuleSlot, Self), AllocationError> {
let next = self.value.checked_add(1).ok_or_else(|| {
AllocationError::capacity_overflow(AllocationContext::ProgramRuleTable)
})?;
Ok((OnceRuleSlot::new(self.value), Self { value: next }))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum RuleSchedule {
Always,
Once(OnceRuleSlot),
}
impl RuleSchedule {
const fn repeat(self) -> RuleRepeat {
match self {
Self::Always => RuleRepeat::Always,
Self::Once(_) => RuleRepeat::Once,
}
}
fn from_repeat(
repeat: RuleRepeat,
once_slot_count: OnceRuleSlotCount,
) -> Result<(Self, OnceRuleSlotCount), AllocationError> {
match repeat {
RuleRepeat::Always => Ok((Self::Always, once_slot_count)),
RuleRepeat::Once => {
let (slot, next_count) = once_slot_count.allocate_next()?;
Ok((Self::Once(slot), next_count))
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuleAnchor {
Anywhere,
Start,
End,
}
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct PayloadView<'program> {
payload: &'program Payload,
}
impl<'program> PayloadView<'program> {
pub(crate) fn new(payload: &'program Payload) -> Self {
Self { payload }
}
#[must_use]
pub fn byte_count(self) -> PayloadByteCount {
self.payload.byte_count()
}
#[must_use]
pub fn is_empty(self) -> bool {
self.byte_count().is_zero()
}
pub fn bytes(self) -> impl Iterator<Item = u8> + 'program {
self.payload.bytes()
}
#[must_use]
pub fn eq_bytes(self, expected: &[u8]) -> bool {
self.payload.eq_bytes(expected)
}
pub fn to_vec(self) -> Result<Vec<u8>, AllocationError> {
self.to_vec_with_context(AllocationContext::PayloadView)
}
pub(crate) fn to_vec_with_context(
self,
context: AllocationContext,
) -> Result<Vec<u8>, AllocationError> {
self.payload.to_vec_with_context(context)
}
}
impl fmt::Debug for PayloadView<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list().entries((*self).bytes()).finish()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuleActionView<'program> {
Replace(PayloadView<'program>),
MoveStart(PayloadView<'program>),
MoveEnd(PayloadView<'program>),
Return(PayloadView<'program>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RuleView<'program> {
rule: &'program Rule,
}
impl<'program> RuleView<'program> {
pub(crate) fn new(rule: &'program Rule) -> Self {
Self { rule }
}
#[must_use]
pub const fn position(self) -> RulePosition {
self.rule.position()
}
#[must_use]
pub fn line_number(self) -> SourceLineNumber {
self.rule.line_number()
}
#[must_use]
pub fn repeat(self) -> RuleRepeat {
self.rule.repeat()
}
#[must_use]
pub fn anchor(self) -> RuleAnchor {
self.rule.anchor()
}
#[must_use]
pub fn lhs(self) -> PayloadView<'program> {
PayloadView::new(self.rule.lhs())
}
#[must_use]
pub fn action(self) -> RuleActionView<'program> {
self.rule.action().view()
}
pub fn canonical_source(self) -> Result<Vec<u8>, AllocationError> {
self.rule.canonical_source()
}
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) enum Action {
Replace(Payload),
MoveStart(Payload),
MoveEnd(Payload),
Return(Payload),
}
impl Action {
pub(crate) fn view(&self) -> RuleActionView<'_> {
match self {
Self::Replace(payload) => RuleActionView::Replace(PayloadView::new(payload)),
Self::MoveStart(payload) => RuleActionView::MoveStart(PayloadView::new(payload)),
Self::MoveEnd(payload) => RuleActionView::MoveEnd(PayloadView::new(payload)),
Self::Return(payload) => RuleActionView::Return(PayloadView::new(payload)),
}
}
fn canonical_parts(&self) -> (Option<SyntaxToken>, &Payload) {
match self {
Self::Replace(payload) => (None, payload),
Self::MoveStart(payload) => (Some(SyntaxToken::Start), payload),
Self::MoveEnd(payload) => (Some(SyntaxToken::End), payload),
Self::Return(payload) => (Some(SyntaxToken::Return), payload),
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct RuleHead {
repeat: RuleRepeat,
anchor: RuleAnchor,
lhs: Payload,
}
impl RuleHead {
pub(crate) fn new(repeat: RuleRepeat, anchor: RuleAnchor, lhs: Payload) -> Self {
Self {
repeat,
anchor,
lhs,
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct RuleBody {
action: Action,
}
impl RuleBody {
pub(crate) const fn new(action: Action) -> Self {
Self { action }
}
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct ParsedRule {
line_number: SourceLineNumber,
head: RuleHead,
body: RuleBody,
}
impl ParsedRule {
pub(crate) const fn from_parts(
line_number: SourceLineNumber,
head: RuleHead,
body: RuleBody,
) -> Self {
Self {
line_number,
head,
body,
}
}
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Rule {
position: RulePosition,
line_number: SourceLineNumber,
schedule: RuleSchedule,
anchor: RuleAnchor,
lhs: Payload,
action: Action,
}
impl Rule {
pub(crate) fn from_parsed(
parsed: ParsedRule,
position: RulePosition,
once_slot_count: OnceRuleSlotCount,
) -> Result<(Self, OnceRuleSlotCount), AllocationError> {
let (schedule, next_once_slot_count) =
RuleSchedule::from_repeat(parsed.head.repeat, once_slot_count)?;
Ok((
Self {
position,
line_number: parsed.line_number,
schedule,
anchor: parsed.head.anchor,
lhs: parsed.head.lhs,
action: parsed.body.action,
},
next_once_slot_count,
))
}
pub(crate) const fn position(&self) -> RulePosition {
self.position
}
pub(crate) const fn line_number(&self) -> SourceLineNumber {
self.line_number
}
pub(crate) const fn repeat(&self) -> RuleRepeat {
self.schedule.repeat()
}
pub(crate) const fn schedule(&self) -> RuleSchedule {
self.schedule
}
pub(crate) const fn anchor(&self) -> RuleAnchor {
self.anchor
}
pub(crate) const fn lhs(&self) -> &Payload {
&self.lhs
}
pub(crate) const fn action(&self) -> &Action {
&self.action
}
pub(crate) fn view(&self) -> RuleView<'_> {
RuleView::new(self)
}
fn canonical_source_len(&self) -> Result<usize, AllocationError> {
let (action_token, payload) = self.action.canonical_parts();
let mut len = self.lhs.len();
if matches!(self.schedule, RuleSchedule::Once(_)) {
len = len.checked_add(SyntaxToken::Once.len()).ok_or_else(|| {
AllocationError::capacity_overflow(AllocationContext::CanonicalSource)
})?;
}
let anchor_len = match self.anchor {
RuleAnchor::Anywhere => 0,
RuleAnchor::Start => SyntaxToken::Start.len(),
RuleAnchor::End => SyntaxToken::End.len(),
};
len = len
.checked_add(anchor_len)
.and_then(|len| len.checked_add(1))
.and_then(|len| len.checked_add(action_token.map_or(0, SyntaxToken::len)))
.and_then(|len| len.checked_add(payload.len()))
.ok_or_else(|| {
AllocationError::capacity_overflow(AllocationContext::CanonicalSource)
})?;
Ok(len)
}
fn canonical_source(&self) -> Result<Vec<u8>, AllocationError> {
let mut output = Vec::new();
try_reserve_total_exact(
&mut output,
self.canonical_source_len()?,
AllocationContext::CanonicalSource,
)?;
if matches!(self.schedule, RuleSchedule::Once(_)) {
push_token(&mut output, SyntaxToken::Once)?;
}
match self.anchor {
RuleAnchor::Anywhere => {}
RuleAnchor::Start => push_token(&mut output, SyntaxToken::Start)?,
RuleAnchor::End => push_token(&mut output, SyntaxToken::End)?,
}
push_payload(&mut output, &self.lhs)?;
try_push(&mut output, b'=', AllocationContext::CanonicalSource)?;
let (action_token, payload) = self.action.canonical_parts();
if let Some(token) = action_token {
push_token(&mut output, token)?;
}
push_payload(&mut output, payload)?;
Ok(output)
}
}
fn push_token(output: &mut Vec<u8>, token: SyntaxToken) -> Result<(), AllocationError> {
for byte in token.bytes().iter().copied() {
try_push(output, byte, AllocationContext::CanonicalSource)?;
}
Ok(())
}
fn push_payload(output: &mut Vec<u8>, payload: &Payload) -> Result<(), AllocationError> {
payload.push_bytes_to(output, AllocationContext::CanonicalSource)
}