1use crate::Result;
6use oxur_lang::{CoreForm, NodeId};
7use std::collections::HashMap;
8use syn::{Block, FnArg};
9
10pub struct Lowerer {
12 source_map: oxur_smap::SourceMap,
13 #[allow(dead_code)] node_map: HashMap<NodeId, syn::Expr>,
15}
16
17impl Lowerer {
18 pub fn new(source_map: oxur_smap::SourceMap) -> Self {
19 Self { source_map, node_map: HashMap::new() }
20 }
21
22 pub fn lower(&mut self, forms: Vec<CoreForm>) -> Result<(syn::File, oxur_smap::SourceMap)> {
27 let mut items = Vec::new();
28
29 for form in forms {
30 items.push(self.lower_top_level(form)?);
31 }
32
33 self.source_map.freeze();
35
36 Ok((syn::File { shebang: None, attrs: vec![], items }, self.source_map.clone()))
37 }
38
39 fn lower_top_level(&mut self, form: CoreForm) -> Result<syn::Item> {
40 match form {
41 CoreForm::DefineFunc { name, params, body, id } => {
42 self.lower_function(name, params, *body, id)
43 }
44 _ => Err(crate::Error::Lowering(
45 "Only function definitions are supported at top level".to_string(),
46 )),
47 }
48 }
49
50 fn lower_function(
51 &mut self,
52 name: String,
53 params: Vec<String>,
54 body: CoreForm,
55 id: NodeId,
56 ) -> Result<syn::Item> {
57 use quote::format_ident;
58 use syn::{ItemFn, ReturnType, Signature};
59
60 let rust_id = oxur_smap::new_node_id();
62 self.source_map.record_lowering(id, rust_id);
63
64 let fn_name = format_ident!("{}", name);
66 let inputs = self.lower_params(params)?;
67
68 let sig = Signature {
69 constness: None,
70 asyncness: None,
71 unsafety: None,
72 abi: None,
73 fn_token: Default::default(),
74 ident: fn_name,
75 generics: Default::default(),
76 paren_token: Default::default(),
77 inputs,
78 variadic: None,
79 output: ReturnType::Default,
80 };
81
82 let block = self.lower_block(body)?;
84
85 Ok(syn::Item::Fn(ItemFn {
86 attrs: vec![],
87 vis: syn::Visibility::Inherited,
88 sig,
89 block: Box::new(block),
90 }))
91 }
92
93 fn lower_params(
94 &self,
95 _params: Vec<String>,
96 ) -> Result<syn::punctuated::Punctuated<FnArg, syn::Token![,]>> {
97 Ok(syn::punctuated::Punctuated::new())
99 }
100
101 fn lower_block(&mut self, body: CoreForm) -> Result<Block> {
102 use syn::parse_quote;
103
104 let stmt = self.lower_to_stmt(body)?;
106
107 Ok(parse_quote! {
108 {
109 #stmt
110 }
111 })
112 }
113
114 fn lower_to_stmt(&mut self, form: CoreForm) -> Result<syn::Stmt> {
115 match form {
116 CoreForm::List { elements, id } => {
117 let rust_id = oxur_smap::new_node_id();
119 self.source_map.record_lowering(id, rust_id);
120
121 if !elements.is_empty() {
123 if let CoreForm::Symbol { name, .. } = &elements[0] {
124 if name.ends_with('!') {
125 return self.lower_macro_call(name.clone(), elements[1..].to_vec());
126 }
127 }
128 }
129 Err(crate::Error::Lowering("Unsupported list form".to_string()))
130 }
131 _ => Err(crate::Error::Lowering(
132 "Only macro calls supported in function body for now".to_string(),
133 )),
134 }
135 }
136
137 fn lower_macro_call(&mut self, macro_name: String, args: Vec<CoreForm>) -> Result<syn::Stmt> {
138 use quote::format_ident;
139 use syn::{parse_quote, StmtMacro};
140
141 let name = macro_name.trim_end_matches('!');
143 let macro_ident = format_ident!("{}", name);
144
145 let arg_tokens = self.lower_macro_args(args)?;
147
148 let mac: syn::Macro = parse_quote! {
150 #macro_ident!(#arg_tokens)
151 };
152
153 Ok(syn::Stmt::Macro(StmtMacro { attrs: vec![], mac, semi_token: Some(Default::default()) }))
154 }
155
156 fn lower_macro_args(&mut self, args: Vec<CoreForm>) -> Result<proc_macro2::TokenStream> {
157 use quote::quote;
158
159 if args.is_empty() {
160 return Ok(quote! {});
161 }
162
163 if args.len() == 1 {
165 if let CoreForm::String { value, id } = &args[0] {
166 let rust_id = oxur_smap::new_node_id();
168 self.source_map.record_lowering(*id, rust_id);
169
170 let string_lit = value.as_str();
171 return Ok(quote! { #string_lit });
172 }
173 }
174
175 Err(crate::Error::Lowering("Only single string arguments supported for macros".to_string()))
176 }
177}
178
179#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[test]
186 fn test_lowerer_creation() {
187 let source_map = oxur_smap::SourceMap::new();
188 let lowerer = Lowerer::new(source_map);
189 assert_eq!(lowerer.node_map.len(), 0);
190 }
191
192 #[test]
193 fn test_lower_empty() {
194 let source_map = oxur_smap::SourceMap::new();
195 let mut lowerer = Lowerer::new(source_map);
196 let result = lowerer.lower(vec![]);
197 assert!(result.is_ok());
198 let (file, _source_map) = result.unwrap();
199 assert_eq!(file.items.len(), 0);
200 }
201
202 #[test]
203 fn test_lower_returns_syn_file() {
204 let source_map = oxur_smap::SourceMap::new();
205 let mut lowerer = Lowerer::new(source_map);
206 let result = lowerer.lower(vec![]);
207 assert!(result.is_ok());
208 let (file, _source_map) = result.unwrap();
209 assert!(file.shebang.is_none());
210 assert_eq!(file.attrs.len(), 0);
211 }
212
213 #[test]
214 fn test_lower_hello_world() {
215 use oxur_lang::{Expander, Parser};
216
217 let source = r#"(deffn main ()
219 (println! "Hello, world!"))"#;
220
221 let mut parser = Parser::new(source.to_string());
222 let surface_forms = parser.parse().unwrap();
223
224 let mut expander = Expander::new();
225 let core_forms = expander.expand(surface_forms).unwrap();
226 let source_map = expander.source_map().clone();
227
228 let mut lowerer = Lowerer::new(source_map);
230 let result = lowerer.lower(core_forms);
231
232 assert!(result.is_ok());
233 let (file, _source_map) = result.unwrap();
234
235 assert_eq!(file.items.len(), 1);
237
238 if let syn::Item::Fn(item_fn) = &file.items[0] {
240 assert_eq!(item_fn.sig.ident.to_string(), "main");
241 assert_eq!(item_fn.sig.inputs.len(), 0); assert_eq!(item_fn.block.stmts.len(), 1);
245
246 if let syn::Stmt::Macro(stmt_mac) = &item_fn.block.stmts[0] {
248 let path = &stmt_mac.mac.path;
250 assert_eq!(quote::quote!(#path).to_string(), "println");
251 } else {
252 panic!("Expected macro statement");
253 }
254 } else {
255 panic!("Expected function item");
256 }
257 }
258
259 #[test]
260 fn test_source_map_function_mapping() {
261 use oxur_lang::{Expander, Parser};
262
263 let source = r#"(deffn main ()
264 (println! "Hello"))"#;
265 let mut parser = Parser::new(source.to_string());
266 let surface_forms = parser.parse().unwrap();
267
268 let mut expander = Expander::new();
269 let core_forms = expander.expand(surface_forms).unwrap();
270 let source_map = expander.source_map().clone();
271
272 let mut lowerer = Lowerer::new(source_map);
274 let result = lowerer.lower(core_forms);
275 assert!(result.is_ok(), "Lowering failed: {:?}", result.err());
276
277 let (_file, source_map) = result.unwrap();
278 let stats = source_map.stats();
279
280 assert!(stats.surface_nodes > 0, "Should have surface node mappings");
282
283 assert!(stats.lowerings > 0, "Should have lowering mappings");
285 }
286
287 #[test]
288 fn test_source_map_preserved_through_lowering() {
289 use oxur_lang::{Expander, Parser};
290
291 let source = r#"(deffn main ()
292 (println! "Hello, world!"))"#;
293
294 let mut parser = Parser::new(source.to_string());
295 let surface_forms = parser.parse().unwrap();
296
297 let mut expander = Expander::new();
298 let core_forms = expander.expand(surface_forms).unwrap();
299
300 let core_id = if let oxur_lang::CoreForm::DefineFunc { id, .. } = &core_forms[0] {
302 *id
303 } else {
304 panic!("Expected DefineFunc");
305 };
306
307 let source_map = expander.source_map().clone();
308 let surface_pos = source_map.get_surface_position(&core_id);
309 assert!(surface_pos.is_some(), "Core node should have surface position");
310
311 let mut lowerer = Lowerer::new(source_map);
313 let result = lowerer.lower(core_forms);
314 assert!(result.is_ok());
315
316 let (_file, final_source_map) = result.unwrap();
317
318 let surface_pos_after = final_source_map.get_surface_position(&core_id);
320 assert!(surface_pos_after.is_some(), "Surface position should be preserved");
321
322 let stats = final_source_map.stats();
324 assert!(stats.lowerings > 0, "Should have lowering mappings");
325 }
326
327 #[test]
328 fn test_source_map_frozen_after_lowering() {
329 use oxur_lang::{Expander, Parser};
330
331 let source = r#"(deffn main ()
332 (println! "Test"))"#;
333 let mut parser = Parser::new(source.to_string());
334 let surface_forms = parser.parse().unwrap();
335
336 let mut expander = Expander::new();
337 let core_forms = expander.expand(surface_forms).unwrap();
338 let source_map = expander.source_map().clone();
339
340 let mut lowerer = Lowerer::new(source_map);
341 let result = lowerer.lower(core_forms);
342 assert!(result.is_ok(), "Lowering failed: {:?}", result.err());
343
344 let (_file, source_map) = result.unwrap();
345 assert!(source_map.is_frozen(), "SourceMap should be frozen after lowering");
346 }
347}