use std::ops::{Deref, DerefMut};
use hashbrown::HashSet;
use super::anyof::{AnyNumberOf, one_of};
use crate::dialects::syntax::SyntaxSet;
use crate::errors::SQLParseError;
use crate::helpers::ToMatchable;
use crate::parser::context::ParseContext;
use crate::parser::grammar::Ref;
use crate::parser::grammar::noncode::NonCodeMatcher;
use crate::parser::match_algorithms::{longest_match, skip_start_index_forward_to_code};
use crate::parser::match_result::MatchResult;
use crate::parser::matchable::{
Matchable, MatchableCacheKey, MatchableTrait, next_matchable_cache_key,
};
use crate::parser::segments::ErasedSegment;
#[derive(Clone, Debug)]
pub struct Delimited {
pub base: AnyNumberOf,
pub allow_trailing: bool,
pub(crate) delimiter: Matchable,
pub min_delimiters: usize,
pub optional_delimiter: bool,
optional: bool,
cache_key: MatchableCacheKey,
}
impl Delimited {
pub fn new(elements: Vec<Matchable>) -> Self {
Self {
base: one_of(elements),
allow_trailing: false,
delimiter: Ref::new("CommaSegment").to_matchable(),
min_delimiters: 0,
optional_delimiter: false,
optional: false,
cache_key: next_matchable_cache_key(),
}
}
pub fn allow_trailing(&mut self) {
self.allow_trailing = true;
}
pub fn optional_delimiter(&mut self) {
self.optional_delimiter = true;
}
pub fn delimiter(&mut self, delimiter: impl ToMatchable) {
self.delimiter = delimiter.to_matchable();
}
}
impl PartialEq for Delimited {
fn eq(&self, other: &Self) -> bool {
self.base == other.base
&& self.allow_trailing == other.allow_trailing
&& self.optional_delimiter == other.optional_delimiter
}
}
impl MatchableTrait for Delimited {
fn elements(&self) -> &[Matchable] {
&self.elements
}
fn is_optional(&self) -> bool {
self.optional || self.base.is_optional()
}
fn simple(
&self,
parse_context: &ParseContext,
crumbs: Option<Vec<&str>>,
) -> Option<(HashSet<String>, SyntaxSet)> {
super::anyof::simple(&self.elements, parse_context, crumbs)
}
fn match_segments(
&self,
segments: &[ErasedSegment],
idx: u32,
parse_context: &mut ParseContext,
) -> Result<MatchResult, SQLParseError> {
let mut delimiters = 0;
let mut seeking_delimiter = false;
let max_idx = segments.len() as u32;
let mut working_idx = idx;
let mut working_match = MatchResult::empty_at(idx);
let mut delimiter_match = None;
let delimiter_matcher = self.delimiter.clone();
let mut terminator_matchers = self.terminators.clone();
terminator_matchers.extend(
parse_context
.terminators
.iter()
.filter(|&t| &delimiter_matcher != t)
.cloned(),
);
let delimiter_matchers = std::slice::from_ref(&self.delimiter);
if !self.allow_gaps {
terminator_matchers.push(NonCodeMatcher.to_matchable());
}
loop {
if self.allow_gaps && working_idx > idx {
working_idx =
skip_start_index_forward_to_code(segments, working_idx, segments.len() as u32);
}
if working_idx >= max_idx {
break;
}
let (match_result, _) = parse_context.deeper_match(false, &[], |this| {
longest_match(segments, &terminator_matchers, working_idx, this)
})?;
if match_result.has_match() {
break;
}
let mut push_terminators: &[_] = &[];
if !seeking_delimiter {
push_terminators = delimiter_matchers;
}
let (match_result, _) =
parse_context.deeper_match(false, push_terminators, |this| {
longest_match(
segments,
if seeking_delimiter {
delimiter_matchers
} else {
&self.elements
},
working_idx,
this,
)
})?;
if !match_result.has_match() {
if seeking_delimiter && self.optional_delimiter {
seeking_delimiter = false;
continue;
}
break;
}
working_idx = match_result.span.end;
if seeking_delimiter {
delimiter_match = match_result.into();
} else {
if let Some(delimiter_match) = &delimiter_match {
delimiters += 1;
working_match = working_match.append(delimiter_match);
}
working_match = working_match.append(match_result);
}
seeking_delimiter = !seeking_delimiter;
}
if let Some(delimiter_match) =
delimiter_match.filter(|_delimiter_match| self.allow_trailing && !seeking_delimiter)
{
delimiters += 1;
working_match = working_match.append(delimiter_match);
}
if delimiters < self.min_delimiters {
return Ok(MatchResult::empty_at(idx));
}
Ok(working_match)
}
fn cache_key(&self) -> MatchableCacheKey {
self.cache_key
}
#[track_caller]
fn copy(
&self,
insert: Option<Vec<Matchable>>,
at: Option<usize>,
before: Option<Matchable>,
remove: Option<Vec<Matchable>>,
terminators: Vec<Matchable>,
replace_terminators: bool,
) -> Matchable {
let mut new_elements = self.elements.clone();
if let Some(insert_elements) = insert {
if let Some(before_element) = before {
if let Some(index) = self.elements.iter().position(|e| e == &before_element) {
new_elements.splice(index..index, insert_elements);
} else {
panic!("Element for insertion before not found");
}
} else if let Some(at_index) = at {
new_elements.splice(at_index..at_index, insert_elements);
} else {
new_elements.extend(insert_elements);
}
}
if let Some(remove_elements) = remove {
new_elements.retain(|elem| !remove_elements.contains(elem));
}
let mut new_grammar = self.clone();
new_grammar.base.elements = new_elements;
new_grammar.base.terminators = if replace_terminators {
terminators
} else {
[self.terminators.clone(), terminators].concat()
};
new_grammar.to_matchable()
}
}
impl Deref for Delimited {
type Target = AnyNumberOf;
fn deref(&self) -> &Self::Target {
&self.base
}
}
impl DerefMut for Delimited {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.base
}
}