rulex 0.4.4

DEPRECATED: Use pomsky instead. A new regular expression language
Documentation
use crate::{
    compile::{CompileResult, CompileState},
    error::{CompileErrorKind, Feature, ParseError},
    features::RulexFeatures,
    options::{CompileOptions, ParseOptions, RegexFlavor},
    regex::Regex,
    span::Span,
};

#[derive(Clone, Copy, PartialEq, Eq)]
pub(crate) struct Reference<'i> {
    pub(crate) target: ReferenceTarget<'i>,
    pub(crate) span: Span,
}

#[derive(Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub(crate) enum ReferenceTarget<'i> {
    Named(&'i str),
    Number(u32),
    Relative(i32),
}

#[derive(Clone, Copy, PartialEq, Eq)]
enum ReferenceDirection {
    Backwards,
    Forwards,
}

impl<'i> Reference<'i> {
    pub(crate) fn new(target: ReferenceTarget<'i>, span: Span) -> Self {
        Reference { target, span }
    }

    pub(crate) fn compile(
        &self,
        options: CompileOptions,
        state: &mut CompileState,
    ) -> CompileResult<'i> {
        let (direction, number) = match self.target {
            ReferenceTarget::Named(name) => match state.used_names.get(name) {
                Some(&n) => {
                    let direction = if n >= state.next_idx {
                        ReferenceDirection::Forwards
                    } else {
                        ReferenceDirection::Backwards
                    };
                    (direction, n)
                }
                None => {
                    return Err(
                        CompileErrorKind::UnknownReferenceName(name.to_string()).at(self.span)
                    );
                }
            },
            ReferenceTarget::Number(idx) => {
                let direction = if idx > 99 {
                    return Err(CompileErrorKind::HugeReference.at(self.span));
                } else if idx > state.groups_count {
                    return Err(CompileErrorKind::UnknownReferenceNumber(idx as i32).at(self.span));
                } else if idx >= state.next_idx {
                    ReferenceDirection::Forwards
                } else {
                    ReferenceDirection::Backwards
                };
                (direction, idx)
            }
            ReferenceTarget::Relative(offset) => {
                let direction = if offset >= 0 {
                    ReferenceDirection::Forwards
                } else {
                    ReferenceDirection::Backwards
                };

                let num = match offset {
                    0 => {
                        return Err(
                            CompileErrorKind::Other("Relative references can't be 0").at(self.span)
                        )
                    }
                    i32::MIN..=-1 => offset + (state.next_idx as i32),
                    1..=i32::MAX => offset + (state.next_idx as i32) - 1,
                };
                if num <= 0 || (num as u32) > state.groups_count {
                    return Err(CompileErrorKind::UnknownReferenceNumber(num).at(self.span));
                }

                (direction, num as u32)
            }
        };

        match options.flavor {
            RegexFlavor::Rust => Err(CompileErrorKind::Unsupported(
                if direction == ReferenceDirection::Backwards {
                    Feature::Backreference
                } else {
                    Feature::ForwardReference
                },
                options.flavor,
            )
            .at(self.span)),
            RegexFlavor::JavaScript if direction == ReferenceDirection::Forwards => {
                Err(CompileErrorKind::Unsupported(Feature::ForwardReference, options.flavor)
                    .at(self.span))
            }
            _ => Ok(Regex::Reference(RegexReference { number })),
        }
    }

    pub(crate) fn validate(&self, options: &ParseOptions) -> Result<(), ParseError> {
        options.allowed_features.require(RulexFeatures::REFERENCES, self.span)
    }
}

#[cfg(feature = "dbg")]
impl std::fmt::Debug for Reference<'_> {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self.target {
            ReferenceTarget::Named(n) => write!(f, "::{}", n),
            ReferenceTarget::Number(i) => write!(f, "::{}", i),
            ReferenceTarget::Relative(o) => write!(f, "::{}{}", if o < 0 { '-' } else { '+' }, o),
        }
    }
}

#[cfg_attr(feature = "dbg", derive(Debug))]
pub(crate) struct RegexReference {
    number: u32,
}

impl RegexReference {
    pub(crate) fn codegen(&self, buf: &mut String, _: RegexFlavor) {
        use std::fmt::Write;

        debug_assert!(self.number <= 99);

        write!(buf, "\\{}", self.number).unwrap();
    }
}