hamelin_lib 0.10.8

Core library for Hamelin query language
Documentation
use std::{
    collections::HashSet,
    fmt::{self, Display, Formatter},
    ops::RangeInclusive,
    rc::Rc,
    sync::Arc,
};

use ordermap::OrderSet;

use antlr_rust::tree::ParseTree;
use derive_more::derive::From;

use crate::antlr::strip_numeric_separators;

use super::clause::FromClause;
use super::identifier::Identifier;
use super::node::{Span, Spannable};
use crate::{
    antlr::{
        hamelinparser::{
            ExactlyContextAttrs, NestedContext, NestedContextAttrs, PatternContextAll,
            QuantifiedContext, QuantifiedContextAttrs, QuantifierContextAll,
        },
        interval,
    },
    err::TranslationError,
    tree::ast::context::{FromCst, ParseContext},
};

// ============================================================================
// Pattern - Top-level pattern type for MATCH command
// ============================================================================

#[derive(Debug, Clone)]
pub struct Pattern {
    pub span: Span,
    pub kind: PatternKind,
}

impl PartialEq for Pattern {
    fn eq(&self, other: &Self) -> bool {
        self.kind == other.kind
    }
}

impl Spannable for Pattern {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        self.span.to_range()
    }
}

impl From<PatternKind> for Pattern {
    fn from(kind: PatternKind) -> Self {
        Self {
            span: Span::NONE,
            kind,
        }
    }
}

impl Pattern {
    /// Recursively extracts table references from this pattern, excluding CTEs and duplicates.
    pub fn extract_datasets(
        &self,
        cte_names: &HashSet<&Identifier>,
        datasets: &mut OrderSet<Identifier>,
    ) -> Result<(), Arc<TranslationError>> {
        match &self.kind {
            PatternKind::Quantified(qp) => {
                let table_ref = qp.from_clause.table_reference();
                let identifier = table_ref.identifier.valid_ref()?;
                if !cte_names.contains(identifier) {
                    datasets.insert(identifier.clone());
                }
            }
            PatternKind::Nested(np) => {
                for pattern in &np.patterns {
                    pattern.extract_datasets(cte_names, datasets)?;
                }
            }
            PatternKind::Error(_) => {}
        }
        Ok(())
    }
}

impl FromCst<Rc<PatternContextAll<'static>>> for Pattern {
    fn from_cst_with_context(cst: Rc<PatternContextAll<'static>>, ctx: &mut ParseContext) -> Self {
        let kind = PatternKind::from_cst_with_context(Rc::clone(&cst), ctx);
        Self {
            span: interval(cst.as_ref()).into(),
            kind,
        }
    }
}

#[derive(Debug, Clone, From, PartialEq)]
pub enum PatternKind {
    Quantified(QuantifiedPattern),
    Nested(NestedPattern),
    Error(Arc<TranslationError>),
}

impl Spannable for PatternKind {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        match self {
            PatternKind::Quantified(p) => p.span(),
            PatternKind::Nested(p) => p.span(),
            PatternKind::Error(_) => None,
        }
    }
}

impl FromCst<Rc<PatternContextAll<'static>>> for PatternKind {
    fn from_cst_with_context(cst: Rc<PatternContextAll<'static>>, ctx: &mut ParseContext) -> Self {
        match cst.as_ref() {
            PatternContextAll::QuantifiedContext(qctx) => {
                QuantifiedPattern::from_ast_with_context(qctx, ctx).into()
            }
            PatternContextAll::NestedContext(nctx) => {
                NestedPattern::from_ast_with_context(nctx, ctx).into()
            }
            PatternContextAll::Error(_) => ctx.error("parse error").at(cst.as_ref()).emit().into(),
        }
    }
}

// ============================================================================
// QuantifiedPattern - A from clause with optional quantifier
// ============================================================================

#[derive(Debug, Clone)]
pub struct QuantifiedPattern {
    pub span: Span,
    pub from_clause: Arc<FromClause>,
    pub quantifier: Option<Arc<Quantifier>>,
}

impl PartialEq for QuantifiedPattern {
    fn eq(&self, other: &Self) -> bool {
        self.from_clause == other.from_clause && self.quantifier == other.quantifier
    }
}

impl Spannable for QuantifiedPattern {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        self.span.to_range()
    }
}

impl QuantifiedPattern {
    fn from_ast_with_context(qctx: &QuantifiedContext<'static>, ctx: &mut ParseContext) -> Self {
        use super::clause::TableReference;

        let from_clause = Arc::new(
            qctx.fromClause()
                .map(|fc| FromClause::from_cst_with_context(fc, ctx))
                .unwrap_or_else(|| {
                    TableReference {
                        span: Span::NONE,
                        identifier: ctx
                            .error("missing from clause in quantified pattern")
                            .at(qctx)
                            .emit()
                            .into(),
                    }
                    .into()
                }),
        );

        let quantifier = qctx
            .quantifier()
            .map(|q| Arc::new(Quantifier::from_cst_with_context(q, ctx)));

        Self {
            span: interval(qctx).into(),
            from_clause,
            quantifier,
        }
    }
}

// ============================================================================
// NestedPattern - Nested patterns with quantifier (like ParenthesizedExpression)
// ============================================================================

#[derive(Debug, Clone)]
pub struct NestedPattern {
    pub span: Span,
    pub patterns: Vec<Arc<Pattern>>,
    pub quantifier: Arc<Quantifier>,
}

