grit_pattern_matcher/pattern/
list.rs

1use super::{
2    list_index,
3    patterns::{Matcher, Pattern, PatternName},
4    resolved_pattern::ResolvedPattern,
5    state::State,
6};
7use crate::context::QueryContext;
8use core::fmt::Debug;
9use grit_util::{
10    error::{GritPatternError, GritResult},
11    AnalysisLogs,
12};
13use std::borrow::Cow;
14
15#[derive(Debug, Clone)]
16pub struct List<Q: QueryContext> {
17    pub patterns: Vec<Pattern<Q>>,
18}
19
20impl<Q: QueryContext> List<Q> {
21    pub fn new(patterns: Vec<Pattern<Q>>) -> Self {
22        Self { patterns }
23    }
24
25    pub fn get(&self, index: isize) -> Option<&Pattern<Q>> {
26        self.patterns
27            .get(list_index::to_unsigned(index, self.patterns.len())?)
28    }
29}
30
31impl<Q: QueryContext> PatternName for List<Q> {
32    fn name(&self) -> &'static str {
33        "LIST"
34    }
35}
36
37impl<Q: QueryContext> Matcher<Q> for List<Q> {
38    fn execute<'a>(
39        &'a self,
40        binding: &Q::ResolvedPattern<'a>,
41        state: &mut State<'a, Q>,
42        context: &'a Q::ExecContext<'a>,
43        logs: &mut AnalysisLogs,
44    ) -> GritResult<bool> {
45        if let Some(items) = binding.get_list_binding_items() {
46            let patterns: Vec<_> = items.map(Cow::Owned).collect();
47            execute_assoc(&self.patterns, &patterns, state, context, logs)
48        } else if let Some(items) = binding.get_list_items() {
49            let patterns: Vec<_> = items.map(Cow::Borrowed).collect();
50            execute_assoc(&self.patterns, &patterns, state, context, logs)
51        } else {
52            Ok(false)
53        }
54    }
55}
56
57fn execute_assoc<'a, Q: QueryContext>(
58    patterns: &'a [Pattern<Q>],
59    children: &[Cow<Q::ResolvedPattern<'a>>],
60    current_state: &mut State<'a, Q>,
61    context: &'a Q::ExecContext<'a>,
62    logs: &mut AnalysisLogs,
63) -> GritResult<bool> {
64    let mut working_state = current_state.clone();
65    match patterns {
66        // short circuit for common case
67        [pattern_for_first_node, Pattern::Dots] => {
68            if children.is_empty() {
69                return Ok(false);
70            }
71            let first_node = children[0].clone();
72            if pattern_for_first_node.execute(&first_node, &mut working_state, context, logs)? {
73                *current_state = working_state;
74                Ok(true)
75            } else {
76                Ok(false)
77            }
78        }
79        // short circuit for common case
80        [Pattern::Dots, pattern_for_last_node] => {
81            if let Some(last_node) = children.last() {
82                if pattern_for_last_node.execute(last_node, &mut working_state, context, logs)? {
83                    *current_state = working_state;
84                    Ok(true)
85                } else {
86                    Ok(false)
87                }
88            } else {
89                Ok(false)
90            }
91        }
92        [Pattern::Dots, head_pattern, tail_patterns @ ..] => {
93            if let Pattern::Dots = head_pattern {
94                return Err(GritPatternError::new(
95                    "Multiple subsequent dots are not allowed.",
96                ));
97            }
98            for index in 0..children.len() {
99                if head_pattern.execute(&children[index], &mut working_state, context, logs)?
100                    && execute_assoc(
101                        tail_patterns,
102                        &children[index + 1..],
103                        &mut working_state,
104                        context,
105                        logs,
106                    )?
107                {
108                    *current_state = working_state;
109                    return Ok(true);
110                }
111            }
112            Ok(false)
113        }
114        [Pattern::Dots] => Ok(true),
115        [only_pattern] => {
116            if children.len() == 1 {
117                if only_pattern.execute(&children[0], &mut working_state, context, logs)? {
118                    *current_state = working_state;
119                    Ok(true)
120                } else {
121                    Ok(false)
122                }
123            } else {
124                Ok(false)
125            }
126        }
127        [head_pattern, tail_patterns @ ..] => match children {
128            [head_node, tail_nodes @ ..] => {
129                if head_pattern.execute(head_node, &mut working_state, context, logs)? {
130                    if let Ok(true) =
131                        execute_assoc(tail_patterns, tail_nodes, &mut working_state, context, logs)
132                    {
133                        *current_state = working_state;
134                        Ok(true)
135                    } else {
136                        Ok(false)
137                    }
138                } else {
139                    Ok(false)
140                }
141            }
142            [] => Ok(false),
143        },
144        [] => Ok(children.is_empty()),
145    }
146}