use std::collections::{HashMap, hash_map::Entry};
use super::{Annotation, Ast, Number};
use crate::{
error::ParseError, identifier::LocIdent, impl_display_from_bytecode_pretty, position::TermPos,
traverse::*,
};
pub mod bindings;
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum PatternData<'ast> {
Wildcard,
Any(LocIdent),
Record(&'ast RecordPattern<'ast>),
Array(&'ast ArrayPattern<'ast>),
Enum(&'ast EnumPattern<'ast>),
Constant(&'ast ConstantPattern<'ast>),
Or(&'ast OrPattern<'ast>),
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Pattern<'ast> {
pub data: PatternData<'ast>,
pub alias: Option<LocIdent>,
pub pos: TermPos,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct EnumPattern<'ast> {
pub tag: LocIdent,
pub pattern: Option<Pattern<'ast>>,
pub pos: TermPos,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct FieldPattern<'ast> {
pub matched_id: LocIdent,
pub annotation: Annotation<'ast>,
pub default: Option<Ast<'ast>>,
pub pattern: Pattern<'ast>,
pub pos: TermPos,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct RecordPattern<'ast> {
pub patterns: &'ast [FieldPattern<'ast>],
pub tail: TailPattern,
pub pos: TermPos,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ArrayPattern<'ast> {
pub patterns: &'ast [Pattern<'ast>],
pub tail: TailPattern,
pub pos: TermPos,
}
impl ArrayPattern<'_> {
pub fn is_open(&self) -> bool {
self.tail.is_open()
}
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct ConstantPattern<'ast> {
pub data: ConstantPatternData<'ast>,
pub pos: TermPos,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum ConstantPatternData<'ast> {
Bool(bool),
Number(&'ast Number),
String(&'ast str),
Null,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct OrPattern<'ast> {
pub patterns: &'ast [Pattern<'ast>],
pub pos: TermPos,
}
#[derive(Debug, PartialEq, Eq, Clone)]
pub enum TailPattern {
Empty,
Open,
Capture(LocIdent),
}
impl Pattern<'_> {
pub fn any(id: LocIdent) -> Self {
let pos = id.pos;
Pattern {
data: PatternData::Any(id),
alias: None,
pos,
}
}
pub fn try_as_any(&self) -> Option<LocIdent> {
if let PatternData::Any(id) = &self.data {
Some(*id)
} else {
None
}
}
}
impl TailPattern {
pub fn is_open(&self) -> bool {
matches!(self, TailPattern::Open | TailPattern::Capture(_))
}
}
impl RecordPattern<'_> {
pub fn check_dup(&self) -> Result<(), ParseError> {
let mut bindings = HashMap::new();
for pat in self.patterns.iter() {
let binding = pat.matched_id;
let label = binding.label().to_owned();
match bindings.entry(label) {
Entry::Occupied(occupied_entry) => {
return Err(ParseError::DuplicateIdentInRecordPattern {
ident: binding,
prev_ident: occupied_entry.remove_entry().1,
});
}
Entry::Vacant(vacant_entry) => {
vacant_entry.insert(binding);
}
}
}
Ok(())
}
pub fn is_open(&self) -> bool {
self.tail.is_open()
}
}
impl<'ast> TraverseAlloc<'ast, Ast<'ast>> for Pattern<'ast> {
fn traverse<F, E>(
self,
alloc: &'ast super::AstAlloc,
f: &mut F,
order: TraverseOrder,
) -> Result<Self, E>
where
F: FnMut(Ast<'ast>) -> Result<Ast<'ast>, E>,
{
match self.data {
data @ (PatternData::Wildcard | PatternData::Any(_) | PatternData::Constant(_)) => {
Ok(Pattern { data, ..self })
}
PatternData::Record(record) => {
let record = record.clone();
let patterns =
traverse_alloc_many(alloc, record.patterns.iter().cloned(), f, order)?;
Ok(Pattern {
data: PatternData::Record(alloc.alloc(RecordPattern { patterns, ..record })),
..self
})
}
PatternData::Array(array) => {
let array = array.clone();
let patterns =
traverse_alloc_many(alloc, array.patterns.iter().cloned(), f, order)?;
Ok(Pattern {
data: PatternData::Array(alloc.alloc(ArrayPattern { patterns, ..array })),
..self
})
}
PatternData::Enum(enum_pat) => {
let enum_pat = enum_pat.clone();
let pattern = enum_pat
.pattern
.map(|p| p.traverse(alloc, f, order))
.transpose()?;
Ok(Pattern {
data: PatternData::Enum(alloc.alloc(EnumPattern {
pattern,
..enum_pat
})),
..self
})
}
PatternData::Or(or) => {
let or = or.clone();
let patterns = traverse_alloc_many(alloc, or.patterns.iter().cloned(), f, order)?;
Ok(Pattern {
data: PatternData::Or(alloc.alloc(OrPattern { patterns, ..or })),
..self
})
}
}
}
fn traverse_ref<S, U>(
&'ast self,
f: &mut dyn FnMut(&'ast Ast<'ast>, &S) -> TraverseControl<S, U>,
scope: &S,
) -> Option<U> {
match &self.data {
PatternData::Wildcard | PatternData::Any(_) | PatternData::Constant(_) => None,
PatternData::Record(record) => record
.patterns
.iter()
.find_map(|field_pat| field_pat.traverse_ref(f, scope)),
PatternData::Array(array) => array
.patterns
.iter()
.find_map(|pat| pat.traverse_ref(f, scope)),
PatternData::Enum(enum_pat) => enum_pat
.pattern
.as_ref()
.and_then(|pat| pat.traverse_ref(f, scope)),
PatternData::Or(or) => or
.patterns
.iter()
.find_map(|pat| pat.traverse_ref(f, scope)),
}
}
}
impl<'ast> TraverseAlloc<'ast, Ast<'ast>> for FieldPattern<'ast> {
fn traverse<F, E>(
self,
alloc: &'ast super::AstAlloc,
f: &mut F,
order: TraverseOrder,
) -> Result<Self, E>
where
F: FnMut(Ast<'ast>) -> Result<Ast<'ast>, E>,
{
let annotation = self.annotation.traverse(alloc, f, order)?;
let default = self
.default
.map(|d| d.traverse(alloc, f, order))
.transpose()?;
let pattern = self.pattern.traverse(alloc, f, order)?;
Ok(FieldPattern {
annotation,
default,
pattern,
..self
})
}
fn traverse_ref<S, U>(
&'ast self,
f: &mut dyn FnMut(&'ast Ast<'ast>, &S) -> TraverseControl<S, U>,
scope: &S,
) -> Option<U> {
self.annotation
.traverse_ref(f, scope)
.or_else(|| self.default.as_ref().and_then(|d| d.traverse_ref(f, scope)))
.or_else(|| self.pattern.traverse_ref(f, scope))
}
}
impl_display_from_bytecode_pretty!(PatternData<'_>);
impl_display_from_bytecode_pretty!(Pattern<'_>);
impl_display_from_bytecode_pretty!(ConstantPatternData<'_>);
impl_display_from_bytecode_pretty!(ConstantPattern<'_>);
impl_display_from_bytecode_pretty!(RecordPattern<'_>);
impl_display_from_bytecode_pretty!(EnumPattern<'_>);
impl_display_from_bytecode_pretty!(ArrayPattern<'_>);