impl PartialEq for NestedPattern {
    fn eq(&self, other: &Self) -> bool {
        self.patterns == other.patterns && self.quantifier == other.quantifier
    }
}

impl Spannable for NestedPattern {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        self.span.to_range()
    }
}

impl NestedPattern {
    fn from_ast_with_context(nctx: &NestedContext<'static>, ctx: &mut ParseContext) -> Self {
        let patterns = nctx
            .pattern_all()
            .into_iter()
            .map(|p| Arc::new(Pattern::from_cst_with_context(p, ctx)))
            .collect();

        let quantifier = Arc::new(
            nctx.quantifier()
                .map(|q| Quantifier::from_cst_with_context(q, ctx))
                .unwrap_or_else(|| {
                    ctx.error("missing quantifier in nested pattern")
                        .at(nctx)
                        .emit()
                        .into()
                }),
        );

        Self {
            span: interval(nctx).into(),
            patterns,
            quantifier,
        }
    }
}

// ============================================================================
// Quantifier - Pattern quantifiers (+, *, ?, {n})
// ============================================================================

#[derive(Debug, Clone)]
pub struct Quantifier {
    pub span: Span,
    pub kind: QuantifierKind,
}

impl PartialEq for Quantifier {
    fn eq(&self, other: &Self) -> bool {
        self.kind == other.kind
    }
}

impl Spannable for Quantifier {
    fn span(&self) -> Option<RangeInclusive<usize>> {
        self.span.to_range()
    }
}

impl From<QuantifierKind> for Quantifier {
    fn from(kind: QuantifierKind) -> Self {
        Self {
            span: Span::NONE,
            kind,
        }
    }
}

impl From<Arc<TranslationError>> for Quantifier {
    fn from(error: Arc<TranslationError>) -> Self {
        Self {
            span: Span::NONE,
            kind: QuantifierKind::Error(error),
        }
    }
}

impl FromCst<Rc<QuantifierContextAll<'static>>> for Quantifier {
    fn from_cst_with_context(
        cst: Rc<QuantifierContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Self {
        let kind = QuantifierKind::from_cst_with_context(Rc::clone(&cst), ctx);
        Self {
            span: interval(cst.as_ref()).into(),
            kind,
        }
    }
}

#[derive(Debug, Clone, From, PartialEq)]
pub enum QuantifierKind {
    AtLeastOne,      // +
    AnyNumber,       // *
    ZeroOrOne,       // ?
    Exactly(String), // {n}
    Error(Arc<TranslationError>),
}

impl FromCst<Rc<QuantifierContextAll<'static>>> for QuantifierKind {
    fn from_cst_with_context(
        cst: Rc<QuantifierContextAll<'static>>,
        ctx: &mut ParseContext,
    ) -> Self {
        match cst.as_ref() {
            QuantifierContextAll::AtLeastOneContext(_) => QuantifierKind::AtLeastOne,
            QuantifierContextAll::AnyNumberContext(_) => QuantifierKind::AnyNumber,
            QuantifierContextAll::ZeroOrOneContext(_) => QuantifierKind::ZeroOrOne,
            QuantifierContextAll::ExactlyContext(ectx) => match ectx.INTEGER_VALUE() {
                Some(v) => {
                    QuantifierKind::Exactly(strip_numeric_separators(&v.get_text()).into_owned())
                }
                None => ctx
                    .error("missing integer value in exact quantifier")
                    .at(ectx)
                    .emit()
                    .into(),
            },
            QuantifierContextAll::Error(_) => {
                ctx.error("parse error").at(cst.as_ref()).emit().into()
            }
        }
    }
}

// ============================================================================
// Display / fmt_indented implementations
// ============================================================================

impl Pattern {
    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        self.kind.fmt_indented(f, indentation)
    }
}

impl Display for Pattern {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        self.fmt_indented(f, 0)
    }
}

impl PatternKind {
    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        match self {
            PatternKind::Quantified(q) => q.fmt_indented(f, indentation),
            PatternKind::Nested(n) => n.fmt_indented(f, indentation),
            PatternKind::Error(_) => write!(f, "?!"),
        }
    }
}

impl QuantifiedPattern {
    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        self.from_clause.fmt_indented(f, indentation)?;
        if let Some(q) = &self.quantifier {
            write!(f, "{}", q)?;
        }
        Ok(())
    }
}

impl NestedPattern {
    pub fn fmt_indented(&self, f: &mut Formatter<'_>, indentation: usize) -> fmt::Result {
        write!(f, "(")?;
        for (i, pat) in self.patterns.iter().enumerate() {
            if i > 0 {
                write!(f, ", ")?;
            }
            pat.fmt_indented(f, indentation + 1)?;
        }
        write!(f, ")")?;
        write!(f, "{}", self.quantifier)
    }
}

impl Display for Quantifier {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        write!(f, "{}", self.kind)
    }
}

impl Display for QuantifierKind {
    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
        match self {
            QuantifierKind::AtLeastOne => write!(f, "+"),
            QuantifierKind::AnyNumber => write!(f, "*"),
            QuantifierKind::ZeroOrOne => write!(f, "?"),
            QuantifierKind::Exactly(n) => write!(f, "{{{}}}", n),
            QuantifierKind::Error(_) => write!(f, "?!"),
        }
    }
}