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