oxur_lang/
expander.rs

1//! Stage 2: Expand
2//!
3//! Converts Surface Forms into Core Forms through macro expansion and desugaring.
4//! This is where syntactic sugar gets transformed into canonical forms.
5
6use crate::core_forms::CoreForm;
7use crate::parser::SurfaceForm;
8use crate::Result;
9use oxur_smap::SourceMap;
10
11/// Expander handles macro expansion and desugaring
12pub struct Expander {
13    source_map: SourceMap,
14}
15
16impl Expander {
17    pub fn new() -> Self {
18        Self { source_map: SourceMap::new() }
19    }
20
21    /// Expand Surface Forms into Core Forms
22    pub fn expand(&mut self, forms: Vec<SurfaceForm>) -> Result<Vec<CoreForm>> {
23        let mut core_forms = Vec::new();
24
25        for form in forms {
26            core_forms.push(self.expand_form(form)?);
27        }
28
29        Ok(core_forms)
30    }
31
32    fn expand_form(&mut self, form: SurfaceForm) -> Result<CoreForm> {
33        match form {
34            SurfaceForm::Symbol { name, span } => {
35                let id = oxur_smap::new_node_id();
36                let pos = Self::span_to_source_pos(&span);
37                self.source_map.record_surface_node(id, pos);
38                Ok(CoreForm::Symbol { id, name })
39            }
40            SurfaceForm::Number { value, span } => {
41                let id = oxur_smap::new_node_id();
42                let pos = Self::span_to_source_pos(&span);
43                self.source_map.record_surface_node(id, pos);
44                Ok(CoreForm::Number { id, value })
45            }
46            SurfaceForm::String { value, span } => {
47                let id = oxur_smap::new_node_id();
48                let pos = Self::span_to_source_pos(&span);
49                self.source_map.record_surface_node(id, pos);
50                Ok(CoreForm::String { id, value })
51            }
52            SurfaceForm::List { elements, span } => {
53                // Check if this is a special form (like deffn)
54                if !elements.is_empty() {
55                    if let SurfaceForm::Symbol { ref name, .. } = elements[0] {
56                        if name == "deffn" {
57                            return self.expand_deffn(elements, span);
58                        }
59                    }
60                }
61
62                // Otherwise, expand as a regular list
63                self.expand_list(elements, span)
64            }
65        }
66    }
67
68    /// Convert a Span to SourcePos, using start position for multi-line spans
69    fn span_to_source_pos(span: &oxur_smap::Span) -> oxur_smap::SourcePos {
70        // Use start position and length 1 (exact length not critical for error reporting)
71        oxur_smap::SourcePos::new(
72            span.file.clone(),
73            span.start_line,
74            span.start_column,
75            1, // Length placeholder
76        )
77    }
78
79    fn expand_deffn(
80        &mut self,
81        elements: Vec<SurfaceForm>,
82        span: oxur_smap::Span,
83    ) -> Result<CoreForm> {
84        // (deffn name params body...)
85        if elements.len() < 4 {
86            return Err(crate::Error::Syntax(
87                "deffn requires at least name, params, and body".to_string(),
88            ));
89        }
90
91        let id = oxur_smap::new_node_id();
92        let pos = Self::span_to_source_pos(&span);
93        self.source_map.record_surface_node(id, pos);
94
95        // Extract function name
96        let name = match &elements[1] {
97            SurfaceForm::Symbol { name, .. } => name.clone(),
98            _ => return Err(crate::Error::Syntax("deffn name must be a symbol".to_string())),
99        };
100
101        // Extract parameters (empty list for now, we'll handle typed params later)
102        let params = match &elements[2] {
103            SurfaceForm::List { elements: param_list, .. } => {
104                let mut params = Vec::new();
105                for param in param_list {
106                    if let SurfaceForm::Symbol { name, .. } = param {
107                        params.push(name.clone());
108                    } else {
109                        return Err(crate::Error::Syntax("Parameter must be a symbol".to_string()));
110                    }
111                }
112                params
113            }
114            _ => return Err(crate::Error::Syntax("deffn params must be a list".to_string())),
115        };
116
117        // Body is the remaining forms (for now, just take the first body form)
118        let body = if elements.len() > 3 {
119            Box::new(self.expand_form(elements[3].clone())?)
120        } else {
121            return Err(crate::Error::Syntax("deffn requires a body".to_string()));
122        };
123
124        Ok(CoreForm::DefineFunc { id, name, params, body })
125    }
126
127    fn expand_list(
128        &mut self,
129        elements: Vec<SurfaceForm>,
130        span: oxur_smap::Span,
131    ) -> Result<CoreForm> {
132        let id = oxur_smap::new_node_id();
133        let pos = Self::span_to_source_pos(&span);
134        self.source_map.record_surface_node(id, pos);
135
136        let mut expanded_elements = Vec::new();
137        for element in elements {
138            expanded_elements.push(self.expand_form(element)?);
139        }
140
141        Ok(CoreForm::List { id, elements: expanded_elements })
142    }
143
144    /// Get the source map after expansion
145    pub fn source_map(&self) -> &SourceMap {
146        &self.source_map
147    }
148
149    /// Check if the source map is empty
150    pub fn is_empty(&self) -> bool {
151        let stats = self.source_map.stats();
152        stats.surface_nodes == 0 && stats.expansions == 0 && stats.lowerings == 0
153    }
154}
155
156impl Default for Expander {
157    fn default() -> Self {
158        Self::new()
159    }
160}
161
162#[cfg(test)]
163mod tests {
164    use super::*;
165
166    #[test]
167    fn test_expander_creation() {
168        let expander = Expander::new();
169        assert!(expander.is_empty());
170    }
171
172    #[test]
173    fn test_expander_default() {
174        let expander = Expander::default();
175        assert!(expander.is_empty());
176    }
177
178    #[test]
179    fn test_expand_empty() {
180        let mut expander = Expander::new();
181        let result = expander.expand(vec![]);
182        assert!(result.is_ok());
183        assert_eq!(result.unwrap().len(), 0);
184    }
185
186    #[test]
187    fn test_source_map_access() {
188        let expander = Expander::new();
189        let _map = expander.source_map();
190        assert!(expander.is_empty());
191    }
192
193    #[test]
194    fn test_expand_deffn_hello_world() {
195        use crate::parser::Parser;
196
197        let source = r#"(deffn main ()
198  (println! "Hello, world!"))"#;
199
200        let mut parser = Parser::new(source.to_string());
201        let surface_forms = parser.parse().unwrap();
202
203        let mut expander = Expander::new();
204        let core_forms = expander.expand(surface_forms).unwrap();
205
206        assert_eq!(core_forms.len(), 1);
207
208        // Should be a DefineFunc
209        if let CoreForm::DefineFunc { name, params, body, .. } = &core_forms[0] {
210            assert_eq!(name, "main");
211            assert_eq!(params.len(), 0); // No parameters
212
213            // Body should be a list (println! "Hello, world!")
214            if let CoreForm::List { elements, .. } = &**body {
215                assert_eq!(elements.len(), 2);
216
217                // First element should be println! symbol
218                if let CoreForm::Symbol { name, .. } = &elements[0] {
219                    assert_eq!(name, "println!");
220                } else {
221                    panic!("Expected Symbol(println!)");
222                }
223
224                // Second element should be the string
225                if let CoreForm::String { value, .. } = &elements[1] {
226                    assert_eq!(value, "Hello, world!");
227                } else {
228                    panic!("Expected String");
229                }
230            } else {
231                panic!("Expected List as body");
232            }
233        } else {
234            panic!("Expected DefineFunc");
235        }
236    }
237
238    #[test]
239    fn test_expand_symbol() {
240        use oxur_smap::Span;
241
242        let mut expander = Expander::new();
243        let span = Span::repl(1, 1, 1, 5);
244        let surface = SurfaceForm::Symbol { span, name: "test".to_string() };
245        let result = expander.expand_form(surface).unwrap();
246
247        if let CoreForm::Symbol { name, .. } = result {
248            assert_eq!(name, "test");
249        } else {
250            panic!("Expected Symbol");
251        }
252    }
253
254    #[test]
255    fn test_expand_number() {
256        use oxur_smap::Span;
257
258        let mut expander = Expander::new();
259        let span = Span::repl(1, 1, 1, 3);
260        let surface = SurfaceForm::Number { span, value: 42 };
261        let result = expander.expand_form(surface).unwrap();
262
263        if let CoreForm::Number { value, .. } = result {
264            assert_eq!(value, 42);
265        } else {
266            panic!("Expected Number");
267        }
268    }
269
270    #[test]
271    fn test_expand_string() {
272        use oxur_smap::Span;
273
274        let mut expander = Expander::new();
275        let span = Span::repl(1, 1, 1, 7);
276        let surface = SurfaceForm::String { span, value: "hello".to_string() };
277        let result = expander.expand_form(surface).unwrap();
278
279        if let CoreForm::String { value, .. } = result {
280            assert_eq!(value, "hello");
281        } else {
282            panic!("Expected String");
283        }
284    }
285
286    #[test]
287    fn test_source_map_symbol() {
288        use crate::parser::Parser;
289
290        let source = "hello";
291        let mut parser = Parser::new(source.to_string());
292        let forms = parser.parse().unwrap();
293
294        let mut expander = Expander::new();
295        let core_forms = expander.expand(forms).unwrap();
296
297        // Get the NodeId from the CoreForm
298        if let CoreForm::Symbol { id, .. } = &core_forms[0] {
299            // Look up the source position via the SourceMap
300            let source_map = expander.source_map();
301            let pos = source_map.get_surface_position(id);
302
303            assert!(pos.is_some(), "Mapping should be recorded");
304            let pos = pos.unwrap();
305            assert_eq!(pos.line, 1);
306            assert_eq!(pos.column, 1);
307        } else {
308            panic!("Expected Symbol");
309        }
310    }
311
312    #[test]
313    fn test_source_map_number() {
314        use crate::parser::Parser;
315
316        let source = "42";
317        let mut parser = Parser::new(source.to_string());
318        let forms = parser.parse().unwrap();
319
320        let mut expander = Expander::new();
321        let core_forms = expander.expand(forms).unwrap();
322
323        if let CoreForm::Number { id, .. } = &core_forms[0] {
324            let source_map = expander.source_map();
325            let pos = source_map.get_surface_position(id);
326
327            assert!(pos.is_some());
328            let pos = pos.unwrap();
329            assert_eq!(pos.line, 1);
330            assert_eq!(pos.column, 1);
331        } else {
332            panic!("Expected Number");
333        }
334    }
335
336    #[test]
337    fn test_source_map_list() {
338        use crate::parser::Parser;
339
340        let source = "(+ 1 2)";
341        let mut parser = Parser::new(source.to_string());
342        let forms = parser.parse().unwrap();
343
344        let mut expander = Expander::new();
345        let core_forms = expander.expand(forms).unwrap();
346
347        if let CoreForm::List { id, elements, .. } = &core_forms[0] {
348            let source_map = expander.source_map();
349
350            // Check list node mapping
351            let list_pos = source_map.get_surface_position(id);
352            assert!(list_pos.is_some());
353            assert_eq!(list_pos.unwrap().line, 1);
354
355            // Check first element ('+' symbol) mapping
356            if let CoreForm::Symbol { id: elem_id, .. } = &elements[0] {
357                let elem_pos = source_map.get_surface_position(elem_id);
358                assert!(elem_pos.is_some());
359                assert_eq!(elem_pos.unwrap().column, 2); // After '('
360            } else {
361                panic!("Expected Symbol");
362            }
363        } else {
364            panic!("Expected List");
365        }
366    }
367
368    #[test]
369    fn test_source_map_deffn() {
370        use crate::parser::Parser;
371
372        let source = "(deffn main () 42)";
373        let mut parser = Parser::new(source.to_string());
374        let forms = parser.parse().unwrap();
375
376        let mut expander = Expander::new();
377        let core_forms = expander.expand(forms).unwrap();
378
379        if let CoreForm::DefineFunc { id, .. } = &core_forms[0] {
380            let source_map = expander.source_map();
381            let pos = source_map.get_surface_position(id);
382
383            assert!(pos.is_some());
384            assert_eq!(pos.unwrap().line, 1);
385            assert_eq!(pos.unwrap().column, 1);
386        } else {
387            panic!("Expected DefineFunc");
388        }
389    }
390
391    #[test]
392    fn test_source_map_stats() {
393        use crate::parser::Parser;
394
395        let source = "(+ 1 2)"; // 4 nodes: list, +, 1, 2
396        let mut parser = Parser::new(source.to_string());
397        let forms = parser.parse().unwrap();
398
399        let mut expander = Expander::new();
400        let _core_forms = expander.expand(forms).unwrap();
401
402        let source_map = expander.source_map();
403        let stats = source_map.stats();
404
405        // Should have 4 surface nodes recorded
406        assert_eq!(stats.surface_nodes, 4);
407        assert_eq!(stats.expansions, 0); // No expansion chains yet (Stage 1.7 only)
408        assert_eq!(stats.lowerings, 0); // No lowerings yet
409    }
410}