Skip to content

Commit b95c126

Browse files
committed
add pretty printing
1 parent d7e4f43 commit b95c126

File tree

1 file changed

+310
-0
lines changed

1 file changed

+310
-0
lines changed

src/decl.rs

Lines changed: 310 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,314 @@ impl Decl {
115115
Decl::Interface(Interface { name, .. }) => *name,
116116
}
117117
}
118+
119+
/// Pretty-print a declaration in lyte syntax.
120+
///
121+
/// This method formats a declaration as it would appear in lyte source code,
122+
/// suitable for displaying to users or generating code.
123+
///
124+
/// # Examples
125+
///
126+
/// ```ignore
127+
/// // Given a function declaration:
128+
/// let decl = Decl::Func(func);
129+
/// let output = decl.pretty_print(&decls);
130+
/// // Output: "add(a: i32, b: i32) → i32"
131+
/// ```
132+
pub fn pretty_print(&self, decls: &DeclTable) -> String {
133+
match self {
134+
Decl::Func(func) => format_func_decl(func, decls, false),
135+
Decl::Macro(func) => format_func_decl(func, decls, true),
136+
Decl::Struct(st) => format_struct_decl(st, decls),
137+
Decl::Enum { name, cases } => format_enum_decl(*name, cases),
138+
Decl::Global { name, ty } => format!("var {}: {}", name, format_type(*ty, decls)),
139+
Decl::Interface(iface) => format_interface(iface, decls),
140+
}
141+
}
142+
}
143+
144+
fn format_typevars(typevars: &[Name]) -> String {
145+
if typevars.is_empty() {
146+
String::new()
147+
} else {
148+
format!("<{}>", typevars.iter().map(|tv| tv.to_string()).collect::<Vec<_>>().join(", "))
149+
}
150+
}
151+
152+
fn format_params(params: &[Param], decls: &DeclTable) -> String {
153+
params
154+
.iter()
155+
.map(|p| {
156+
if let Some(ty) = p.ty {
157+
format!("{}: {}", p.name, format_type(ty, decls))
158+
} else {
159+
p.name.to_string()
160+
}
161+
})
162+
.collect::<Vec<_>>()
163+
.join(", ")
164+
}
165+
166+
fn format_type(ty: TypeID, decls: &DeclTable) -> String {
167+
match &*ty {
168+
Type::Void => "void".to_string(),
169+
Type::Bool => "bool".to_string(),
170+
Type::Int8 => "i8".to_string(),
171+
Type::UInt8 => "u8".to_string(),
172+
Type::Int32 => "i32".to_string(),
173+
Type::UInt32 => "u32".to_string(),
174+
Type::Float32 => "f32".to_string(),
175+
Type::Float64 => "f64".to_string(),
176+
Type::Tuple(types) => {
177+
format!("({})", types.iter().map(|t| format_type(*t, decls)).collect::<Vec<_>>().join(", "))
178+
}
179+
Type::Var(name) => name.to_string(),
180+
Type::Anon(id) => format!("?{}", id),
181+
Type::Func(dom, rng) => format!("{} → {}", format_type(*dom, decls), format_type(*rng, decls)),
182+
Type::Array(elem, size) => format!("[{}; {}]", format_type(*elem, decls), size),
183+
Type::Name(name, params) => {
184+
if params.is_empty() {
185+
name.to_string()
186+
} else {
187+
format!("{}<{}>", name, params.iter().map(|t| format_type(*t, decls)).collect::<Vec<_>>().join(", "))
188+
}
189+
}
190+
}
191+
}
192+
193+
fn format_constraints(constraints: &[InterfaceConstraint]) -> String {
194+
if constraints.is_empty() {
195+
String::new()
196+
} else {
197+
let formatted = constraints
198+
.iter()
199+
.map(|c| {
200+
if c.typevars.is_empty() {
201+
c.interface_name.to_string()
202+
} else {
203+
format!("{}<{}>", c.interface_name, c.typevars.iter().map(|tv| tv.to_string()).collect::<Vec<_>>().join(", "))
204+
}
205+
})
206+
.collect::<Vec<_>>()
207+
.join(", ");
208+
format!(" where {}", formatted)
209+
}
210+
}
211+
212+
fn format_func_decl(func: &FuncDecl, decls: &DeclTable, is_macro: bool) -> String {
213+
let keyword = if is_macro { "macro" } else { "" };
214+
let typevars = format_typevars(&func.typevars);
215+
let params = format_params(&func.params, decls);
216+
let ret_type = if matches!(&*func.ret, Type::Void) {
217+
String::new()
218+
} else {
219+
format!(" → {}", format_type(func.ret, decls))
220+
};
221+
let constraints = format_constraints(&func.constraints);
222+
223+
let signature = if is_macro {
224+
format!("macro {}{}", func.name, typevars)
225+
} else if keyword.is_empty() {
226+
format!("{}{}", func.name, typevars)
227+
} else {
228+
format!("{} {}{}", keyword, func.name, typevars)
229+
};
230+
231+
if func.body.is_some() {
232+
format!("{}({}){}{} {{ ... }}", signature, params, ret_type, constraints)
233+
} else {
234+
format!("{}({}){}{}", signature, params, ret_type, constraints)
235+
}
236+
}
237+
238+
fn format_struct_decl(st: &StructDecl, decls: &DeclTable) -> String {
239+
let typevars = format_typevars(&st.typevars);
240+
let fields = st
241+
.fields
242+
.iter()
243+
.map(|f| format!(" {}: {}", f.name, format_type(f.ty, decls)))
244+
.collect::<Vec<_>>()
245+
.join("\n");
246+
247+
if fields.is_empty() {
248+
format!("struct {}{} {{}}", st.name, typevars)
249+
} else {
250+
format!("struct {}{} {{\n{}\n}}", st.name, typevars, fields)
251+
}
252+
}
253+
254+
fn format_enum_decl(name: Name, cases: &[Name]) -> String {
255+
let cases_str = cases
256+
.iter()
257+
.map(|c| format!(" {}", c))
258+
.collect::<Vec<_>>()
259+
.join("\n");
260+
261+
if cases.is_empty() {
262+
format!("enum {} {{}}", name)
263+
} else {
264+
format!("enum {} {{\n{}\n}}", name, cases_str)
265+
}
266+
}
267+
268+
fn format_interface(iface: &Interface, decls: &DeclTable) -> String {
269+
let typevars = format_typevars(&iface.typevars);
270+
let funcs = iface
271+
.funcs
272+
.iter()
273+
.map(|f| format!(" {}", format_func_decl(f, decls, false)))
274+
.collect::<Vec<_>>()
275+
.join("\n");
276+
277+
if funcs.is_empty() {
278+
format!("interface {}{} {{}}", iface.name, typevars)
279+
} else {
280+
format!("interface {}{} {{\n{}\n}}", iface.name, typevars, funcs)
281+
}
282+
}
283+
284+
#[cfg(test)]
285+
mod tests {
286+
use super::*;
287+
288+
#[test]
289+
fn test_pretty_print_func() {
290+
let decls = DeclTable::new(vec![]);
291+
292+
let func = FuncDecl {
293+
name: Name::str("add"),
294+
typevars: vec![],
295+
params: vec![
296+
Param {
297+
name: Name::str("a"),
298+
ty: Some(mk_type(Type::Int32)),
299+
},
300+
Param {
301+
name: Name::str("b"),
302+
ty: Some(mk_type(Type::Int32)),
303+
},
304+
],
305+
body: None,
306+
ret: mk_type(Type::Int32),
307+
constraints: vec![],
308+
loc: test_loc(),
309+
arena: ExprArena::new(),
310+
types: vec![],
311+
};
312+
313+
let decl = Decl::Func(func);
314+
let output = decl.pretty_print(&decls);
315+
assert_eq!(output, "add(a: i32, b: i32) → i32");
316+
}
317+
318+
#[test]
319+
fn test_pretty_print_generic_func() {
320+
let decls = DeclTable::new(vec![]);
321+
322+
let func = FuncDecl {
323+
name: Name::str("id"),
324+
typevars: vec![Name::str("T")],
325+
params: vec![Param {
326+
name: Name::str("x"),
327+
ty: Some(mk_type(Type::Var(Name::str("T")))),
328+
}],
329+
body: Some(0),
330+
ret: mk_type(Type::Var(Name::str("T"))),
331+
constraints: vec![],
332+
loc: test_loc(),
333+
arena: ExprArena::new(),
334+
types: vec![],
335+
};
336+
337+
let decl = Decl::Func(func);
338+
let output = decl.pretty_print(&decls);
339+
assert_eq!(output, "id<T>(x: T) → T { ... }");
340+
}
341+
342+
#[test]
343+
fn test_pretty_print_struct() {
344+
let decls = DeclTable::new(vec![]);
345+
346+
let st = StructDecl {
347+
name: Name::str("Point"),
348+
typevars: vec![],
349+
fields: vec![
350+
Field {
351+
name: Name::str("x"),
352+
ty: mk_type(Type::Float32),
353+
loc: test_loc(),
354+
},
355+
Field {
356+
name: Name::str("y"),
357+
ty: mk_type(Type::Float32),
358+
loc: test_loc(),
359+
},
360+
],
361+
};
362+
363+
let decl = Decl::Struct(st);
364+
let output = decl.pretty_print(&decls);
365+
assert_eq!(output, "struct Point {\n x: f32\n y: f32\n}");
366+
}
367+
368+
#[test]
369+
fn test_pretty_print_interface() {
370+
let decls = DeclTable::new(vec![]);
371+
372+
let iface = Interface {
373+
name: Name::str("Compare"),
374+
typevars: vec![Name::str("A")],
375+
funcs: vec![FuncDecl {
376+
name: Name::str("cmp"),
377+
typevars: vec![],
378+
params: vec![
379+
Param {
380+
name: Name::str("lhs"),
381+
ty: Some(mk_type(Type::Var(Name::str("A")))),
382+
},
383+
Param {
384+
name: Name::str("rhs"),
385+
ty: Some(mk_type(Type::Var(Name::str("A")))),
386+
},
387+
],
388+
body: None,
389+
ret: mk_type(Type::Int32),
390+
constraints: vec![],
391+
loc: test_loc(),
392+
arena: ExprArena::new(),
393+
types: vec![],
394+
}],
395+
loc: test_loc(),
396+
};
397+
398+
let decl = Decl::Interface(iface);
399+
let output = decl.pretty_print(&decls);
400+
assert_eq!(output, "interface Compare<A> {\n cmp(lhs: A, rhs: A) → i32\n}");
401+
}
402+
403+
#[test]
404+
fn test_pretty_print_global() {
405+
let decls = DeclTable::new(vec![]);
406+
407+
let decl = Decl::Global {
408+
name: Name::str("counter"),
409+
ty: mk_type(Type::Int32),
410+
};
411+
412+
let output = decl.pretty_print(&decls);
413+
assert_eq!(output, "var counter: i32");
414+
}
415+
416+
#[test]
417+
fn test_pretty_print_enum() {
418+
let decls = DeclTable::new(vec![]);
419+
420+
let decl = Decl::Enum {
421+
name: Name::str("Status"),
422+
cases: vec![Name::str("Active"), Name::str("Inactive")],
423+
};
424+
425+
let output = decl.pretty_print(&decls);
426+
assert_eq!(output, "enum Status {\n Active\n Inactive\n}");
427+
}
118428
}

0 commit comments

Comments
 (0)