gridline_engine/engine/
deps.rs1use regex::Regex;
2
3use super::cell_ref::CellRef;
4
5pub fn extract_dependencies(script: &str) -> Vec<CellRef> {
7 let mut deps = Vec::new();
8
9 let script = strip_string_literals(script);
11
12 let range_re = crate::builtins::range_fn_re();
14
15 let script_without_ranges = range_re.replace_all(&script, "").to_string();
17
18 for caps in range_re.captures_iter(&script) {
20 if let (Some(start), Some(end)) = (CellRef::from_str(&caps[2]), CellRef::from_str(&caps[3]))
21 {
22 let min_row = start.row.min(end.row);
23 let max_row = start.row.max(end.row);
24 let min_col = start.col.min(end.col);
25 let max_col = start.col.max(end.col);
26 for row in min_row..=max_row {
27 for col in min_col..=max_col {
28 deps.push(CellRef::new(row, col));
29 }
30 }
31 }
32 }
33
34 let cell_re = Regex::new(r"\b([A-Za-z]+)([0-9]+)\b").unwrap();
36
37 for caps in cell_re.captures_iter(&script_without_ranges) {
38 let cell_ref = format!("{}{}", &caps[1], &caps[2]);
39 if let Some(cr) = CellRef::from_str(&cell_ref) {
40 deps.push(cr);
41 }
42 }
43
44 deps
45}
46
47fn strip_string_literals(script: &str) -> String {
48 let mut out = String::with_capacity(script.len());
49 let mut in_string = false;
50 let mut escaped = false;
51
52 for ch in script.chars() {
53 if in_string {
54 if escaped {
55 escaped = false;
56 out.push(' ');
57 continue;
58 }
59 if ch == '\\' {
60 escaped = true;
61 out.push(' ');
62 continue;
63 }
64 if ch == '"' {
65 in_string = false;
66 out.push('"');
67 } else {
68 out.push(' ');
69 }
70 } else if ch == '"' {
71 in_string = true;
72 out.push('"');
73 } else {
74 out.push(ch);
75 }
76 }
77
78 out
79}
80
81pub fn parse_range(range: &str) -> Option<(usize, usize, usize, usize)> {
83 let parts: Vec<&str> = range.split(':').collect();
84 if parts.len() != 2 {
85 return None;
86 }
87 let start = CellRef::from_str(parts[0])?;
88 let end = CellRef::from_str(parts[1])?;
89 Some((start.row, start.col, end.row, end.col))
90}