sphinx/debug/
symbol.rs

1use std::rc::Rc;
2
3pub mod table;
4pub mod resolver;
5pub mod errors;
6
7pub use table::{ChunkSymbols, DebugSymbolTable};
8pub use resolver::{DebugSymbolResolver, ResolvedSymbolTable};
9
10
11// Max source file length ~4 billion characters (assuming mostly single byte UTF8 that's a ~4GB file)
12// Max token length 65535 characters
13pub type TokenIndex = u32;
14pub type TokenLength = u16;
15
16
17/// When provided along with the source text, identifies a span of source code.
18/// Because they are passed around everywhere, the debug symbols used by Sphinx are very light weight.
19/// Just an index into the source text and a length. In order to be useful they must first be resolved
20/// using a `DebugSymbolResolver`.
21#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
22pub struct DebugSymbol {
23    start: TokenIndex,
24    length: TokenLength,
25}
26
27impl TryFrom<(TokenIndex, TokenIndex)> for DebugSymbol {
28    type Error = (TokenIndex, TokenIndex);
29    fn try_from(tuple: (TokenIndex, TokenIndex)) -> Result<Self, Self::Error> {
30        let (start, end) = tuple;
31        if let Ok(length) = TokenLength::try_from(end - start) {
32            Ok(DebugSymbol { start, length })
33        } else {
34            Err((start, end))
35        }
36    }
37}
38
39impl DebugSymbol {
40    pub fn new(start: TokenIndex, length: TokenLength) -> Self {
41        Self { start, length }
42    }
43    
44    pub fn start(&self) -> TokenIndex { self.start }
45    pub fn end(&self) -> TokenIndex { self.start + TokenIndex::from(self.length) }
46    pub fn len(&self) -> TokenLength { self.length }
47    pub fn is_empty(&self) -> bool { self.length == 0 } // ok clippy, sure.
48}
49
50
51
52// Resolved Symbols
53
54#[derive(Debug, Clone)]
55pub struct ResolvedSymbol {
56    lines: Vec<Rc<String>>,
57    lineno: usize,  // line number at the start of the symbol
58    start: usize,   // start,end indices into self.text
59    end: usize,
60}
61
62impl ResolvedSymbol {
63    pub fn new(lines: Vec<Rc<String>>, lineno: usize, start: usize, end: usize) -> Self {
64        ResolvedSymbol { lines, lineno, start, end }
65    }
66    
67    pub fn is_multiline(&self) -> bool { self.lines.len() > 1 }
68    pub fn line_count(&self) -> usize { self.lines.len() }
69    
70    pub fn lineno(&self) -> usize { self.lineno }
71    pub fn start(&self) -> usize { self.start }
72    pub fn end(&self) -> usize { self.end }
73    
74    pub fn start_col(&self) -> usize { self.start }
75    pub fn end_col(&self) -> usize {
76        let offset = self.lines.iter()
77            .take(self.lines.len() - 1)
78            .map(|line| line.len())
79            .reduce(|acc, n| acc + n)
80            .unwrap_or(0);
81        
82        self.end - offset  // subtract index to start of final line
83    }
84    
85    pub fn iter_whole_lines(&self) -> impl Iterator<Item=&str> { 
86        self.lines.iter().map(|s| s.as_str())
87    }
88    
89    // write just the symbol text itself
90    pub fn iter_lines(&self) -> impl Iterator<Item=&str> { 
91        self.lines.iter().scan(0, |cur_line_start, line| {
92            let cur_line_end = *cur_line_start + line.len();
93            
94            let result;
95            if (*cur_line_start <= self.start) && (self.start < cur_line_end) {
96                let start = self.start - *cur_line_start;
97                result = &line[start..];
98            } else if (*cur_line_start <= self.end) && (self.end < cur_line_end) {
99                let end = self.end - *cur_line_start;
100                result = &line[..end];
101            } else {
102                result = line;
103            };
104            
105            *cur_line_start = cur_line_end;
106            Some(result)
107        })
108    }
109
110}