grit_pattern_matcher/pattern/
contains.rsuse super::{
patterns::{Matcher, Pattern, PatternName},
resolved_pattern::{LazyBuiltIn, ResolvedPattern, ResolvedSnippet},
State,
};
use crate::{
binding::Binding, constants::PROGRAM_INDEX, context::QueryContext,
pattern::resolved_pattern::File,
};
use crate::{constants::GLOBAL_VARS_SCOPE_INDEX, context::ExecContext};
use core::fmt::Debug;
use grit_util::{error::GritResult, AnalysisLogs};
use grit_util::{AstCursor, AstNode};
#[derive(Debug, Clone)]
pub struct Contains<Q: QueryContext> {
pub contains: Pattern<Q>,
pub until: Option<Pattern<Q>>,
}
impl<Q: QueryContext> Contains<Q> {
pub fn new(contains: Pattern<Q>, until: Option<Pattern<Q>>) -> Self {
Self { contains, until }
}
pub fn new_pattern(contains: Pattern<Q>, until: Option<Pattern<Q>>) -> Pattern<Q> {
Pattern::Contains(Box::new(Contains::new(contains, until)))
}
}
impl<Q: QueryContext> PatternName for Contains<Q> {
fn name(&self) -> &'static str {
"CONTAINS"
}
}
fn execute_until<'a, Q: QueryContext>(
init_state: &mut State<'a, Q>,
node: &Q::Node<'a>,
context: &'a Q::ExecContext<'a>,
logs: &mut AnalysisLogs,
the_contained: &'a Pattern<Q>,
until: &'a Option<Pattern<Q>>,
) -> GritResult<bool> {
let mut did_match = false;
let mut cur_state = init_state.clone();
let mut cursor = node.walk();
let mut still_computing = true;
while still_computing {
let node = cursor.node();
let node_lhs = ResolvedPattern::from_node_binding(node);
let state = cur_state.clone();
if the_contained.execute(&node_lhs, &mut cur_state, context, logs)? {
did_match = true;
} else {
cur_state = state;
}
let mut state = cur_state.clone();
let skip_children = if let Some(until) = until {
until.execute(&node_lhs, &mut state, context, logs)?
} else {
false
};
if (!skip_children && cursor.goto_first_child()) || cursor.goto_next_sibling() {
continue;
}
loop {
if !cursor.goto_parent() {
still_computing = false;
break;
}
if cursor.goto_next_sibling() {
break;
}
}
}
if did_match {
*init_state = cur_state;
}
Ok(did_match)
}
impl<Q: QueryContext> Matcher<Q> for Contains<Q> {
fn execute<'a>(
&'a self,
resolved_pattern: &Q::ResolvedPattern<'a>,
init_state: &mut State<'a, Q>,
context: &'a Q::ExecContext<'a>,
logs: &mut AnalysisLogs,
) -> GritResult<bool> {
if let Some(binding) = resolved_pattern.get_last_binding() {
if let Some(node) = binding.as_node() {
execute_until(
init_state,
&node,
context,
logs,
&self.contains,
&self.until,
)
} else if let Some(items) = binding.list_items() {
let mut cur_state = init_state.clone();
let mut did_match = false;
for item in items {
let state = cur_state.clone();
if self.execute(
&ResolvedPattern::from_node_binding(item),
&mut cur_state,
context,
logs,
)? {
did_match = true;
} else {
cur_state = state;
}
}
if !did_match {
return Ok(false);
}
*init_state = cur_state;
Ok(true)
} else if let Some(_c) = binding.as_constant() {
self.contains
.execute(resolved_pattern, init_state, context, logs)
} else {
Ok(false)
}
} else if let Some(items) = resolved_pattern.get_list_items() {
let mut cur_state = init_state.clone();
let mut did_match = false;
for item in items {
let state = cur_state.clone();
if self.execute(item, &mut cur_state, context, logs)? {
did_match = true;
} else {
cur_state = state;
}
}
if !did_match {
return Ok(false);
}
*init_state = cur_state;
Ok(true)
} else if let Some(file) = resolved_pattern.get_file() {
if !context.load_file(file, init_state, logs)? {
return Ok(false);
}
init_state.bindings[GLOBAL_VARS_SCOPE_INDEX as usize]
.last_mut()
.unwrap()[PROGRAM_INDEX]
.value = Some(file.binding(&init_state.files));
let mut cur_state = init_state.clone();
let mut did_match = false;
let prev_state = cur_state.clone();
if self
.contains
.execute(resolved_pattern, &mut cur_state, context, logs)?
{
did_match = true;
} else {
cur_state = prev_state;
}
let prev_state = cur_state.clone();
if self
.contains
.execute(&file.name(&cur_state.files), &mut cur_state, context, logs)?
{
did_match = true;
} else {
cur_state = prev_state;
}
let prev_state = cur_state.clone();
if self.execute(
&file.binding(&cur_state.files),
&mut cur_state,
context,
logs,
)? {
did_match = true;
} else {
cur_state = prev_state;
}
if !did_match {
return Ok(false);
}
*init_state = cur_state;
Ok(true)
} else if let Some(files) = resolved_pattern.get_files() {
let mut cur_state = init_state.clone();
let mut did_match = false;
let prev_state = cur_state.clone();
if self
.contains
.execute(resolved_pattern, &mut cur_state, context, logs)?
{
did_match = true;
} else {
cur_state = prev_state;
}
let prev_state = cur_state.clone();
if self.execute(files, &mut cur_state, context, logs)? {
did_match = true;
} else {
cur_state = prev_state;
}
if !did_match {
return Ok(false);
}
*init_state = cur_state;
Ok(true)
} else if let Some(snippets) = resolved_pattern.get_snippets() {
let mut cur_state = init_state.clone();
let mut did_match = false;
for snippet in snippets {
let state = cur_state.clone();
let resolved = match snippet {
ResolvedSnippet::Text(_) => {
ResolvedPattern::from_resolved_snippet(snippet.to_owned())
}
ResolvedSnippet::Binding(b) => ResolvedPattern::from_binding(b.to_owned()),
ResolvedSnippet::LazyFn(l) => match &*l {
LazyBuiltIn::Join(j) => {
ResolvedPattern::from_list_parts(j.list.iter().cloned())
}
},
};
if self.execute(&resolved, &mut cur_state, context, logs)? {
did_match = true;
} else {
cur_state = state;
}
}
if !did_match {
return Ok(false);
}
*init_state = cur_state;
Ok(true)
} else {
return Ok(false);
}
}
}