logicaffeine_compile/
sourcemap.rs1use crate::intern::Symbol;
25use crate::token::Span;
26use std::collections::HashMap;
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq)]
30pub enum OwnershipRole {
31 GiveObject,
33 GiveRecipient,
35 ShowObject,
37 ShowRecipient,
39 LetBinding,
41 SetTarget,
43 ZoneLocal,
45}
46
47#[derive(Debug, Clone)]
49pub struct VarOrigin {
50 pub logos_name: Symbol,
51 pub span: Span,
52 pub role: OwnershipRole,
53}
54
55#[derive(Debug, Clone, Default)]
57pub struct SourceMap {
58 line_to_span: HashMap<u32, Span>,
60
61 var_origins: HashMap<String, VarOrigin>,
63
64 logos_source: String,
66}
67
68impl SourceMap {
69 pub fn new(logos_source: String) -> Self {
71 Self {
72 line_to_span: HashMap::new(),
73 var_origins: HashMap::new(),
74 logos_source,
75 }
76 }
77
78 pub fn get_span_for_line(&self, line: u32) -> Option<Span> {
80 self.line_to_span.get(&line).copied()
81 }
82
83 pub fn get_var_origin(&self, rust_var: &str) -> Option<&VarOrigin> {
85 self.var_origins.get(rust_var)
86 }
87
88 pub fn logos_source(&self) -> &str {
90 &self.logos_source
91 }
92
93 pub fn line_span_entries(&self) -> Vec<(u32, Span)> {
95 let mut entries: Vec<(u32, Span)> =
96 self.line_to_span.iter().map(|(l, s)| (*l, *s)).collect();
97 entries.sort_by_key(|(l, _)| *l);
98 entries
99 }
100
101 pub fn find_nearest_span(&self, rust_line: u32) -> Option<Span> {
103 if let Some(span) = self.line_to_span.get(&rust_line) {
105 return Some(*span);
106 }
107
108 for offset in 1..=5 {
110 if rust_line > offset {
111 if let Some(span) = self.line_to_span.get(&(rust_line - offset)) {
112 return Some(*span);
113 }
114 }
115 if let Some(span) = self.line_to_span.get(&(rust_line + offset)) {
116 return Some(*span);
117 }
118 }
119
120 None
121 }
122}
123
124#[derive(Debug)]
126pub struct SourceMapBuilder {
127 current_line: u32,
128 map: SourceMap,
129}
130
131impl SourceMapBuilder {
132 pub fn new(logos_source: &str) -> Self {
134 Self {
135 current_line: 1,
136 map: SourceMap::new(logos_source.to_string()),
137 }
138 }
139
140 pub fn record_line(&mut self, logos_span: Span) {
142 self.map.line_to_span.insert(self.current_line, logos_span);
143 }
144
145 pub fn record_line_at(&mut self, line: u32, logos_span: Span) {
149 self.map.line_to_span.insert(line, logos_span);
150 }
151
152 pub fn record_var(&mut self, rust_name: &str, logos_name: Symbol, span: Span, role: OwnershipRole) {
154 self.map.var_origins.insert(
155 rust_name.to_string(),
156 VarOrigin {
157 logos_name,
158 span,
159 role,
160 },
161 );
162 }
163
164 pub fn newline(&mut self) {
166 self.current_line += 1;
167 }
168
169 pub fn add_lines(&mut self, count: u32) {
171 self.current_line += count;
172 }
173
174 pub fn current_line(&self) -> u32 {
176 self.current_line
177 }
178
179 pub fn build(self) -> SourceMap {
181 self.map
182 }
183}
184
185#[cfg(test)]
186mod tests {
187 use super::*;
188
189 #[test]
190 fn source_map_stores_line_mappings() {
191 let mut map = SourceMap::new("Let x be 5.".to_string());
192 map.line_to_span.insert(1, Span::new(0, 11));
193
194 assert_eq!(map.get_span_for_line(1), Some(Span::new(0, 11)));
195 assert_eq!(map.get_span_for_line(2), None);
196 }
197
198 #[test]
199 fn source_map_builder_tracks_lines() {
200 let mut builder = SourceMapBuilder::new("test source");
201 assert_eq!(builder.current_line(), 1);
202
203 builder.newline();
204 assert_eq!(builder.current_line(), 2);
205
206 builder.add_lines(3);
207 assert_eq!(builder.current_line(), 5);
208 }
209
210 #[test]
211 fn source_map_builder_records_spans() {
212 let mut builder = SourceMapBuilder::new("Let x be 5.\nLet y be 10.");
213 builder.record_line(Span::new(0, 11));
214 builder.newline();
215 builder.record_line(Span::new(12, 24));
216
217 let map = builder.build();
218 assert_eq!(map.get_span_for_line(1), Some(Span::new(0, 11)));
219 assert_eq!(map.get_span_for_line(2), Some(Span::new(12, 24)));
220 }
221
222 #[test]
223 fn find_nearest_span_searches_nearby() {
224 let mut builder = SourceMapBuilder::new("source");
225 builder.record_line(Span::new(0, 10));
226 builder.add_lines(5);
227 let map = builder.build();
230 assert_eq!(map.find_nearest_span(3), Some(Span::new(0, 10)));
232 }
233}