basic/mach/
listing.rs

1use super::MAX_LINE_LEN;
2use crate::error;
3use crate::lang::{Column, Error, Line, LineNumber, MaxValue};
4use std::collections::{btree_map::Values, BTreeMap, HashMap};
5use std::ops::{Range, RangeInclusive};
6use std::sync::Arc;
7
8#[derive(Debug, Clone, Default)]
9pub struct Listing {
10    source: Arc<BTreeMap<LineNumber, Line>>,
11    pub indirect_errors: Arc<Vec<Error>>,
12    pub direct_errors: Arc<Vec<Error>>,
13}
14
15impl Listing {
16    pub fn clear(&mut self) {
17        self.source = Arc::default();
18        self.indirect_errors = Arc::default();
19        self.direct_errors = Arc::default();
20    }
21
22    pub fn is_empty(&self) -> bool {
23        self.source.is_empty()
24    }
25
26    pub fn insert(&mut self, line: Line) -> Option<Line> {
27        Arc::get_mut(&mut self.source)
28            .unwrap()
29            .insert(line.number(), line)
30    }
31
32    pub fn remove(&mut self, ln: LineNumber) -> Option<Line> {
33        Arc::get_mut(&mut self.source).unwrap().remove(&ln)
34    }
35
36    pub fn remove_range(&mut self, range: RangeInclusive<LineNumber>) -> bool {
37        let to_remove = self
38            .source
39            .range(range)
40            .map(|(k, _)| *k)
41            .collect::<Vec<LineNumber>>();
42        if to_remove.is_empty() {
43            return false;
44        }
45        let source = Arc::get_mut(&mut self.source).unwrap();
46        for line_number in to_remove {
47            source.remove(&line_number);
48        }
49        true
50    }
51
52    pub fn line(&self, num: usize) -> Option<(String, Vec<Range<usize>>)> {
53        if num > LineNumber::max_value() as usize {
54            return None;
55        }
56        let mut range = Some(num as u16)..=Some(num as u16);
57        self.list_line(&mut range)
58    }
59
60    pub fn lines(&self) -> Values<'_, LineNumber, Line> {
61        self.source.values()
62    }
63
64    /// Used for loading a new Listing from a file.
65    pub fn load_str(&mut self, line: &str) -> Result<(), Error> {
66        if line.len() > MAX_LINE_LEN {
67            return Err(error!(LineBufferOverflow));
68        }
69        let line = Line::new(line);
70        if line.is_empty() {
71            if !line.is_direct() {
72                Arc::get_mut(&mut self.source)
73                    .unwrap()
74                    .remove(&line.number());
75            }
76            Ok(())
77        } else if line.is_direct() {
78            Err(error!(DirectStatementInFile))
79        } else {
80            self.insert(line);
81            Ok(())
82        }
83    }
84
85    pub fn list_line(
86        &self,
87        range: &mut RangeInclusive<LineNumber>,
88    ) -> Option<(String, Vec<Range<usize>>)> {
89        let mut source_range = self.source.range(range.clone());
90        if let Some((line_number, line)) = source_range.next() {
91            if line_number < range.end() {
92                if let Some(num) = line_number {
93                    *range = Some(num + 1)..=*range.end();
94                }
95            } else {
96                *range = Some(LineNumber::max_value() + 1)..=Some(LineNumber::max_value() + 1);
97            }
98            let columns: Vec<Column> = self
99                .indirect_errors
100                .iter()
101                .filter_map(|e| {
102                    if e.line_number() == *line_number {
103                        Some(e.column())
104                    } else {
105                        None
106                    }
107                })
108                .collect();
109            return Some((line.to_string(), columns));
110        }
111        None
112    }
113
114    pub fn renum(&mut self, new_start: u16, old_start: u16, step: u16) -> Result<(), Error> {
115        let mut changes: HashMap<u16, u16> = HashMap::default();
116        let mut old_end: u16 = LineNumber::max_value() + 1;
117        let mut new_num = new_start;
118        for (&ln, _) in self.source.iter() {
119            let ln = match ln {
120                Some(ln) => ln,
121                None => return Err(error!(InternalError)),
122            };
123            if ln >= old_start {
124                if old_end <= LineNumber::max_value() && old_end >= new_start {
125                    return Err(error!(IllegalFunctionCall));
126                }
127                if new_num > LineNumber::max_value() {
128                    return Err(error!(Overflow));
129                }
130                changes.insert(ln, new_num);
131                new_num = match new_num.checked_add(step) {
132                    Some(num) => num,
133                    None => return Err(error!(Overflow)),
134                };
135            } else {
136                old_end = ln;
137            }
138        }
139        let mut new_source: BTreeMap<LineNumber, Line> = BTreeMap::default();
140        for line in self.lines() {
141            let line = line.renum(&changes);
142            new_source.insert(line.number(), line);
143        }
144        self.source = Arc::from(new_source);
145        Ok(())
146    }
147}