assembler/
source.rs

1//! Representation of the original input.
2use std::fmt::{self, Display, Formatter};
3use std::ops::Range;
4
5#[derive(Debug)]
6pub(super) struct Source<'s> {
7    body: &'s str,
8}
9
10impl<'s> Source<'s> {
11    pub(super) fn new(body: &'s str) -> Source<'s> {
12        Source { body }
13    }
14
15    pub(super) fn as_str(&self) -> &str {
16        self.body
17    }
18
19    pub(super) fn extract(&self, range: Range<usize>) -> &str {
20        &self.body[range]
21    }
22
23    pub(super) fn location_of(&self, pos: usize) -> LineAndColumn {
24        const START_COL: u32 = 1;
25        const START_LINE: u32 = 1;
26
27        let mut line = START_LINE;
28        let mut column = START_COL;
29        for (i, ch) in self.body.char_indices() {
30            if i == pos {
31                break;
32            }
33            match ch {
34                '\n' => {
35                    column = START_COL;
36                    line += 1;
37                }
38                _ => {
39                    column += 1;
40                }
41            }
42        }
43        LineAndColumn { line, column }
44    }
45
46    pub(crate) fn extract_prefix(&self, pos: usize) -> &'s str {
47        let body_prefix = &self.body[0..pos];
48        let line_start = match body_prefix.rfind('\n') {
49            None => 0,
50            Some(n) => n + 1,
51        };
52        let prefix = &self.body[line_start..pos];
53        if prefix.chars().all(char::is_whitespace) {
54            prefix
55        } else {
56            ""
57        }
58    }
59}
60
61#[test]
62fn test_extract_prefix() {
63    fn extract_prefix(s: &str, pos: usize) -> &str {
64        let src = Source::new(s);
65        src.extract_prefix(pos)
66    }
67
68    assert_eq!(extract_prefix("hello", 0), "");
69    assert_eq!(extract_prefix(" hello", 0), "");
70    assert_eq!(extract_prefix(" hello", 1), " ");
71    assert_eq!(extract_prefix("x\nhello", 2), "");
72    assert_eq!(extract_prefix("x\n hello", 3), " ");
73    assert_eq!(extract_prefix("x\nY hello", 4), "");
74}
75
76#[derive(Debug, PartialEq, Eq, Hash, Clone)]
77pub struct LineAndColumn {
78    line: u32,
79    column: u32,
80}
81
82impl Display for LineAndColumn {
83    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
84        write!(f, "line {}, column {}", self.line, self.column)
85    }
86}
87
88pub trait Located {
89    fn location(&self) -> LineAndColumn;
90}
91
92#[derive(Debug, PartialEq, Eq, Hash)]
93pub struct WithLocation<T> {
94    pub(crate) inner: T,
95    pub(crate) location: LineAndColumn,
96}
97
98impl<T> Located for WithLocation<T> {
99    fn location(&self) -> LineAndColumn {
100        self.location.clone()
101    }
102}
103
104impl<T: Located> From<T> for WithLocation<T> {
105    fn from(inner: T) -> WithLocation<T> {
106        WithLocation {
107            location: inner.location().clone(),
108            inner,
109        }
110    }
111}
112
113impl<T> WithLocation<T> {
114    pub fn location(&self) -> &LineAndColumn {
115        &self.location
116    }
117}
118
119impl<T: Display> Display for WithLocation<T> {
120    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
121        write!(f, "{}: {}", &self.location, &self.inner)
122    }
123}