grit_pattern_matcher/pattern/
list_index.rs1use super::{
2 accessor::execute_resolved_with_binding,
3 container::{Container, PatternOrResolved, PatternOrResolvedMut},
4 list::List,
5 patterns::{Matcher, Pattern, PatternName},
6 resolved_pattern::ResolvedPattern,
7 state::State,
8};
9use crate::{
10 binding::Binding,
11 context::{ExecContext, QueryContext},
12};
13use grit_util::{
14 error::{GritPatternError, GritResult},
15 AnalysisLogs,
16};
17
18#[derive(Debug, Clone)]
19pub enum ListOrContainer<Q: QueryContext> {
20 Container(Container<Q>),
21 List(List<Q>),
22}
23
24#[derive(Debug, Clone)]
25pub enum ContainerOrIndex<Q: QueryContext> {
26 Container(Container<Q>),
27 Index(isize),
28}
29
30#[derive(Debug, Clone)]
31pub struct ListIndex<Q: QueryContext> {
32 pub list: ListOrContainer<Q>,
33 pub index: ContainerOrIndex<Q>,
34}
35
36impl<Q: QueryContext> ListIndex<Q> {
37 fn get_index<'a>(&'a self, state: &State<'a, Q>, lang: &Q::Language<'a>) -> GritResult<isize> {
38 match &self.index {
39 ContainerOrIndex::Container(c) => {
40 let raw_index = c.get_pattern_or_resolved(state, lang)?.ok_or_else(|| {
41 GritPatternError::new(format!("list index must be resolvable: {:?}", self))
42 })?;
43 let index = match raw_index {
44 PatternOrResolved::Resolved(r) => r.text(&state.files, lang)?,
45 PatternOrResolved::ResolvedBinding(r) => r.text(&state.files, lang)?,
46 PatternOrResolved::Pattern(_) => {
47 return Err(GritPatternError::new("list index must be resolved"))
48 }
49 };
50 let int_index = index.parse::<isize>().map_err(|_| {
51 GritPatternError::new(format!(
52 "list index must be an integer but got {:?}",
53 index
54 ))
55 })?;
56 Ok(int_index)
57 }
58 ContainerOrIndex::Index(i) => Ok(*i),
59 }
60 }
61
62 pub fn get<'a, 'b>(
63 &'a self,
64 state: &'b State<'a, Q>,
65 lang: &Q::Language<'a>,
66 ) -> GritResult<Option<PatternOrResolved<'a, 'b, Q>>> {
67 let index = self.get_index(state, lang)?;
68 match &self.list {
69 ListOrContainer::Container(c) => match c.get_pattern_or_resolved(state, lang)? {
70 None => Ok(None),
71 Some(PatternOrResolved::Pattern(Pattern::List(l))) => {
72 Ok(l.get(index).map(PatternOrResolved::Pattern))
73 }
74 Some(PatternOrResolved::Resolved(resolved)) => {
75 if resolved.is_list() {
76 Ok(resolved
77 .get_list_item_at(index)
78 .map(PatternOrResolved::Resolved))
79 } else if let Some(mut items) =
80 resolved.get_last_binding().and_then(Binding::list_items)
81 {
82 let len = items.clone().count();
83 return Ok(to_unsigned(index, len)
84 .and_then(|index| items.nth(index))
85 .map(|n| {
86 PatternOrResolved::ResolvedBinding(
87 ResolvedPattern::from_node_binding(n),
88 )
89 }));
90 } else {
91 return Err(GritPatternError::new(
92 "left side of a listIndex must be a list",
93 ));
94 }
95 }
96 Some(s) => Err(GritPatternError::new(format!(
97 "left side of a listIndex must be a list but got {:?}",
98 s
99 ))),
100 },
101 ListOrContainer::List(l) => Ok(l.get(index).map(PatternOrResolved::Pattern)),
102 }
103 }
104
105 pub fn get_mut<'a, 'b>(
106 &'a self,
107 state: &'b mut State<'a, Q>,
108 lang: &Q::Language<'a>,
109 ) -> GritResult<Option<PatternOrResolvedMut<'a, 'b, Q>>> {
110 let index = self.get_index(state, lang)?;
111 match &self.list {
112 ListOrContainer::Container(c) => match c.get_pattern_or_resolved_mut(state, lang)? {
113 None => Ok(None),
114 Some(PatternOrResolvedMut::Pattern(Pattern::List(l))) => {
115 Ok(l.get(index).map(PatternOrResolvedMut::Pattern))
116 }
117 Some(PatternOrResolvedMut::Resolved(resolved)) => {
118 if let Some(mut items) = resolved.get_list_binding_items() {
119 let len = items.clone().count();
120 return Ok(to_unsigned(index, len)
121 .and_then(|index| items.nth(index))
122 .map(|_| PatternOrResolvedMut::_ResolvedBinding));
123 }
124
125 if resolved.is_list() {
126 Ok(resolved
127 .get_list_item_at_mut(index)
128 .map(PatternOrResolvedMut::Resolved))
129 } else {
130 Err(GritPatternError::new(
131 "left side of a listIndex must be a list",
132 ))
133 }
134 }
135 Some(s) => Err(GritPatternError::new(format!(
136 "left side of a listIndex must be a list but got {:?}",
137 s
138 ))),
139 },
140 ListOrContainer::List(l) => Ok(l.get(index).map(PatternOrResolvedMut::Pattern)),
141 }
142 }
143
144 pub fn set_resolved<'a>(
145 &'a self,
146 state: &mut State<'a, Q>,
147 lang: &Q::Language<'a>,
148 value: Q::ResolvedPattern<'a>,
149 ) -> GritResult<bool> {
150 let index = self.get_index(state, lang)?;
151 match &self.list {
152 ListOrContainer::Container(c) => match c.get_pattern_or_resolved_mut(state, lang)? {
153 None => Ok(false),
154 Some(PatternOrResolvedMut::Resolved(resolved)) => {
155 resolved.set_list_item_at_mut(index, value)
156 }
157 Some(_) => Err(GritPatternError::new(
158 "accessor can only mutate a resolved list",
159 )),
160 },
161 ListOrContainer::List(_) => Err(GritPatternError::new("cannot mutate a list literal")),
162 }
163 }
164}
165
166pub fn to_unsigned(index: isize, len: usize) -> Option<usize> {
167 if index >= 0 {
168 Some(index as usize)
169 } else if len as isize + index < 0 {
170 None
171 } else {
172 Some((len as isize + index) as usize)
173 }
174}
175
176impl<Q: QueryContext> PatternName for ListIndex<Q> {
177 fn name(&self) -> &'static str {
178 "LIST_INDEX"
179 }
180}
181
182impl<Q: QueryContext> Matcher<Q> for ListIndex<Q> {
183 fn execute<'a>(
184 &'a self,
185 binding: &Q::ResolvedPattern<'a>,
186 state: &mut State<'a, Q>,
187 context: &'a Q::ExecContext<'a>,
188 logs: &mut AnalysisLogs,
189 ) -> GritResult<bool> {
190 match self.get(state, context.language())? {
191 Some(PatternOrResolved::Resolved(r)) => {
192 execute_resolved_with_binding(r, binding, state, context.language())
193 }
194 Some(PatternOrResolved::ResolvedBinding(r)) => {
195 execute_resolved_with_binding(&r, binding, state, context.language())
196 }
197 Some(PatternOrResolved::Pattern(p)) => p.execute(binding, state, context, logs),
198 None => Ok(binding.matches_false_or_undefined()),
199 }
200 }
201}