1use perl_ast::ast::Node;
2use std::ops::Range;
3
4use crate::{PragmaSnapshot, PragmaState, range_builder};
5
6#[derive(Debug, Clone, PartialEq)]
8pub struct PragmaStateQuery {
9 offset: usize,
10 snapshot: PragmaSnapshot,
11}
12
13impl PragmaStateQuery {
14 #[must_use]
16 pub fn offset(&self) -> usize {
17 self.offset
18 }
19
20 #[must_use]
22 pub fn snapshot(&self) -> &PragmaSnapshot {
23 &self.snapshot
24 }
25}
26
27#[derive(Debug, Clone, Default, PartialEq)]
30pub struct CompileTimePragmaEnvironment {
31 map: PragmaMap,
32}
33
34#[derive(Debug, Clone, PartialEq)]
36#[non_exhaustive]
37pub struct PragmaEntry {
38 pub range: Range<usize>,
43 pub snapshot: PragmaSnapshot,
45}
46
47#[derive(Debug, Clone, Default, PartialEq)]
49#[non_exhaustive]
50pub struct PragmaMap {
51 entries: Box<[PragmaEntry]>,
52}
53
54impl CompileTimePragmaEnvironment {
55 #[must_use]
57 pub fn build(ast: &Node) -> Self {
58 let mut ranges = Vec::new();
59 let mut current_state = PragmaState::default();
60 range_builder::build_ranges(ast, &mut current_state, &mut ranges);
61 ranges.sort_by_key(|(range, _)| range.start);
62
63 let entries = ranges
64 .into_iter()
65 .map(|(range, state)| PragmaEntry { range, snapshot: PragmaSnapshot::from(state) })
66 .collect::<Vec<_>>()
67 .into_boxed_slice();
68
69 Self { map: PragmaMap { entries } }
70 }
71
72 #[must_use]
74 pub fn query_at(&self, offset: usize) -> PragmaStateQuery {
75 PragmaStateQuery { offset, snapshot: self.snapshot_at(offset) }
76 }
77
78 #[must_use]
80 pub fn snapshot_at(&self, offset: usize) -> PragmaSnapshot {
81 self.map.snapshot_at(offset)
82 }
83
84 #[must_use]
86 pub fn map(&self) -> &PragmaMap {
87 &self.map
88 }
89
90 #[must_use]
92 pub fn as_map(&self) -> Vec<(Range<usize>, PragmaSnapshot)> {
93 self.map.to_tuples()
94 }
95}
96
97impl PragmaMap {
98 #[must_use]
100 pub fn snapshot_at(&self, offset: usize) -> PragmaSnapshot {
101 let idx = self.entries.partition_point(|entry| entry.range.start <= offset);
102 let snapshot = if idx > 0 {
103 self.entries[idx - 1].snapshot.clone()
104 } else {
105 PragmaSnapshot::default()
106 };
107
108 normalize_snapshot(snapshot)
109 }
110
111 #[must_use]
113 pub fn state_at(&self, offset: usize) -> PragmaState {
114 self.snapshot_at(offset).into()
115 }
116
117 #[must_use]
119 pub fn final_state(&self) -> PragmaState {
120 let state = self
121 .entries
122 .last()
123 .map_or_else(PragmaState::default, |entry| entry.snapshot.clone().into());
124
125 normalize_state(state)
126 }
127
128 #[must_use]
130 pub fn cursor(&self) -> PragmaQueryCursor {
131 PragmaQueryCursor::new()
132 }
133
134 #[must_use]
136 pub fn entries(&self) -> &[PragmaEntry] {
137 &self.entries
138 }
139
140 #[must_use]
142 pub fn to_tuples(&self) -> Vec<(Range<usize>, PragmaSnapshot)> {
143 self.entries.iter().map(|e| (e.range.clone(), e.snapshot.clone())).collect()
144 }
145}
146
147fn normalize_snapshot(mut snapshot: PragmaSnapshot) -> PragmaSnapshot {
148 if snapshot.state.signatures_strict {
149 snapshot.state.strict_vars = true;
150 snapshot.state.strict_subs = true;
151 snapshot.state.strict_refs = true;
152 }
153
154 snapshot
155}
156
157pub(crate) fn normalize_state(mut state: PragmaState) -> PragmaState {
158 if state.signatures_strict {
159 state.strict_vars = true;
160 state.strict_subs = true;
161 state.strict_refs = true;
162 }
163
164 state
165}
166
167#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
172pub struct PragmaQueryCursor {
173 index: usize,
174}
175
176impl PragmaQueryCursor {
177 #[must_use]
179 pub fn new() -> Self {
180 Self::default()
181 }
182
183 pub fn snapshot_at(&mut self, pragma_map: &PragmaMap, offset: usize) -> PragmaSnapshot {
189 let snapshot = self
190 .entry_for_offset(pragma_map.entries(), offset)
191 .map_or_else(PragmaSnapshot::default, |entry| entry.snapshot.clone());
192
193 normalize_snapshot(snapshot)
194 }
195
196 pub fn state_at(&mut self, pragma_map: &PragmaMap, offset: usize) -> PragmaState {
198 self.snapshot_at(pragma_map, offset).into()
199 }
200
201 pub fn state_for_offset(
207 &mut self,
208 pragma_map: &[(Range<usize>, PragmaState)],
209 offset: usize,
210 ) -> PragmaState {
211 if pragma_map.is_empty() {
212 return PragmaState::default();
213 }
214
215 if self.index >= pragma_map.len() {
216 self.index = pragma_map.len() - 1;
217 }
218
219 if pragma_map[self.index].0.start > offset {
220 self.index = pragma_map.partition_point(|(range, _)| range.start <= offset);
221 if self.index > 0 {
222 self.index -= 1;
223 }
224 } else {
225 while self.index + 1 < pragma_map.len() && pragma_map[self.index + 1].0.start <= offset
226 {
227 self.index += 1;
228 }
229 }
230
231 let state = if pragma_map[self.index].0.start <= offset {
232 pragma_map[self.index].1.clone()
233 } else {
234 PragmaState::default()
235 };
236
237 normalize_state(state)
238 }
239
240 fn entry_for_offset<'a>(
241 &mut self,
242 entries: &'a [PragmaEntry],
243 offset: usize,
244 ) -> Option<&'a PragmaEntry> {
245 if entries.is_empty() {
246 return None;
247 }
248
249 if self.index >= entries.len() {
250 self.index = entries.len() - 1;
251 }
252
253 if entries[self.index].range.start > offset {
254 self.index = entries.partition_point(|entry| entry.range.start <= offset);
255 if self.index > 0 {
256 self.index -= 1;
257 }
258 } else {
259 while self.index + 1 < entries.len() && entries[self.index + 1].range.start <= offset {
260 self.index += 1;
261 }
262 }
263
264 if entries[self.index].range.start <= offset { Some(&entries[self.index]) } else { None }
265 }
266}