use alloc::vec::Vec;
use core::fmt;
use crate::allocation::{AllocationContext, AllocationError, try_push, try_reserve_total_exact};
use crate::bytes::{ByteCount, Payload};
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 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 {
const fn from_zero_based(zero_based: usize) -> Self {
Self {
one_based: zero_based + 1,
}
}
#[must_use]
pub const fn get(self) -> usize {
self.one_based
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RulePosition {
zero_based: usize,
}
impl RulePosition {
pub(crate) const fn new(zero_based: usize) -> Self {
Self { zero_based }
}
#[must_use]
pub const fn number(self) -> RuleNumber {
RuleNumber::from_zero_based(self.zero_based)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RuleRepeat {
Always,
Once,
}
impl RuleRepeat {
#[must_use]
pub const fn is_once(self) -> bool {
matches!(self, Self::Once)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct OnceRuleSlot {
zero_based: usize,
}
impl OnceRuleSlot {
pub(crate) const fn new(zero_based: usize) -> Self {
Self { zero_based }
}
pub(crate) const fn zero_based(self) -> usize {
self.zero_based
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum RuleRepeatPlan {
Always,
Once { slot: OnceRuleSlot },
}
impl RuleRepeatPlan {
pub(crate) const fn always() -> Self {
Self::Always
}
pub(crate) const fn once(slot: OnceRuleSlot) -> Self {
Self::Once { slot }
}
pub(crate) const fn public_repeat(self) -> RuleRepeat {
match self {
Self::Always => RuleRepeat::Always,
Self::Once { .. } => RuleRepeat::Once,
}
}
pub(crate) const fn once_slot(self) -> Option<OnceRuleSlot> {
match self {
Self::Always => None,
Self::Once { slot } => Some(slot),
}
}
pub(crate) const fn is_once(self) -> bool {
matches!(self, Self::Once { .. })
}
}
#[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) -> ByteCount {
ByteCount::new(self.payload.len())
}
#[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::Payload)
}
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>),
}
impl<'program> RuleActionView<'program> {
#[must_use]
pub const fn payload(self) -> PayloadView<'program> {
match self {
Self::Replace(payload)
| Self::MoveStart(payload)
| Self::MoveEnd(payload)
| Self::Return(payload) => payload,
}
}
#[must_use]
pub const fn is_return(self) -> bool {
matches!(self, Self::Return(_))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RuleView<'program> {
position: RulePosition,
rule: &'program Rule,
}
impl<'program> RuleView<'program> {
pub(crate) fn new(position: RulePosition, rule: &'program Rule) -> Self {
Self { position, rule }
}
#[must_use]
pub const fn position(self) -> RulePosition {
self.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 ParsedRule {
line_number: SourceLineNumber,
repeat: RuleRepeat,
anchor: RuleAnchor,
lhs: Payload,
action: Action,
}
impl ParsedRule {
pub(crate) fn new(
line_number: SourceLineNumber,
repeat: RuleRepeat,
anchor: RuleAnchor,
lhs: Payload,
action: Action,
) -> Self {
Self {
line_number,
repeat,
anchor,
lhs,
action,
}
}
pub(crate) const fn repeat(&self) -> RuleRepeat {
self.repeat
}
}
#[derive(Debug, PartialEq, Eq)]
pub(crate) struct Rule {
line_number: SourceLineNumber,
repeat: RuleRepeatPlan,
anchor: RuleAnchor,
lhs: Payload,
action: Action,
}
impl Rule {
pub(crate) fn from_parsed(parsed: ParsedRule, repeat: RuleRepeatPlan) -> Self {
Self {
line_number: parsed.line_number,
repeat,
anchor: parsed.anchor,
lhs: parsed.lhs,
action: parsed.action,
}
}
pub(crate) const fn line_number(&self) -> SourceLineNumber {
self.line_number
}
pub(crate) const fn repeat(&self) -> RuleRepeat {
self.repeat.public_repeat()
}
pub(crate) const fn once_slot(&self) -> Option<OnceRuleSlot> {
self.repeat.once_slot()
}
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, position: RulePosition) -> RuleView<'_> {
RuleView::new(position, self)
}
fn canonical_source_len(&self) -> Result<usize, AllocationError> {
let (action_token, payload) = self.action.canonical_parts();
let mut len = self.lhs.len();
if self.repeat.is_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 self.repeat.is_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)
}